Linux 套接字编程 - TCP连接基础
第五章的内容,实现一个echo服务器和对应的客户端,主要收获:
0. TCP socket编程主要基本步骤
1. SIGCHLD信号含义(子进程退出时向父进程发送,提醒父进程对其状态信息进行一个获取),waitpid 和 wait在使用上的差异,前者可以配置参数设定为非阻塞方式调用,更加灵活。
2. 信号处理函数与其过程(尤其是信号发生后不列队这个性质),相同的信号多次发生(间隔非常接近的话)可能仅会调用一次信号处理函数
3. 信号处理对慢系统(阻塞)调用如accept等的影响(如果信号处理设置时没有置SA_RESTART),accept被中断后直接返回EINTR,而不是一个合法的socket fd,所以对一些调用的错误值检测并不是杞人忧天
4. 僵尸进程,只要父进程调用了wait*函数获取了已死子进程的状态信息后,它就消失了。但如果父进程产生了超多子进程,而他们有很快的死掉,然后父进程也不调用wait*函数,那么会使得pid号不够用
服务端程序:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 5 #include <unistd.h> 6 #include <signal.h> 7 8 #include <sys/socket.h> 9 #include <arpa/inet.h> 10 #include <netinet/in.h> 11 12 13 #define SOCKET_BACKLOG 100 14 #define SERVER_PORT 1234 15 #define BUF_SIZE 256 16 17 void echo(int fd); 18 19 void setup_signal_handler(); 20 21 int main() { 22 /* setup SIGCHLD handler */ 23 setup_signal_handler(); 24 25 /* define socket address */ 26 struct sockaddr_in server = {0}; 27 server.sin_family = AF_INET; 28 server.sin_port = htons( SERVER_PORT ); 29 30 31 /* define socket file descriptor */ 32 int server_fd = socket(AF_INET, SOCK_STREAM, 0); 33 34 /* bind socket file descriptor to the socket address */ 35 bind(server_fd, (struct sockaddr *)&server, sizeof(server)); 36 37 /* listen on this socket file descriptor */ 38 listen( server_fd, SOCKET_BACKLOG ); 39 40 /* define socket struct/file descriptor used to present remote peer(client) */ 41 struct sockaddr_in client = {0}; 42 int client_fd; 43 int client_sockaddr_len = 0; 44 45 /* application send buffer */ 46 char buffer[BUF_SIZE]; 47 48 while (1) { 49 printf("server ready to accept\n"); 50 client_fd = accept(server_fd, (struct sockaddr *)&client, &client_sockaddr_len); 51 if (client_fd < 0) { 52 /* if SA_RESTART is not set in setup_signal_handler and 53 * then when process is interrupted by SIGCHLD 54 * the accept() will return EINTR instead of a valid socket fd 55 */ 56 printf("server accept error!\n"); 57 continue; 58 } 59 if (fork() == 0) { 60 close(server_fd); 61 printf("child process start pid(%d)\n", getpid()); 62 63 echo(client_fd); 64 65 printf("child process exit pid(%d)\n", getpid()); 66 exit(0); 67 } 68 close(client_fd); 69 } 70 71 return 0; 72 } 73 74 void echo(int fd) { 75 int n; 76 char buffer[BUF_SIZE]; 77 while ((n = read(fd, buffer, BUF_SIZE)) > 0) { 78 write(fd, buffer, n); 79 } 80 } 81 82 void signal_child_handler(int signo) { 83 pid_t pid; 84 int stat; 85 86 /* pid = wait(&stat); 87 * signal is not queued, many child process exits 88 * may just cause one signal handle process 89 * so we should use waitpid() in a row instead of a single wait() 90 * to collect child process information 91 */ 92 while ((pid = waitpid(-1, &stat, WNOHANG)) > 0) { 93 printf("child process pid(%d) terminated\n", pid); 94 } 95 } 96 97 void setup_signal_handler() { 98 struct sigaction act, old_act; 99 100 act.sa_handler = signal_child_handler; 101 sigemptyset(&act.sa_mask); 102 act.sa_flags = 0; 103 #ifdef SA_RESTART 104 act.sa_flags |= SA_RESTART; 105 printf("SA_RESTART\n"); 106 #endif 107 if (sigaction(SIGCHLD, &act, &old_act) < 0) { 108 printf("setup SIGCHLD Failed."); 109 } 110 }
客户端程序:
#include <unistd.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #define SERVER_PORT 1234 #define SERVER_IP "127.0.0.1" #define BUF_SIZE 256 void send_echo(FILE* fp, int fd); int main() { int sockfd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in server_addr = {0}; server_addr.sin_family = AF_INET; server_addr.sin_port = htons(SERVER_PORT); inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr); connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)); send_echo(stdin, sockfd); return 0; } void send_echo(FILE* fp, int fd) { char send_buf[BUF_SIZE] = {0}; char recv_buf[BUF_SIZE] = {0}; int readn = 0; int writen = 0; while(fgets(send_buf, BUF_SIZE, fp) != NULL) { if ((writen = write(fd, send_buf, strlen(send_buf) + 1)) < 0) { printf("1st write error\n"); break; } else { printf("1st write ok\n"); } sleep(1); if ((writen = write(fd, "(test)", strlen("(test)") + 1)) < 0) { printf("2nd write error\n"); break; } else { printf("2nd write ok\n"); } sleep(1); if ((readn = read(fd, recv_buf, BUF_SIZE)) < 0) { printf("read error\n"); break; } else if (readn == 0) { printf("read EOF\n"); break; } fputs(recv_buf, stdout); } printf("client exit\n"); }
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。