Lecture et écriture ligne par ligne à partir du tampon avec communication TCP entre C et Ruby

Notez que j'ai eu du mal à communiquer avec le serveur Ruby TCP et le client C.

En conséquence, le problème était que les unités de la quantité de données apparentes que Ruby et C envoyaient et recevaient dans le flux étaient différentes. Par conséquent, nous avons implémenté la fonction sputs et la fonction sgets qui encapsulent l'écriture et la lecture en C afin qu'ils puissent communiquer dans la même unité que Ruby.

L'environnement est

est.

Serveur Ruby

C'est un serveur qui démarre la communication et renvoie la chaîne de caractères reçue du client presque telle quelle. Répétez ceci 5 fois pour terminer.

server.rb


require 'socket'

#Démarrez le serveur sur le port 20000
server = TCPServer.open(20000)
#Accepter la communication
sock = server.accept

5.times do
  #Recevez une ligne du tampon.(Recevez jusqu'au saut de ligne)
  line = sock.gets.chomp
  #Affiché sur la console. S'il est p, il est également nul\Il est affiché sous la forme x00.
  p line
  #Revenir. À la fin\n est ajouté.
  sock.puts("you sent <<<#{line}>>>.")
end

#Déconnecter la communication
sock.close

Client C

Implémentez les fonctions sputs et sgets pour les get et les put de ruby.

client.c


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#define RECV_SIZE (10000)
#define SEND_SIZE (10000)

/*
 *Récupère une ligne dans le tampon.(get string from stream)
 *S'il n'y a pas de données pour une ligne dans le tampon, attendez.
 */
char* sgets(int sd, char* line) {
	//La partie retirée avant le dernier saut de ligne
	static char* read_buf = NULL;
	if (read_buf == NULL) {
		read_buf = malloc(sizeof(char) * RECV_SIZE);
		read_buf[0] = '\0';
	}

	while (1) {
		int e;
		if (strlen(read_buf) != (e = strcspn(read_buf, "\n"))) {
			//Initialiser la ligne
			memset(line, '\0', sizeof(char) * strlen(line));
			//Copier une ligne en ligne
			strncpy(line, read_buf, (e + 1) - 0);
			//Lisez la ligne suivante_Copier depuis le début de buf
			strcpy(read_buf, strchr(read_buf, '\n') + 1);

			break;
		}
		//Initialiser et préparer une baie pour la réception
		char r[RECV_SIZE] = { 0 };

		//Reçoit la quantité de char qui vient d'être envoyée du tampon.(Aucun NULL de fin)
		if (read(sd, r, sizeof(r) * RECV_SIZE) < 0) {
			perror("recv");
			fflush(0);
			return NULL;
		}
		//Accumulez les données lues.
		strcat(read_buf, r);
	}

	return line;
}

/*
 *Envoyez une ligne.(put string to stream)
 *À la fin"\n"Est ajouté.
 */
void sputs(int sd, char* str) {
	char* send_str = malloc(sizeof(char) *(strlen(str) + 2));
	memset(send_str, '\0', sizeof(char) *(strlen(str) + 2));
	strcat(send_str, str);
	send_str[strlen(str)] = '\n';
	if (write(sd, send_str, sizeof(char) * strlen(send_str)) < 0) {
		perror("send");
		return;
	}
	free(send_str);
}

int main(int argc, char *argv[]) {
	int sd;  //Variables de création de sockets
	struct sockaddr_in addr;  //Variables pour la connexion au serveur
	char *recv[sizeof(char) * RECV_SIZE] = {0};

	//Créer un socket pour IPv4 TCP
	if ((sd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		perror("socket");
		return -1;
	}

	//Définissez l'adresse de destination et le numéro de port
	addr.sin_family = AF_INET;
	addr.sin_port = htons(20000);
	addr.sin_addr.s_addr = inet_addr("127.0.0.1");

	//Connexion serveur (pour TCP, vous devez établir une connexion)
	connect(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_in));

	char* strs[5] = {"abcde", "fg", "hijklmn", "opqrs", "tuvwxyz"};

	for(int i = 0; i < 5; i++) {
		//Envoyez une ligne. À la fin\n est ajouté.
		sputs(sd, strs[i]);
		//Obtenez une ligne.
		sgets(sd, recv);
		//afficher
		printf("I recived <<<%s>>>\n", recv);
	}

	//Fermer la prise
	close(sd);

	return 0;
}

Soyez prudent avec la fonction de lecture utilisée dans sgets. Le contenu du tampon est écrit dans r avec read (sd, r, sizeof (r) * RECV_SIZE), mais la fonction read ne passe pas bien la fin de la chaîne de caractères, mais 1 octet. Écrivez le point que vous avez reçu à chaque fois. De plus, null n'est pas ajouté à la fin de l'écriture. Par conséquent, il est recommandé de remplir r avec null à chaque fois.

Notez également la fonction d'écriture utilisée dans les sputs. Si vous utilisez write (sd, send_str, sizeof (char) * strlen (send_str) avec un grand nombre de 3ème arguments et le définissez sur SEND_SIZE, SEND_SIZE octets sera envoyé quelle que soit la longueur de la chaîne dans le 2ème argument. Il semble que la partie manquante soit remplie avec NULL.

À propos, l'implémentation qui ne fonctionne pas bien est la suivante.

client.c(Mauvais exemple)


int main(int argc, char *argv[]) {
	int sd;  //Variables de création de sockets
	struct sockaddr_in addr;  //Variables pour la connexion au serveur
	char *recv[sizeof(char) * RECV_SIZE] = {0};

	//Créer un socket pour IPv4 TCP
	if ((sd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		perror("socket");
		return -1;
	}

	//Définissez l'adresse de destination et le numéro de port
	addr.sin_family = AF_INET;
	addr.sin_port = htons(20000);
	addr.sin_addr.s_addr = inet_addr("127.0.0.1");

	//Connexion serveur (pour TCP, vous devez établir une connexion)
	connect(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_in));

	char* strs[5] = {"abcde", "fg", "hijklmn", "opqrs", "tuvwxyz"};

	for(int i = 0; i < 5; i++) {
		//Envoyez une ligne.
		write(sd, strs[i], SEND_SIZE);
		//Obtenez une ligne.
		read(sd, recv, RECV_SIZE);
		//afficher
		printf("I recived <<<%s>>>\n", recv);
	}

	//Fermer la prise
	close(sd);

	return 0;
}

Ecrire en lecture et en écriture avec la même sensation get et met que Ruby échouera.

Exécuter

$ ruby server.rb
$ gcc -O2 -o client client.c
$ ./clinet

Du côté serveur


"abcde\n"
"fg\n"
"hijklmn\n"
"opqrs\n"
"tuvwxyz\n"

Côté client


I recived <<<you sent <<<abcde>>>.
>>>
I recived <<<you sent <<<fg>>>.
>>>
I recived <<<you sent <<<hijklmn>>>.
>>>
I recived <<<you sent <<<opqrs>>>.
>>>
I recived <<<you sent <<<tuvwxyz>>>.
>>>

En C, il ne se convertit pas en une chaîne de caractères comme Ruby, j'ai donc dû faire attention au traitement octet par octet. Probablement, si la communication TCP est effectuée entre d'autres langues, divers problèmes sont susceptibles de se produire en raison de la différence dans la gestion de la communication.

Recommended Posts

Lecture et écriture ligne par ligne à partir du tampon avec communication TCP entre C et Ruby
J'ai essayé d'implémenter Ruby avec Ruby (et C) (j'ai joué avec intégré)
Comment écrire un exemple d'implémentation F04 ruby et C99 en temps réel hors ligne
Résolution avec Ruby, Perl et Java AtCoder ABC 128 C
En fait, Ruby fait la distinction entre les sauts de ligne et les espaces
Une application qui acquiert la valeur du capteur d'accélération par communication UDP entre C # et Android
La différence entre la programmation qui utilise des classes Ruby et la programmation qui n'utilise pas
Résolution avec Ruby, Perl et Java AtCoder ABC 129 C (Partie 1)