/************************ * client ************************/ #include <stdio.h> #include <errno.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string.h> #define SERV_ADDR "127.0.0.1" #define SERV_PORT 5358 #define BUF_LEN 1024 void str_cli(FILE *fp, int sockfd) { char sendbuf[BUF_LEN] = {0}; char recvbuf[BUF_LEN] = {0}; while(1) { bzero(sendbuf, BUF_LEN); bzero(recvbuf, BUF_LEN); scanf("%s", sendbuf); write(sockfd, sendbuf, strlen(sendbuf)); read(sockfd, recvbuf, strlen(sendbuf)); printf("recieve: %s\n", recvbuf); } return; } int main(int argc, char **argv) { int fd; struct sockaddr_in servaddr; fd = socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = inet_addr(SERV_ADDR); servaddr.sin_port = htons(SERV_PORT); if (connect(fd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1) { printf("connect error: %s\n", strerror(errno)); return 0; } str_cli(stdin, fd); return 0; } /********************** * server **********************/ #include <stdio.h> #include <sys/socket.h> #include <netinet/in.h> #include <errno.h> #include <signal.h> #include <sys/wait.h> #include <sys/types.h> #include <string.h> #include <stdlib.h> #define SERV_PORT 5358 #define MAX_CONN 10 #define BUF_LEN 1024 void str_echo(FILE *fp, int sockfd){ ssize_t nread; char buf[BUF_LEN] = {0}; while(1) { bzero(buf, BUF_LEN); if((nread = read(sockfd, buf, BUF_LEN)) == -1) { if(errno == EINTR) { continue; } else { printf("readn error: %s\n", strerror(errno)); continue; } } else if (nread == 0) { break; } else { printf("%s\n", buf); write(sockfd, buf, nread); } } } void sig_chld(int sig){ pid_t pid; int state; while((pid = waitpid(-1, &state, WNOHANG)) > 0){ printf("child process %d exited.", pid); } return; } int main(int argc, char **argv) { int listenfd, connfd, cliaddrlen; pid_t childpid; struct sockaddr_in servaddr, cliaddr; if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){ printf("socket error: %s\n", strerror(errno)); return 0; } bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(SERV_PORT); if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1){ printf("bind error: %s\n", strerror(errno)); return 0; } if(listen(listenfd, MAX_CONN) == -1){ printf("listen error: %s\n", strerror(errno)); return 0; } signal(SIGCHLD, sig_chld); while(1){ cliaddrlen = sizeof(cliaddr); if((connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddrlen)) == -1){ if(errno == EINTR){ continue; } else{ printf("accept error: %s\n", strerror(errno)); continue; } } if((childpid = fork()) == 0){ close(listenfd); str_echo(stdin, connfd); exit(0); } else if(childpid > 0){ close(connfd); } else{ printf("fork error!\n"); continue; } } }
需要注意的问题:
1、必须对僵死子进程进行处理
unp上说到处理僵死子进程应当在信号处理函数里面调用waitpid而不是wait,原因在于unix信号不排队,从而导致信号处理函数调用次数 不够,不能保证处理完所有僵死进程。个人认为问题的本质在于接收到一个SUGCHLD应该调用多少次wait或者 waitpid,wait(&status)和waitpid(-1, &status, WNOHANG)的唯一区别在于前者是阻塞的,但是既然是收到SIGCHLD信号就证明有子进程结束,这样阻塞与不阻塞就没有什么区别。而unp上在调用 二者时一个最大的区别在于wait只调用了一次,但是waitpid是放在一个循环中的,直到处理完所有的僵死子进程,问题的关键应在这个循环里。
2、服务器进程终止,客户端不能立即收到正确信息
参考<<服务器的几种异常终止>>
3、read和write函数存在一些问题
read函数有可能读不够期望的字节数就返回,原因在于内核中buf的字节数不足,为此,unp上专门写了个readn函数来解决。
write函数同样可能出现一次无法写完期望的字节数,原因在于内核的buf已满或被信号中断,unp上同样也给出了writen处理这个问题。
处理这些情况本身也确实比较麻烦,具体情况具体分析吧。