首页 > 代码库 > Linux 套接字编程 - TCP连接基础

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_RESTART104         act.sa_flags |= SA_RESTART;105         printf("SA_RESTART\n");106 #endif107         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 256void 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");}