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");
}

 

Linux 套接字编程 - TCP连接基础,古老的榕树,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。