问题
I have created two programs, a client and a server. They communicate via sockets sending char
arrays of a fixed size of 115 bytes.
The data I want to transfer is stored in the following struct
:
typedef struct {
char origin[14];
char type;
char data[100];
} socket_data;
But in order to send the data serialized, I want to send that information in a single string concatenating al the fields in the struct
so I send a 115 bytes string. If any of those fields does not reach it's max size, I will manually fill the extra array positions with \0
.
I have created two functions implemented in both client and server that send data through the socket or receive data from the socket.
The two functions are the following:
void socket_send(int socket, char *origin, char type, char *data) {
char info[115]; //data to be sent
socket_data aux;
strcpy(aux.origin, origin);
aux.type = type;
strcpy(aux.data, data);
//Filling up the remaining positions of origin and data variables
for (int i = (int) strlen(aux.origin); i<14; i++) aux.origin[i] = '\0';
for (int i = (int) strlen(aux.data); i<100; i++) aux.data[i] = '\0';
//Building up the 115 byte string I want to send via socket
for (int i=0; i<14; i++) info[i] = aux.origin[i];
info[14] = type;
for (int i=0; i<100; i++) info[i+15] = aux.data[i];
ssize_t total_bytes = 115;
ssize_t bytes_written = 0;
//Here I send all the bytes through the socket
do {
bytes_written = write(socket, info + (115 - total_bytes), total_bytes);
total_bytes -= bytes_written;
} while (total_bytes > 0);
}
socket_data socket_rcv(int socket) {
socket_data info;
char sequence[115];
ssize_t total_bytes = 115;
ssize_t bytes_read = 0;
//Here I receive all the bytes from the socket (till I fill up the 115 byte string called sequence)
do {
bytes_read = read(socket, sequence + (115 - total_bytes), total_bytes);
total_bytes -= bytes_read;
} while (total_bytes > 0);
//Then I return a stuct
for (int i=0; i<14; i++) info.origin[i] = sequence[i];
info.type = sequence[14];
for (int i=0; i<100; i++) info.data[i] = sequence[i+15];
return info;
}
As you can see, I loop both read()
and write()
to make sure all bytes are sent as I'm aware sometimes those functions read or write less bytes than demanded.
The issue is that, testing the functionality of the program, I have seen that in the case that less bytes are read (it loops), the program blocks (maybe waiting for another write()
from the server side) instead of reading the remaining bytes in the socket buffer (because all 115 bytes where sent and only 111 received, so there should be still 4 bytes in the socket buffer). Sometimes also, instead of blocking waiting for a possible write()
, the program terminates when it shouldn't...
I can't find the issue here and I'd appreciate some help
EDIT
I created this functions to set up the sockets...
Server:
int socketConfig (connection_info cinfo) {
int socketfd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (socketfd < 0) {
write(1, "Socket error\n", strlen("Socket error\n"));
return -1;
}
struct sockaddr_in s_addr;
memset (&s_addr, 0, sizeof (s_addr));
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(cinfo.port);
s_addr.sin_addr.s_addr = INADDR_ANY;
if (bind (socketfd, (void *) &s_addr, sizeof (s_addr)) < 0) {
write(1, "Bind error\n", strlen("Bind error\n"));
return -1;
}
listen(socketfd, 3);
return socketfd;
}
int receiveClient(int serverfd) {
struct sockaddr_in client;
socklen_t len = sizeof(client);
return accept(serverfd, (void *) &client, &len);
}
Client:
int connect_to_server(Config config) {
struct sockaddr_in client;
int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
write(1, "Connecting Jack...\n", strlen("Connecting Jack...\n"));
if (sockfd < 0) {
write(1, "Error creating the socket\n", strlen("Error creating the socket\n"));
return -1;
}
memset(&client, 0, sizeof(client));
client.sin_family = AF_INET;
client.sin_port = htons(config.port_jack);
if (inet_aton(config.ip_jack, &client.sin_addr) == 0) {
write(1, "Invalid IP address\n", strlen("Invalid IP address\n"));
return -1;
}
if (connect(sockfd, (void *) &client, sizeof(client)) < 0) {
write(1, "Error connecting to Jack\n", strlen("Error connecting to Jack\n"));
return -1;
}
return sockfd;
}
I can guarantee the connection works
回答1:
You are not checking the return values of write()
and read()
for failures.
Try something more like this:
int socket_send_all(int socket, const void *data, size_t size) {
const char *pdata = (const char*) data;
ssize_t bytes_written;
while (size > 0) {
bytes_written = write(socket, pdata, size);
if (bytes_written < 0) return bytes_written;
pdata += bytes_written;
size -= bytes_written;
}
return 0;
}
int socket_rcv_all(int socket, void *data, size_t size) {
char *pdata = (char*) data;
ssize_t bytes_read;
while (size > 0) {
bytes_read = read(socket, pdata, size);
if (bytes_read <= 0) return bytes_read;
pdata += bytes_read;
size -= bytes_read;
}
return 1;
}
int socket_send2(int socket, const socket_data *sd) {
char bytes[115];
memcpy(bytes, sd->origin, 14);
bytes[14] = sd->type;
memcpy(bytes+15, sd->data, 100);
return socket_send_all(socket, bytes, 115);
/* alternatively:
int ret = socket_send_all(socket, sd->origin, 14);
if (ret == 0) ret = socket_send_all(socket, &(sd->type), 1);
if (ret == 0) ret = socket_send_all(socket, sd->data, 100);
return ret;
*/
}
int socket_send(int socket, char *origin, char type, char *data) {
socket_data aux;
strncpy(aux.origin, origin, 14);
aux.type = type;
strncpy(aux.data, data, 100);
return socket_send2(socket, &aux);
}
int socket_rcv2(int socket, socket_data *sd) {
char bytes[115];
int ret = socket_rcv_all(socket, bytes, 115);
if (ret > 0) {
memcpy(sd->origin, bytes, 14);
sd->type = bytes[14];
memcpy(sd->data, bytes+15, 100);
}
return ret;
/* alternatively:
int ret = socket_rcv_all(socket, sd->origin, 14);
if (ret > 0) ret = socket_rcv_all(socket, &(sd->type), 1);
if (ret > 0) ret = socket_rcv_all(socket, sd->data, 100);
return ret;
*/
}
socket_data socket_rcv(int socket) {
socket_data aux;
int ret = socket_rcv2(socket, &aux);
if (ret <= 0) {
// error handling ...
}
return aux;
}
回答2:
read()
returns as many bytes as it wants. Perchance the sent output went out in two packets. Perhaps something else (memory alignment comes to mind). Always handle short reads by trying to read more or have a headache. In addition, write()
only writes as many byte as it wants. A short write is usually a full buffer or split by a signal, but stranger things have been observed.
You need to check for errors every time around the loop. Your program as written will trash memory otherwise.
来源:https://stackoverflow.com/questions/65600536/read-not-reading-the-remaining-bytes-on-a-socket-buffer