I wrote simple application in C which forks one child to act as a network server, and many children to act as a network clients. Clients connect to the server, and ask for data. Here is the code:
#include <stdio.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/queue.h>
#include <strings.h>
#include <sys/wait.h>
#define LISTENSOCKET 1519
#define MAXCLIENT 200
#define MAXLINE 80
void client_do_something(int c)
{
printf("Process %d, server gave %d\n", getpid(), c);
}
void client_body()
{
struct sockaddr_in clientaddr;
struct sockaddr_in localaddr;
int sockfd;
int nread;
int s;
char serveraddr[20] = "127.0.0.1";
char laddr[20];
char command[4] = "GET";
int c;
command[3] = '\0';
socklen_t len;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
clientaddr.sin_family = AF_INET;
clientaddr.sin_port = htons(LISTENSOCKET);
inet_pton(AF_INET, serveraddr, &clientaddr.sin_addr);
printf("Client %d started\n", getpid());
if ((s=connect(sockfd, (struct sockaddr *) &clientaddr, sizeof(clientaddr))) != 0) {
perror("conn err:");
printf("Connect error: pid %d %d\n", getpid(), errno);
close(sockfd);
sleep(1);
exit(1);
}
getsockname(sockfd, (struct sockaddr *) &localaddr, &len);
inet_ntop(AF_INET, &localaddr.sin_addr, laddr, sizeof(laddr));
printf("Client %d passed connect (%d), %s:%d \n", getpid(), s, laddr, ntohs(localaddr.sin_port));
while(1) {
send(sockfd, command, 4, 0);
if ( (nread = recv(sockfd, &c, 4, 0)) < 0 ) {
if (errno == ENOTCONN) {
sleep(1);
continue;
}
perror("client recv err:");
printf("Client %d received error %d ", getpid(), errno);
exit(1);
} else if (nread == 0) {
printf("Pid %d received FIN\n", getpid());
close(sockfd);
exit(0);
}
client_do_something(c);
}
}
int start_server() {
int i, nread, maxi, val, listenfd, connfd, sockfd, maxfd, nready, client[MAXCLIENT];
socklen_t len;
char *c;
struct sockaddr_in servaddr, clientaddr, localaddr;
fd_set rset, allset;
char addr[MAXLINE];
char laddr[INET_ADDRSTRLEN];
struct timeval timeout;
printf("Started\n");
printf("Server PID=%d", getpid());
val = 0;
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(LISTENSOCKET);
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
perror("socket failed\n");
if(bind(listenfd, (struct sockaddr *) &servaddr, (socklen_t) sizeof(servaddr)) == -1)
perror("listen failed\n");
getsockname(listenfd, (struct sockaddr *) &localaddr, &len);
inet_ntop(AF_INET, &localaddr.sin_addr, laddr, INET_ADDRSTRLEN);
printf("Server %d passed connect , %s:%d \n", getpid(), laddr, ntohs(localaddr.sin_port));
if(listen(listenfd, 10) == -1)
perror("listen failed\n");
maxi = -1;
for(i=0; i < MAXCLIENT; i++)
client[i] = -1;
maxfd = listenfd;
FD_ZERO(&allset);
FD_SET(listenfd, &allset);
bzero(&timeout, sizeof(struct timeval));
timeout.tv_sec = 15;
printf("Server process, after listen, sleep 5s before accept\n");
/* Here I purpousely sleep because I want client to initiate connection before accept */
sleep(5);
printf("Server porcess, slept for 5s\n");
while(1) {
rset = allset;
nready = select(maxfd + 1, &rset, NULL, NULL, NULL);
if (FD_ISSET(listenfd, &rset)) {
len = sizeof(clientaddr);
bzero(&clientaddr, sizeof(clientaddr));
if((connfd = accept(listenfd, (struct sockaddr *) &clientaddr, &len)) < 0) {
perror("accept failed");
exit(-1);
}
for(i=0; i < MAXCLIENT; i++) {
if(client[i] < 0) {
client[i] = connfd;
break;
}
}
FD_SET(connfd, &allset);
if(connfd >= maxfd)
maxfd = connfd;
if(i > maxi)
maxi = i;
if(--nready <= 0)
continue;
}
for(i=0; i <= maxi; i++) {
if ( (sockfd = client[i]) < 0)
continue;
if(FD_ISSET(sockfd, &rset)) {
if( (nread = read(sockfd, addr, MAXLINE)) < 0) {
if(errno == EINTR)
nread = 0;
else {
printf("Izlazim");
return -1;
}
} else if (nread == 0) {
close(sockfd);
FD_CLR(sockfd, &allset);
client[i] = -1;
continue;
}
if (strncmp(addr, "GET", 3) == 0) {
if(val < 100) {
i = send(sockfd, (int *) &val, (size_t) sizeof(val), 0);
val++;
if (i == -1)
printf("nread=%d, errno=%d\n",nread, errno);
} else {
FD_CLR(sockfd, &allset);
client[i] = -1;
close(sockfd);
}
} else {
c = addr;
printf("Poslao je addr=%s\n", addr);
}
if(--nready <= 0)
break;
}
}
}
}
int main(int argc, char *argv[]) {
printf("Pid=%d",getpid());
int i, child_num, status;
pid_t p, pp;
child_num = 100;
printf("%d childs, pid=%d \n", child_num, getpid());
p = fork();
if(p == 0) {
start_server();
exit(0);
} else if (p < 0) {
exit(1);
}
pp = p;
for(i = 0; i < child_num; i++) {
p = fork();
if (p < 0) {
exit(1);
} else if (p == 0){
client_body();
exit(0);
}
}
for(i = 0; i < child_num; i++) {
wait(&status);
}
kill(pp, SIGTERM);
return 0;
}
The problem is in connect() system call (in client_body function); according to the man pages, connect should return 0 if connection succeeds, and -1 on error. I have noticed that, in my program, connect() returned 0 although it didn't establish connection with the server (received SYN ACK from the server). Later, in the program, the same child process issued recv system call, which produced error with errno value 104 (ECONNRESET). All the time I watched the wireshark capture, and I didn't notice RESET flag in any TCP packet sent from the server.
Anybody have any idea what's wrong? I have tested this code on linux, 2.6.38-8-generic kernel.