服务端流程步骤
- socket函数创建监听套接字lfd 
- bind函数将监听套接字绑定ip和端口 
- listen函数设置服务器为被动监听状态,同时创建一条未完成连接队列(没走完tcp三次握手流程的连接),和一条已完成连接队列(已完成tcp三次握手的连接) 
- accept函数循环的从已完成连接队列中提取连接,并返回一个新的套接字cfd跟客户端进行通信 
- fork函数创建一个子进程,让子进程与客户端进行通信 
- 子进程:read函数循环的从r缓冲区读取客户端发送的数据,write函数将要发送的数据写入w缓冲区 
- close函数关闭套接字 
客户端流程步骤
- socket函数创建套接字 
- connect函数连接服务器 
- write函数将要发送的数据写入w缓冲区,read函数从r缓冲区读取服务器发送给客户端的数据 
- close函数关闭套接字 
客户端跟服务器通信流程图
 
 
   相关函数
- int socket(int domain, int type, int protocol); 
功能:创建一个用于网络通信的套接字文件描述符
参数:domain:协议族(AF_INET:ipv4,AF_INET6:ipv6,等等)
type:套接字类型(SOCK_DGRAM:udp,SOCK_STREAM:tcp,等等)
protocol:用于制定某个协议的特定类型,即type类型中的某个类型,通常不用管它,设置 为 0
返回值:成功则返回socket套接字描述符, 失败返回-1,并设置errno
头文件:#include <sys/socket.h>
- int bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen); 
功能:将sockfd绑定ip和端口
参数:sockfd:套接字
my_addr:存放有协议,ip,端口的结构体信息
addrlen:my_addr结构体大小
返回值:成功返回0,失败返回-1,并设置errno
头文件:#include <sys/types.h>
#include <sys/socket.h>
- int listen(int s, int backlog); 
功能:让服务器处于被动监听状态,同时创建了一条未完成三次握手的连接队列和一条已经完成三次握 手的连接队列
参数:s:套接字
backlog:支持未完成连接和已完成连接之和的最大值,一般设置128
返回值:成功返回0,失败返回-1,并设置errno
头文件:#include <sys/socket.h>
- int accept(int s, struct sockaddr *addr, socklen_t *addrlen); 
功能:从已完成连接队列中提取客户端连接
参数:s:套接字
addr:存放成功连接的客户端的ip,端口等信息结构体
addrlen:存放addr结构体大小的变量地址
返回值:成功则返回一个非负整数标识这个连接套接字,是否返回-1
头文件:#include <sys/types.h>
#include <sys/socket.h>
- ssize_t read(int fd, void *buf, size_t count); 
功能:从文件描述符 fd 中读取 count 字节的数据并放入从 buf 开始的缓冲区中
参数:fd:文件描述符
buf:缓冲区
count:读count字节
返回值:成功时返回读取到的字节数,失败返回-1,并设置errno
头文件:#include <unistd.h>
- ssize_t write(int fd, const void *buf, size_t count); 
功能:向文件描述符fd所引用的文件中写入从buf开始的缓冲区中count字节的数据
参数:fd:文件描述符
buf:缓冲区
count:写count字节
返回值:功时返回所写入的字节数,失败返回-1,并设置errno
头文件:#include <unistd.h>
- int close(int fd); 
功能:关闭 一个文件描述符
参数:fd:要关闭的文件描述符
返回值:成功返回0,失败返回-1,并设置errno
头文件:#include <unistd.h>
- int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 
功能:连接服务器
参数:sockfd:套接字
addr:服务器的ip,端口等结构体信息
addrlen:addr结构体大小
返回值:成功返回0,失败返回-1,并设置errno
头文件:#include <sys/socket.h>
- pid_t fork(void); 
功能:创建一个子进程
参数:无
返回值:父进程返回子进程的进程id,子进程返回0
头文件:#include <unistd.h>
- int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); 
功能:设置信号集屏蔽字
参数:how:SIG_BLOCK (mask |= set),SIG_UNBLOCK (mask &= ~set),SIG_SETMASK (mask = set)
set:信号集
oldset:旧的信号集
返回值:成功返回0,失败返回-1,并设置errno
头文件:#include <signal.h>
- pid_t waitpid(pid_t pid, int *status, int options); 
功能:回收已结束的子进程资源
参数:pid:pid<-1 等待进程组识别码为pid绝对值的任何子进程
pid=-1 等待任何子进程, 相当于wait()
pid=0 等待进程组识别码与目前进程相同的任何子进程
pid>0 等待任何子进程识别码为pid的子进程
status:子进程的结束状态值
options:WNOHANG 如果没有任何已经结束的子进程则马上返回, 不予以等待
WUNTRACED 如果子进程进入暂停执行情况则马上返回, 但结束状态不予以理会
返回值:成功返回子进程pid,失败返回-1
头文件: #include <sys/types.h>
#include <sys/wait.h>
服务端代码
   
    - 
     
      
     
     
      
       #include <stdio.h>
      
     
- 
     
      
     
     
      
       #include <sys/socket.h>
      
     
- 
     
      
     
     
      
       #include <arpa/inet.h>
      
     
- 
     
      
     
     
      
       #include <string.h>
      
     
- 
     
      
     
     
      
       #include <unistd.h>
      
     
- 
     
      
     
     
      
       #include <signal.h>
      
     
- 
     
      
     
     
      
       #include <stdlib.h>
      
     
- 
     
      
     
     
      
       #include <sys/types.h>
      
     
- 
     
      
     
     
      
       #include <sys/wait.h>
      
     
- 
     
      
     
     
      
       #include <errno.h>
      
     
- 
     
      
     
     
       
      
     
- 
     
      
     
     
      
       #define BUF_SIZE 256
      
     
- 
     
      
     
     
       
      
     
- 
     
      
     
     
      
       void do_sigchld(int signo, siginfo_t *siginfo, void *p)
      
     
- 
     
      
     
     
      
       {
      
     
- 
     
      
     
     
          
       int status;
      
     
- 
     
      
     
     
          
       pid_t pid;
      
     
- 
     
      
     
     
          
       while((pid = 
       waitpid(
       0, &status, WNOHANG)) > 
       0) 
       //0:回收跟调用进程同组的子进程的资源,WNOHANG:不阻塞
      
     
- 
     
      
     
     
      
           {
      
     
- 
     
      
     
     
              
       if (
       WIFEXITED(status))
      
     
- 
     
      
     
     
                  
       printf(
       "pid = %d, parent pid = %d, child pid = %d, exit status %d\n", pid, 
       getpid(), siginfo->si_pid, 
       WEXITSTATUS(status));
      
     
- 
     
      
     
     
              
       else 
       if (
       WEXITSTATUS(status))
      
     
- 
     
      
     
     
                  
       printf(
       "pid = %d, parent pid = %d, child pid = %d, exit by signal %d\n ", pid, 
       getpid(), siginfo->si_pid, 
       WIFSIGNALED(status));
      
     
- 
     
      
     
     
      
           }
      
     
- 
     
      
     
     
      
       }
      
     
- 
     
      
     
     
       
      
     
- 
     
      
     
     
      
       void sys_err(const char *str, int err)
      
     
- 
     
      
     
     
      
       {
      
     
- 
     
      
     
     
          
       perror(str);
      
     
- 
     
      
     
     
          
       exit(err);
      
     
- 
     
      
     
     
      
       }
      
     
- 
     
      
     
     
       
      
     
- 
     
      
     
     
      
       int main(int argc, char *argv[])
      
     
- 
     
      
     
     
      
       {
      
     
- 
     
      
     
     
          
       if (argc < 
       2)
      
     
- 
     
      
     
     
      
           {
      
     
- 
     
      
     
     
              
       printf(
       "%s port\n", argv[
       0]);
      
     
- 
     
      
     
     
              
       exit(
       1);
      
     
- 
     
      
     
     
      
           }
      
     
- 
     
      
     
     
          
       //创建流式套接字
      
     
- 
     
      
     
     
          
       int lfd = 
       socket(AF_INET, SOCK_STREAM, 
       0);
      
     
- 
     
      
     
     
          
       if (lfd < 
       0)
      
     
- 
     
      
     
     
              
       sys_err(
       "socket", 
       1);
      
     
- 
     
      
     
     
       
      
     
- 
     
      
     
     
          
       //绑定ip端口 
      
     
- 
     
      
     
     
          
       struct 
       sockaddr_in server_addr;
      
     
- 
     
      
     
     
      
           server_addr.sin_family = AF_INET;
      
     
- 
     
      
     
     
      
           server_addr.sin_port = 
       htons((
       unsigned 
       short)
       atoi(argv[
       1]));
      
     
- 
     
      
     
     
      
           server_addr.sin_addr.s_addr = 
       0; 
       //0表示将本机所有ip都绑定上
      
     
- 
     
      
     
     
          
       int ret = 
       bind(lfd, (
       struct sockaddr *)&server_addr, 
       sizeof(server_addr));
      
     
- 
     
      
     
     
          
       if (ret < 
       0)
      
     
- 
     
      
     
     
              
       sys_err(
       "bind", 
       1);
      
     
- 
     
      
     
     
          
      
     
- 
     
      
     
     
          
       //监听
      
     
- 
     
      
     
     
      
           ret = 
       listen(lfd, 
       128);
      
     
- 
     
      
     
     
          
       if (ret < 
       0)
      
     
- 
     
      
     
     
              
       sys_err(
       "listen", 
       1);
      
     
- 
     
      
     
     
          
      
     
- 
     
      
     
     
          
       //阻塞SIGCHLD信号
      
     
- 
     
      
     
     
          
       sigset_t set;
      
     
- 
     
      
     
     
          
       sigemptyset(&set);
      
     
- 
     
      
     
     
          
       sigaddset(&set, SIGCHLD);
      
     
- 
     
      
     
     
          
       sigprocmask(SIG_BLOCK, &set, 
       NULL);
      
     
- 
     
      
     
     
          
       int block = 
       1;
      
     
- 
     
      
     
     
          
       //循环提取
      
     
- 
     
      
     
     
          
       int cfd;
      
     
- 
     
      
     
     
          
       pid_t pid;
      
     
- 
     
      
     
     
          
       struct 
       sockaddr_in client_addr; 
      
     
- 
     
      
     
     
          
       socklen_t len = 
       sizeof(client_addr);
      
     
- 
     
      
     
     
          
       char buf[BUF_SIZE];
      
     
- 
     
      
     
     
          
       ssize_t size;
      
     
- 
     
      
     
     
          
       char ip[INET_ADDRSTRLEN] = 
       "";
      
     
- 
     
      
     
     
          
       while(
       1)
      
     
- 
     
      
     
     
      
           {
      
     
- 
     
      
     
     
      
               cfd = 
       accept(lfd, (
       struct sockaddr *)&client_addr, &len);
      
     
- 
     
      
     
     
              
       if (cfd < 
       0)
      
     
- 
     
      
     
     
      
               {
      
     
- 
     
      
     
     
                  
       if (errno == EINTR)
      
     
- 
     
      
     
     
                      
       continue;
      
     
- 
     
      
     
     
       
      
     
- 
     
      
     
     
                  
       sys_err(
       "accept", 
       1);
      
     
- 
     
      
     
     
      
               }
      
     
- 
     
      
     
     
       
      
     
- 
     
      
     
     
              
       printf(
       "client ip = %s, port = %d connect success\n", 
       inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, ip, 
       sizeof(ip)), 
       ntohs(client_addr.sin_port));
      
     
- 
     
      
     
     
      
               pid = fork();
      
     
- 
     
      
     
     
              
       if (pid == 
       0)
      
     
- 
     
      
     
     
      
               {
      
     
- 
     
      
     
     
                  
       //in child
      
     
- 
     
      
     
     
                  
       close(lfd); 
       //关闭不用了的监听套接字
      
     
- 
     
      
     
     
                  
       //解除阻塞SIGCHLD信号
      
     
- 
     
      
     
     
                  
       sigprocmask(SIG_UNBLOCK, &set, 
       NULL);
      
     
- 
     
      
     
     
      
                   block = 
       0;
      
     
- 
     
      
     
     
       
      
     
- 
     
      
     
     
                  
       while(
       1)
      
     
- 
     
      
     
     
      
                   {
      
     
- 
     
      
     
     
                      
       memset(buf, 
       0, 
       sizeof(buf));
      
     
- 
     
      
     
     
      
                       size = 
       read(cfd, buf, 
       sizeof(buf));
      
     
- 
     
      
     
     
                      
       if (size == 
       0) 
       //客户端断开连接
      
     
- 
     
      
     
     
      
                       {
      
     
- 
     
      
     
     
                          
       printf(
       "client close\n");
      
     
- 
     
      
     
     
                          
       break;
      
     
- 
     
      
     
     
      
                       }
      
     
- 
     
      
     
     
       
      
     
- 
     
      
     
     
                      
       printf(
       "%s\n", buf);
      
     
- 
     
      
     
     
                      
       write(cfd, buf, size);
      
     
- 
     
      
     
     
      
                   }
      
     
- 
     
      
     
     
       
      
     
- 
     
      
     
     
                  
       break;
      
     
- 
     
      
     
     
      
               }
      
     
- 
     
      
     
     
              
       else 
       if (pid > 
       0)
      
     
- 
     
      
     
     
      
               {
      
     
- 
     
      
     
     
                  
       //in parent
      
     
- 
     
      
     
     
                  
       close(cfd); 
       //关闭不用了的跟客户端通讯的套接字
      
     
- 
     
      
     
     
                  
       if (
       1 == block)
      
     
- 
     
      
     
     
      
                   {
      
     
- 
     
      
     
     
                      
       //先捕捉
      
     
- 
     
      
     
     
                      
       struct 
       sigaction sa;
      
     
- 
     
      
     
     
      
                       sa.sa_sigaction = do_sigchld;
      
     
- 
     
      
     
     
                      
       sigemptyset(&sa.sa_mask);
      
     
- 
     
      
     
     
      
                       sa.sa_flags = SA_SIGINFO;
      
     
- 
     
      
     
     
                      
       sigaction(SIGCHLD, &sa, 
       NULL);
      
     
- 
     
      
     
     
                      
       //后解除阻塞
      
     
- 
     
      
     
     
                      
       sigprocmask(SIG_UNBLOCK, &set, 
       NULL);
      
     
- 
     
      
     
     
      
                       block = 
       0;
      
     
- 
     
      
     
     
      
                   }
      
     
- 
     
      
     
     
      
               }
      
     
- 
     
      
     
     
              
       else
      
     
- 
     
      
     
     
                  
       sys_err(
       "fork", 
       1);
      
     
- 
     
      
     
     
      
           }
      
     
- 
     
      
     
     
          
      
     
- 
     
      
     
     
          
       //关闭套接字
      
     
- 
     
      
     
     
          
       if (pid == 
       0)
      
     
- 
     
      
     
     
              
       close(cfd);
      
     
- 
     
      
     
     
          
       else 
       if (pid > 
       0)
      
     
- 
     
      
     
     
              
       close(lfd);
      
     
- 
     
      
     
     
       
      
     
- 
     
      
     
     
          
       return 
       0;
      
     
- 
     
      
     
     
      
       }
      
     
  客户端代码
   
    - 
     
      
     
     
      
       #include <stdio.h>
      
     
- 
     
      
     
     
      
       #include <unistd.h>
      
     
- 
     
      
     
     
      
       #include <sys/socket.h>
      
     
- 
     
      
     
     
      
       #include <arpa/inet.h>
      
     
- 
     
      
     
     
      
       #include <stdlib.h>
      
     
- 
     
      
     
     
      
       #include <string.h>
      
     
- 
     
      
     
     
       
      
     
- 
     
      
     
     
      
       int main(int argc, char *argv[])
      
     
- 
     
      
     
     
      
       {
      
     
- 
     
      
     
     
          
       if (argc < 
       3)
      
     
- 
     
      
     
     
      
           {
      
     
- 
     
      
     
     
              
       printf(
       "%s sever_ip server_port\n", argv[
       0]);
      
     
- 
     
      
     
     
              
       exit(
       1);
      
     
- 
     
      
     
     
      
           }
      
     
- 
     
      
     
     
       
      
     
- 
     
      
     
     
          
       //创建流式套接字
      
     
- 
     
      
     
     
          
       int fd = 
       socket(AF_INET, SOCK_STREAM, 
       0);
      
     
- 
     
      
     
     
          
       if (fd < 
       0)
      
     
- 
     
      
     
     
      
           {
      
     
- 
     
      
     
     
              
       perror(
       "socket");
      
     
- 
     
      
     
     
              
       exit(
       1);
      
     
- 
     
      
     
     
      
           }
      
     
- 
     
      
     
     
       
      
     
- 
     
      
     
     
          
       //连接服务器
      
     
- 
     
      
     
     
          
       struct 
       sockaddr_in addr;
      
     
- 
     
      
     
     
      
           addr.sin_family = AF_INET;
      
     
- 
     
      
     
     
      
           addr.sin_port = 
       htons((
       unsigned 
       short)
       atoi(argv[
       2]));
      
     
- 
     
      
     
     
      
           addr.sin_addr.s_addr = 
       inet_addr(argv[
       1]);
      
     
- 
     
      
     
     
          
       int ret = 
       connect(fd, (
       struct sockaddr *)&addr, 
       sizeof(addr));
      
     
- 
     
      
     
     
          
       printf(
       "connect ret = %d\n", ret);
      
     
- 
     
      
     
     
          
       if (ret < 
       0)
      
     
- 
     
      
     
     
      
           {
      
     
- 
     
      
     
     
              
       perror(
       "connect");
      
     
- 
     
      
     
     
              
       exit(
       1);
      
     
- 
     
      
     
     
      
           }
      
     
- 
     
      
     
     
       
      
     
- 
     
      
     
     
          
       //读写数据
      
     
- 
     
      
     
     
          
       char buf_w[
       128] = 
       "";
      
     
- 
     
      
     
     
          
       char buf_r[
       128] = 
       "";
      
     
- 
     
      
     
     
          
       ssize_t size_r = 
       0;
      
     
- 
     
      
     
     
          
       while(
       1)
      
     
- 
     
      
     
     
      
           {
      
     
- 
     
      
     
     
              
       fgets(buf_w, 
       sizeof(buf_w), stdin);
      
     
- 
     
      
     
     
      
               buf_w[
       strlen(buf_w) - 
       1] = 
       0;
      
     
- 
     
      
     
     
              
       write(fd, buf_w, 
       strlen(buf_w));
      
     
- 
     
      
     
     
      
               size_r = 
       read(fd, buf_r, 
       sizeof(buf_r));
      
     
- 
     
      
     
     
              
       if (size_r == 
       0) 
       //服务器断开
      
     
- 
     
      
     
     
                  
       break;
      
     
- 
     
      
     
     
              
       else
      
     
- 
     
      
     
     
                  
       printf(
       "%s\n", buf_r);
      
     
- 
     
      
     
     
       
      
     
- 
     
      
     
     
              
       memset(buf_w, 
       0, 
       sizeof(buf_w));
      
     
- 
     
      
     
     
              
       memset(buf_r, 
       0, 
       sizeof(buf_r));
      
     
- 
     
      
     
     
      
           }
      
     
- 
     
      
     
     
       
      
     
- 
     
      
     
     
          
       //关闭套接字
      
     
- 
     
      
     
     
          
       close(fd);
      
     
- 
     
      
     
     
       
      
     
- 
     
      
     
     
          
       return 
       0;
      
     
- 
     
      
     
     
      
       }
      
     
  结果
- 3个客户端连接服务器时 
服务端
 
 
   客户端
 
 
   ps aux 可以看到有4个服务器进程(其中一个是主进程,用于监听处理新的连接,其他3个子进程用于跟客户端进行通信)
 
 
   - 回射服务信息截图 
客户端1:
 
 
   客户端2:
 
 
   客户端3:
 
 
   服务器收到进行打印:
 
 
   3个客户端都退出时
服务端收到客户端退出,并回收跟客户端进行通信的3个子进程的资源
 
 
   ps aux 查看可以看到只剩主进程,用于监听处理新的连接,其他3个子进程都已结束
 
 
   转载:https://blog.csdn.net/d704791892/article/details/129168681
 
					