客户端代码如下:
#include <sys/socket.h> #include <arpa/inet.h> #include <stdio.h> #include <string.h> #include <unistd.h> void str_cli(FILE *stream, int fd); int main(int argc, char* argv[]) { if (argc < 2) { fprintf(stdout, "Usage: %s ip_address", argv[0]); } int socket_fd[5]; int i; for(i = 0; i < 5; i++) { socket_fd[i] = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in server_addr; memset(&server_addr, 0, sizeof(server_addr)); inet_pton(AF_INET, argv[1], &(server_addr.sin_addr.s_addr)); server_addr.sin_port = htons(13500); server_addr.sin_family = AF_INET; printf("%d", i); if ((connect(socket_fd[i], (struct sockaddr *) &server_addr, sizeof(server_addr))) == -1) { printf("socket %d connect failed.", i); } } str_cli(stdin, socket_fd[0]); return 0; } void str_cli(FILE *stream, int fd) { char buff[5000]; char read_buff[5000]; printf("begin to read\n"); while((fgets(buff, sizeof(buff), stream)) != NULL) { fprintf(stderr, "%s\n", buff); write(fd, buff, strlen(buff)); read(fd, read_buff, sizeof(read_buff)); fprintf(stdout, "$%s\n", read_buff); printf("begin to read\n"); } }
服务器端代码如下:
#include <sys/socket.h> #include <string.h> #include <arpa/inet.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <sys/signal.h> #include <sys/wait.h> #include <errno.h> void str_echo(int fd); void sig_chld(int signal); int main() { signal(SIGCHLD, sig_chld); int connect_fd, listen_fd; listen_fd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in client_addr, server_addr; memset(&server_addr, 0, sizeof(server_addr)); memset(&client_addr, 0, sizeof(client_addr)); server_addr.sin_addr.s_addr = htonl(INADDR_ANY); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(13500); bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)); listen(listen_fd, 100); while(1) { socklen_t client_addr_length = sizeof(client_addr); if ((connect_fd = accept(listen_fd, (struct sockaddr *) &client_addr, &client_addr_length) < 0)) { printf("connect_fd is %d\n", connect_fd); if (EINTR == errno) { continue; } else { return -1; } } pid_t child_pid; if ((child_pid = fork()) == 0) { //child process close(listen_fd); str_echo(connect_fd); exit(0); } close(connect_fd); } } void str_echo(int fd) { char buff[5000]; int read_length; while (1) { printf("1\n"); if ( (read_length = read(fd, buff, sizeof(buff))) > 0) { printf("%s\n", buff); write(fd, buff, read_length); printf("%s\n", buff); } else { printf("2\n"); return; } } } void sig_chld(int signal) { pid_t pid; int status; pid = wait(&status); printf("child %d terminated!\n", signal); return; }
程序的预期功能是:
客户端从标准输入读入一行,发送到服务器,服务器接受到消息一行,将该消息回送到客户端,客户端将接受到回送消息显示在标准输出上。
bug特征:
第一次打开客户端,输入字符,不能正确回显,消息阻塞在read部分,不关闭服务端,重启客户端,工具就能正常工作。
bug分析:
服务端程序的
if ((connect_fd = accept(listen_fd, (struct sockaddr *) &client_addr, &client_addr_length) < 0))
括号加错了位置。第一次开启客户端程序的时候,connect_fd被错误的赋值为1,导致其accept、read、write都在标准输入上进行。但是执行到
close(connect_fd);
的时候,标准输入被关闭,当关闭客户端程序再次打开的时候,accept返回的的结果是当前最小fd,该fd为0,0<0的结果为0,阴差阳错,connect_fd被赋予了正确的fd,所以第二次,程序就能正常工作。所以初学者看上去诡异的bug就出现了。感谢文卿大牛啊。
经验教训:
如果按照编程规范进行,就能避免很多着由看似不起眼的小问题出现的bug.....编程规范!!!!