Lesen und Schreiben Sie zeilenweise aus dem Puffer mit TCP-Kommunikation zwischen C und Ruby

Beachten Sie, dass ich Schwierigkeiten hatte, mit dem Ruby TCP-Server und dem C-Client zu kommunizieren.

Infolgedessen bestand das Problem darin, dass die Einheiten der Menge an scheinbaren Daten, die Ruby und C an den Stream gesendet und empfangen haben, unterschiedlich waren. Aus diesem Grund haben wir die Sputs-Funktion und die Sgets-Funktion implementiert, die C-Schreib- und Lesefunktionen umschließen, damit sie in derselben Einheit wie Ruby kommunizieren können.

Die Umwelt ist

ist.

Ruby Server

Es ist ein Server, der die Kommunikation startet und die vom Client empfangene Zeichenfolge fast so zurückgibt, wie sie ist. Wiederholen Sie dies 5 Mal, um den Vorgang abzuschließen.

server.rb


require 'socket'

#Starten Sie den Server an Port 20000
server = TCPServer.open(20000)
#Kommunikation akzeptieren
sock = server.accept

5.times do
  #Empfangen Sie eine Zeile aus dem Puffer.(Erhalten Sie bis zum Zeilenumbruch)
  line = sock.gets.chomp
  #Wird auf der Konsole angezeigt. Wenn es p ist, ist es auch null\Es wird als x00 angezeigt.
  p line
  #Rückkehr. Am Ende\n wird hinzugefügt.
  sock.puts("you sent <<<#{line}>>>.")
end

#Trennen Sie die Kommunikation
sock.close

C-Client

Implementieren Sie die Sputs- und Sgets-Funktionen für Rubys Gets und Puts.

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)

/*
 *Holen Sie sich eine Zeile aus dem Puffer.(get string from stream)
 *Wenn sich keine Daten für eine Zeile im Puffer befinden, warten Sie.
 */
char* sgets(int sd, char* line) {
	//Der Teil, der vor dem letzten Zeilenumbruch herausgenommen wurde
	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"))) {
			//Zeile initialisieren
			memset(line, '\0', sizeof(char) * strlen(line));
			//Kopieren Sie eine Zeile in Zeile
			strncpy(line, read_buf, (e + 1) - 0);
			//Lesen Sie die nächste Zeile_Kopie vom Anfang von buf
			strcpy(read_buf, strchr(read_buf, '\n') + 1);

			break;
		}
		//Initialisieren und bereiten Sie ein Array für den Empfang vor
		char r[RECV_SIZE] = { 0 };

		//Erhält die gerade gesendete Zeichenmenge aus dem Puffer.(Kein nachfolgendes NULL)
		if (read(sd, r, sizeof(r) * RECV_SIZE) < 0) {
			perror("recv");
			fflush(0);
			return NULL;
		}
		//Sammeln Sie die gelesenen Daten.
		strcat(read_buf, r);
	}

	return line;
}

/*
 *Senden Sie eine Zeile.(put string to stream)
 *Am Ende"\n"Hinzugefügt.
 */
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;  //Variablen zum Erstellen von Sockets
	struct sockaddr_in addr;  //Variablen für die Serververbindung
	char *recv[sizeof(char) * RECV_SIZE] = {0};

	//Erstellen Sie einen Socket für IPv4-TCP
	if ((sd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		perror("socket");
		return -1;
	}

	//Stellen Sie die Zieladresse und die Portnummer ein
	addr.sin_family = AF_INET;
	addr.sin_port = htons(20000);
	addr.sin_addr.s_addr = inet_addr("127.0.0.1");

	//Serververbindung (für TCP müssen Sie eine Verbindung herstellen)
	connect(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_in));

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

	for(int i = 0; i < 5; i++) {
		//Senden Sie eine Zeile. Am Ende\n wird hinzugefügt.
		sputs(sd, strs[i]);
		//Holen Sie sich eine Zeile.
		sgets(sd, recv);
		//Anzeige
		printf("I recived <<<%s>>>\n", recv);
	}

	//Steckdose schließen
	close(sd);

	return 0;
}

Seien Sie vorsichtig mit der in sgets verwendeten Lesefunktion. Der Inhalt des Puffers wird mit read (sd, r, sizeof (r) * RECV_SIZE) in r geschrieben, aber die Funktion read liest das Ende der Zeichenkette nicht gut, sondern 1 Byte. Schreiben Sie jedes Mal bis zu dem Punkt auf, den Sie erhalten haben. Außerdem wird am Ende des Schreibens keine Null hinzugefügt. Daher wird empfohlen, r jedes Mal mit null zu füllen.

Beachten Sie auch die Schreibfunktion, die in Sputs verwendet wird. Wenn Sie "write (sd, send_str, sizeof (char) * strlen (send_str)" mit einer großen Anzahl von dritten Argumenten verwenden und auf "SEND_SIZE" setzen, werden SEND_SIZE-Bytes unabhängig von der Länge der Zeichenfolge des zweiten Arguments gesendet. Es scheint, dass der fehlende Teil mit NULL gefüllt ist.

Übrigens ist die Implementierung, die nicht gut funktioniert, wie folgt.

client.c(Schlechtes Beispiel)


int main(int argc, char *argv[]) {
	int sd;  //Variablen zum Erstellen von Sockets
	struct sockaddr_in addr;  //Variablen für die Serververbindung
	char *recv[sizeof(char) * RECV_SIZE] = {0};

	//Erstellen Sie einen Socket für IPv4-TCP
	if ((sd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		perror("socket");
		return -1;
	}

	//Stellen Sie die Zieladresse und die Portnummer ein
	addr.sin_family = AF_INET;
	addr.sin_port = htons(20000);
	addr.sin_addr.s_addr = inet_addr("127.0.0.1");

	//Serververbindung (für TCP müssen Sie eine Verbindung herstellen)
	connect(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_in));

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

	for(int i = 0; i < 5; i++) {
		//Senden Sie eine Zeile.
		write(sd, strs[i], SEND_SIZE);
		//Holen Sie sich eine Zeile.
		read(sd, recv, RECV_SIZE);
		//Anzeige
		printf("I recived <<<%s>>>\n", recv);
	}

	//Steckdose schließen
	close(sd);

	return 0;
}

Schreiben Lesen und Schreiben mit dem gleichen get und Puts Gefühl, wie Ruby scheitern wird.

Lauf

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

Serverseite


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

Client-Seite


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>>>.
>>>

In C wird es nicht in eine Zeichenkette wie Ruby konvertiert, daher musste ich bei der byteweisen Verarbeitung vorsichtig sein. Wenn eine TCP-Kommunikation zwischen anderen Sprachen durchgeführt wird, treten wahrscheinlich verschiedene Probleme aufgrund der unterschiedlichen Handhabung der Kommunikation auf.

Recommended Posts

Lesen und Schreiben Sie zeilenweise aus dem Puffer mit TCP-Kommunikation zwischen C und Ruby
Ich habe versucht, Ruby mit Ruby (und C) zu implementieren (ich habe mit Builtin gespielt)
Offline-Echtzeit zum Schreiben eines F04-Ruby- und C99-Implementierungsbeispiels
Lösen mit Ruby, Perl und Java AtCoder ABC 128 C.
Tatsächlich unterscheidet Ruby zwischen Zeilenumbrüchen und Leerzeichen
Eine Anwendung, die den Wert des Beschleunigungssensors durch UDP-Kommunikation zwischen C # und Android erfasst
Der Unterschied zwischen der Programmierung mit Ruby-Klassen und der Programmierung ohne Ruby-Klassen
Lösen mit Ruby, Perl und Java AtCoder ABC 129 C (Teil 1)