I have a simple client-server example using TCP socket on Linux. The server listens on the loopback address. The client connects to server and sends some integer plus an "END" string to mark the end of data. Server reads the numbers, add them all and returns the total sum. However, my server sometime blocks on read()
even though the client has sent all the data successfully.
Here's the code:
server.c:
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define BACKLOG 5
int main(int argc, char *argv[]) {
struct sockaddr_in addr;
int down_flag = 0;
int result = 0;
int ret = 0;
int sfd = socket(AF_INET, SOCK_STREAM, 0);
if (sfd < 0) {
perror("Create server socket error: %s\n");
return 0;
}
/* Bind socket to loopback address */
memset((void *) &addr, 0, sizeof(struct sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_port = htons(8888);
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
if (bind(sfd, (struct sockaddr *) &addr, sizeof(struct sockaddr_in)) == -1) {
perror("Bind server socket failed");
goto _exit;
}
if (listen(sfd, BACKLOG) == -1) {
perror("Listen failed");
goto _exit;
}
ssize_t num_rd = 0;
char buf[100] = {0};
for (;;)
{
printf("Waiting to accept a connection...\n");
int cfd = accept(sfd, NULL, NULL);
printf("Accepted socket fd = %d\n", cfd);
result = 0;
while ((num_rd = read(cfd, buf, sizeof(buf))) > 0) {
/* Ensure the buffer is 0-terminated */
buf[sizeof(buf) - 1] = 0;
printf("Read data: %s\n", buf);
/* Handle commands */
if (!strncmp(buf, "DOWN", sizeof(buf))) {
down_flag = 1;
break;
}
if (!strncmp(buf, "END", sizeof(buf))) {
break;
}
/* Add received summand */
result += atoi(buf);
}
if (-1 == num_rd) {
perror("Read error");
}
/* Send result */
sprintf(buf, "%d", result);
ret = write(cfd, buf, sizeof(buf));
if (-1 == ret) {
perror("Write error\n");
goto _exit;
}
close(cfd);
/* Quit on DOWN command */
if (down_flag) {
break;
}
}
_exit:
close(sfd);
return 0;
}
client.c:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(int argc, char *argv[]) {
struct sockaddr_in addr;
int ret;
int data_socket;
char buf[100] = {0};
int i = 0;
data_socket = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == data_socket) {
perror("Create client socket error");
exit(EXIT_FAILURE);
}
/* Connect to server socket */
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(8888);
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
ret = connect(data_socket, (const struct sockaddr *) &addr, sizeof(addr));
if (-1 == ret) {
perror("Connect error");
exit(EXIT_FAILURE);
}
/* Send arguments */
for (i = 1; i < argc; i++) {
ret = write(data_socket, argv[i], strlen(argv[i]) + 1);
if (-1 == ret) {
perror("Write error");
break;
}
}
strcpy(buf, "END");
ret = write(data_socket, buf, strlen(buf) + 1);
printf("write %s to socket, ret = %d\n", buf, ret);
if (-1 == ret) {
perror("Write to socket error");
exit(EXIT_FAILURE);
}
/* Read the result */
memset(buf, 0, sizeof(buf));
ret = read(data_socket, buf, sizeof(buf));
if (-1 == ret) {
perror("Read from client socket error");
exit(EXIT_FAILURE);
}
buf[sizeof(buf) - 1] = 0;
printf("Result = %s\n", buf);
close(data_socket);
exit(EXIT_SUCCESS);
}
Run the client a few times and the server will be blocking on the read()
call at some points:
$ for i in {1..100}; do ./client 3 4 5 6; done
write END to socket, ret = 4
Result = 18
write END to socket, ret = 4
Server output:
$ ./server
Waiting to accept a connection...
Accepted socket fd = 4
Read data: 3
Read data: 4
Read data: 5
Read data: 6
Read data: END
Waiting to accept a connection...
Accepted socket fd = 4
Read data: 3
The server is blocking on while ((num_rd = read(cfd, buf, sizeof(buf))) > 0)
line.
Edit: My question is why the read()
blocks. AFAIK, read()
will block until there's at least 1 byte of data to be read from the socket. In this case, the client has sent more data than the server has read, so I think there's available data to be read from the socket. So why does read()
still block?