Note that I had a hard time communicating with the Ruby TCP server and C client.
As a result, the problem was that Ruby and C had different units of apparent data to send and receive to stream. Therefore, we implemented the sputs function and sgets function that wrap C write and read so that they can communicate in the same unit as Ruby.
The environment is
is.
It is a server that starts communication and returns the character string received from the client almost as it is. Repeat this 5 times to finish.
server.rb
require 'socket'
#Start the server on port 20000
server = TCPServer.open(20000)
#Accept communication
sock = server.accept
5.times do
#Receives one line from the buffer.(Receive up to line breaks)
line = sock.gets.chomp
#Displayed on the console. If it is p, null is also\It is displayed as x00.
p line
#Return. At the end\n is added.
sock.puts("you sent <<<#{line}>>>.")
end
#Disconnect communication
sock.close
Implement the puts and sgets functions corresponding to ruby's gets and 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)
/*
*Fetch a line from the buffer.(get string from stream)
*If there is no data for one line in the buffer, it waits.
*/
char* sgets(int sd, char* line) {
//The part taken out before the last line break
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"))) {
//Initialize line
memset(line, '\0', sizeof(char) * strlen(line));
//Copy one line to line
strncpy(line, read_buf, (e + 1) - 0);
//Read the next line_Copy from the beginning of buf
strcpy(read_buf, strchr(read_buf, '\n') + 1);
break;
}
//Initialize and prepare an array for receiving
char r[RECV_SIZE] = { 0 };
//Receives the amount of char just sent from the buffer.(Null not added at the end)
if (read(sd, r, sizeof(r) * RECV_SIZE) < 0) {
perror("recv");
fflush(0);
return NULL;
}
//Accumulate the read data.
strcat(read_buf, r);
}
return line;
}
/*
*Send one line.(put string to stream)
*At the end"\n"Is added.
*/
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 for creating sockets
struct sockaddr_in addr; //Variables for server connection
char *recv[sizeof(char) * RECV_SIZE] = {0};
//Create an IPv4 TCP socket
if ((sd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket");
return -1;
}
//Set the destination address and port number
addr.sin_family = AF_INET;
addr.sin_port = htons(20000);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
//Server connection (for TCP, you need to establish a connection)
connect(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_in));
char* strs[5] = {"abcde", "fg", "hijklmn", "opqrs", "tuvwxyz"};
for(int i = 0; i < 5; i++) {
//Send one line. At the end\n is added.
sputs(sd, strs[i]);
//Get one line.
sgets(sd, recv);
//display
printf("I recived <<<%s>>>\n", recv);
}
//Close socket
close(sd);
return 0;
}
Be careful with the read function used in sgets. The contents of the buffer are written to r
byread (sd, r, sizeof (r) * RECV_SIZE)
, but the read
function does not pass the end of the string well, but 1 byte. Write up to the point you received each time. Also, null is not added at the end of writing. Therefore, it is recommended to fill r with null every time.
Also, be careful about the write function used in sputs. If you use write (sd, send_str, sizeof (char) * strlen (send_str)
with a large number of 3rd arguments and set it to SEND_SIZE
, SEND_SIZE bytes will be sent regardless of the length of the character string of the 2nd argument. It seems that the missing part is filled with NULL.
By the way, the implementation that does not work well is as follows.
client.c(Bad example)
int main(int argc, char *argv[]) {
int sd; //Variables for creating sockets
struct sockaddr_in addr; //Variables for server connection
char *recv[sizeof(char) * RECV_SIZE] = {0};
//Create an IPv4 TCP socket
if ((sd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket");
return -1;
}
//Set the destination address and port number
addr.sin_family = AF_INET;
addr.sin_port = htons(20000);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
//Server connection (for TCP, you need to establish a connection)
connect(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_in));
char* strs[5] = {"abcde", "fg", "hijklmn", "opqrs", "tuvwxyz"};
for(int i = 0; i < 5; i++) {
//Send one line.
write(sd, strs[i], SEND_SIZE);
//Get one line.
read(sd, recv, RECV_SIZE);
//display
printf("I recived <<<%s>>>\n", recv);
}
//Close socket
close(sd);
return 0;
}
Writing read and write with the same feeling of gets and puts as Ruby will fail.
$ ruby server.rb
$ gcc -O2 -o client client.c
$ ./clinet
Server side
"abcde\n"
"fg\n"
"hijklmn\n"
"opqrs\n"
"tuvwxyz\n"
Client side
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, it doesn't convert to a character string like Ruby, so I had to pay attention to byte-by-byte processing. Probably, if TCP communication is performed between other languages, various problems are likely to occur due to the difference in the handling of communication.
Recommended Posts