zoukankan      html  css  js  c++  java
  • linux网络编程:使用单进程实现多客户端通信

    服务端:

    //回射服务器
    //避免僵尸进程
    #include "unistd.h"
    #include "sys/types.h"
    #include "sys/socket.h"
    #include "sys/wait.h"
    #include "netinet/in.h"
    #include "arpa/inet.h"
    #include "stdlib.h"
    #include "stdio.h"
    #include "errno.h"
    #include "string.h"
    #include "signal.h"
    
    
    #define ERR_EXIT(m)         
      do                        
      {                         
        perror(m);              
        exit(EXIT_FAILURE);     
      }while(0)
    
    ssize_t readn(int fd , void *buf, size_t count)
    {
      size_t nleft = count; //剩余字节数  //在32位系统中size_t是4字节的,而在64位系统中,size_t是8字节的  增加可移植性
      ssize_t nread;//读到的字节数 //ssize_t:这个数据类型用来表示可以被执行读写操作的数据块的大小,它表示的是sign size_t类型的。
      char *bufp = (char*)buf;
    
      while (nleft > 0)
      {
        if ((nread = read(fd , bufp ,nleft)) < 0)
        {
          if (errno == EINTR)
            continue;
          return -1;
        }
        else if (nread == 0)   //对等方关闭
          return count - nleft;
        bufp += nread;
        nleft -= nread;
      }
      return count;
    }
    
    ssize_t writen(int fd , const void *buf , size_t count)
    {
      size_t nleft = count; //剩余字节数
      ssize_t nwritten;
      char *bufp = (char*)buf;
    
      while (nleft > 0)
      {
        if ((nwritten = write(fd , bufp ,nleft)) < 0)
        {
          if (errno == EINTR)
            continue;
          return -1;
        }
        else if (nwritten == 0)   //对等方关闭
          continue;
        bufp += nwritten;
        nleft -= nwritten;
      }
      return count;
    }
    
    ssize_t recv_peek(int sockfd , void *buf , size_t len)
    {
      while (1)
      {
        int ret = recv(sockfd , buf , len , MSG_PEEK);
        if (ret == -1 && errno == EINTR)
          continue;
        return ret;
      }
    }
    
    ssize_t readline(int sockfd , void *buf , size_t maxline)
    {
      int ret;
      int nread;
      char *bufp = buf;
      int nleft = maxline;
      while (1)
      {
        ret = recv_peek(sockfd , bufp , nleft);
        if (ret < 0 )
          return ret;
        else if (ret == 0)
          return ret;
        nread = ret;
        int i;
        for (i=0;i<nread;i++)
        {
          if (bufp[i] == '
    ')
          {
            ret = readn(sockfd,bufp,i+1); // i+1  包括 
    
            if (ret != i + 1)
              exit(EXIT_FAILURE);
            return ret;
          }
        }
        if (nread > nleft)
          exit(EXIT_FAILURE);
        nleft -= nread;
        ret = readn(sockfd , bufp , nread);
        if (ret != nread)
          exit(EXIT_FAILURE);
        bufp += nread;
      }
      return -1; //运行到这里就出错了
    }
    
    void echo_srv(int conn)
    {
      char recvbuf[1024];
      int n;
      while(1)
      {
        memset(&recvbuf,0,sizeof(recvbuf));
        int ret = readline(conn,recvbuf,1024);
        //扑捉客户端关闭
        if (ret == -1)
          ERR_EXIT("readline");
        if (ret == 0)
        {
          printf("clientclose
    ");
          break;
        }
        fputs(recvbuf,stdout);
        writen(conn,recvbuf,strlen(recvbuf));
      }
    }
    
    void handle_sigchld(int sig)
    {
      // wait(NULL);
      while (waitpid(-1,NULL,WNOHANG) > 0);//waitpid()会暂时停止目前进程的执行,直到有信号来到或子进程结束。
    }
    
    int main(int argc, char const *argv[])
    {
     // signal(SIGCHLD,SIG_IGN);//避免僵尸进程
      signal(SIGCHLD,handle_sigchld);//避免僵尸进程
      int listenfd;
      if ((listenfd = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP)) < 0) //IPv4为PF_INET,IPv6为PF_INET6;TCP传输时为SOCK_STREAM, UDP位SOCK_DGRAM
        ERR_EXIT("socket");
      struct sockaddr_in servaddr;
      memset(&servaddr,0,sizeof(servaddr));
      servaddr.sin_family = AF_INET; //设置地址家族   协议族,在socket编程中只能是AF_INET
      servaddr.sin_port = htons(5188); //设置端口
      servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //设置地址
      // servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
      // inet_aton("127.0.0.1",&servaddr.sin_addr);
    
      int on = 1;
      if (setsockopt(listenfd, SOL_SOCKET , SO_REUSEADDR, &on , sizeof(on)) < 0)//setsockopt()函数,用于任意类型、任意状态套接口的设置选项值。
        ERR_EXIT("setsockopt");
    
      if (bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) < 0)
        ERR_EXIT("bind");
      /*定义函数:int bind(int sockfd, struct sockaddr * my_addr, int addrlen);
        函数说明:bind()用来设置给参数sockfd 的socket 一个名称. 此名称由参数my_addr 指向一sockaddr 结构,对于不同的socket domain 定义了一个通用的数据结构
    */
      if (listen(listenfd,SOMAXCONN) < 0)
        ERR_EXIT("listen");
    
      struct sockaddr_in peeraddr;
      socklen_t peerlen = sizeof(peeraddr);
      int conn;
    
      // pid_t pid;
      // while (1)
      // {
      //   if ((conn = accept(listenfd,(struct sockaddr*)&peeraddr,&peerlen)) < 0)
      //     ERR_EXIT("accept");
      //   /*
      //   定义函数:int accept(int s, struct sockaddr * addr, int * addrlen);
      //   函数说明:accept()用来接受参数s 的socket 连线. 参数s 的socket 必需先经bind()、listen()函数处理过,
      //   当有连线进来时accept()会返回一个新的socket 处理代码, 往后的数据传送与读取就是经由新的socket处理,
      //   而原来参数s 的socket 能继续使用accept()来接受新的连线要求. 连线成功时, 参数addr
      //   所指的结构会被系统填入远程主机的地址数据, 参数addrlen 为scokaddr 的结构长度. 关于机构sockaddr 的定义请参考bind().
      //   */
      //   printf("ip=%s port=%d
    ", inet_ntoa(peeraddr.sin_addr) , ntohs(peeraddr.sin_port));
      //   //inet_ntoa   将网络地址转换成“.”点隔的字符串格式。
      //   //ntohs  本函数将一个16位数由网络字节顺序转换为主机字节顺序。
      //
      //   pid = fork();
      //   if (pid == -1)
      //     ERR_EXIT("fork");
      //   if (pid == 0)
      //   {
      //     close(listenfd);
      //     echo_srv(conn);
      //     exit(EXIT_SUCCESS);
      //   }else
      //     close(conn);
      // }
    
      int client[FD_SETSIZE];//保存客户端文件描述符
      int maxi = 0;
      int i;
      for(i=0 ; i < FD_SETSIZE;i++)
        client[i] = -1;
    
      int nready;
      int maxfd = listenfd;
      fd_set rset;
      fd_set allset;
      FD_ZERO(&rset);
      FD_ZERO(&allset);
      FD_SET(listenfd,&allset);
      while (1)
      {
        rset = allset;
        nready = select(maxfd+1,&rset,NULL,NULL,NULL);
        if (nready == -1)
        {
          if (errno == EINTR)
            continue;
          ERR_EXIT("select");
        }
        if (nready == 0)
          continue;
        if (FD_ISSET(listenfd,&rset))
        {
          peerlen = sizeof(peeraddr);
          conn = accept(listenfd,(struct sockaddr*)&peeraddr,&peerlen);
          if (conn == -1)
            ERR_EXIT("accept");
          for (i=0;i<FD_SETSIZE;i++)
          {
            if (client[i] < 0)
            {
              client[i] = conn;
              if (i > maxi)
                maxi = i;
              break;
            }
          }
          if (i == FD_SETSIZE)
          {
            fprintf(stderr,"too many clients
    ");
            exit(EXIT_FAILURE);
          }
          printf("ip=%s port=%d
    ", inet_ntoa(peeraddr.sin_addr) , ntohs(peeraddr.sin_port));
          FD_SET(conn,&allset);
          if (conn > maxfd)
            maxfd = conn;
    
          if (--nready <= 0)
            continue;
        }
    
        for(i=0;i<=maxi;i++)
        {
          conn = client[i];
          if (conn == -1)
            continue;
          if (FD_ISSET(conn,&rset))
          {
            char recvbuf[1024] = {0};
            int ret = readline(conn,recvbuf,1024);
            //扑捉客户端关闭
            if (ret == -1)
              ERR_EXIT("readline");
            if (ret == 0)
            {
              printf("clientclose
    ");
              FD_CLR(conn,&allset);
              client[i] = -1;
            }
            fputs(recvbuf,stdout);
            writen(conn,recvbuf,strlen(recvbuf));
    
            if (--nready <= 0)
              break;
          }
        }
      }
      return 0;
    }

    客户端:

    //回射客户端
    
    #include "unistd.h"
    #include "sys/types.h"
    #include "sys/socket.h"
    #include "netinet/in.h"
    #include "arpa/inet.h"
    #include "stdlib.h"
    #include "stdio.h"
    #include "errno.h"
    #include "string.h"
    #include "signal.h"
    
    #define ERR_EXIT(m)         
      do                        
      {                         
        perror(m);              
        exit(EXIT_FAILURE);     
      }while(0)
    
      ssize_t readn(int fd , void *buf, size_t count)
      {
        size_t nleft = count; //剩余字节数
        ssize_t nread;//读到的字节数
        char *bufp = (char*)buf;
    
        while (nleft > 0)
        {
          if ((nread = read(fd , bufp ,nleft)) < 0)
          {
            if (errno == EINTR)
              continue;
            return -1;
          }
          else if (nread == 0)   //对等方关闭
            return count - nleft;
          bufp += nread;
          nleft -= nread;
        }
        return count;
      }
    
      ssize_t writen(int fd , const void *buf , size_t count)
      {
        size_t nleft = count; //剩余字节数
        ssize_t nwritten;
        char *bufp = (char*)buf;
    
        while (nleft > 0)
        {
          if ((nwritten = write(fd , bufp ,nleft)) < 0)
          {
            if (errno == EINTR)
              continue;
            return -1;
          }
          else if (nwritten == 0)   //对等方关闭
            continue;
          bufp += nwritten;
          nleft -= nwritten;
        }
        return count;
      }
    
      ssize_t recv_peek(int sockfd , void *buf , size_t len)
      {
        while (1)
        {
          int ret = recv(sockfd , buf , len , MSG_PEEK);
          if (ret == -1 && errno == EINTR)
            continue;
          return ret;
        }
      }
    
      ssize_t readline(int sockfd , void *buf , size_t maxline)
      {
        int ret;
        int nread;
        char *bufp = buf;
        int nleft = maxline;
        while (1)
        {
          ret = recv_peek(sockfd , bufp , nleft);
          if (ret < 0 )
            return ret;
          else if (ret == 0)
            return ret;
          nread = ret;
          int i;
          for (i=0;i<nread;i++)
          {
            if (bufp[i] == '
    ')
            {
              ret = readn(sockfd,bufp,i+1); // i+1  包括 
    
              if (ret != i + 1)
                exit(EXIT_FAILURE);
              return ret;
            }
          }
          if (nread > nleft)
            exit(EXIT_FAILURE);
          nleft -= nread;
          ret = readn(sockfd , bufp , nread);
          if (ret != nread)
            exit(EXIT_FAILURE);
          bufp += nread;
        }
        return -1; //运行到这里就出错了
      }
    
    void echo_cli(int sock)
    {
      //  char sendbuf[1024] = {0};
      //  char recvbuf[1024] = {0};
      //  while (fgets(sendbuf,sizeof(sendbuf),stdin) != NULL)
      //  {
      //    writen(sock,sendbuf,strlen(sendbuf));  //包头加包体
        //  int ret = readline(sock ,recvbuf,sizeof(recvbuf));
        //  //扑捉客户端关闭
        //  if (ret == -1)
        //    ERR_EXIT("readline");
        //  else if (ret == 0)
        //  {
        //    printf("client close
    ");
        //    break;
        //  }
        //  fputs(recvbuf,stdout);
        //  memset(sendbuf , 0 , sizeof(sendbuf));
        //  memset(recvbuf , 0 , sizeof(recvbuf));
      //  }
      //  close(sock);
        fd_set rset;
        FD_ZERO(&rset);
    
        int nready;
        int maxfd;
        int fd_stdin = fileno(stdin);
        if (fd_stdin > sock)
          maxfd = fd_stdin;
        else
          maxfd = sock;
    
        char sendbuf[1024] = {0};
        char recvbuf[1024] = {0};
        while (1)
        {
          FD_SET(fd_stdin,&rset);
          FD_SET(sock,&rset);
          nready = select(maxfd+1,&rset,NULL,NULL,NULL);
          if (nready == -1)
            ERR_EXIT("select");
          if (nready == 0)
            continue;
          if (FD_ISSET(sock,&rset))
          {
            int ret = readline(sock ,recvbuf,sizeof(recvbuf));
            //扑捉客户端关闭
            if (ret == -1)
              ERR_EXIT("readline");
            else if (ret == 0)
            {
              printf("server close
    ");
              break;
            }
            fputs(recvbuf,stdout);
            memset(recvbuf , 0 , sizeof(recvbuf));
          }
    
          if (FD_ISSET(fd_stdin,&rset))
          {
            if (fgets(sendbuf,sizeof(sendbuf),stdin) == NULL)
              break;
            writen(sock,sendbuf,strlen(sendbuf));
            memset(sendbuf,0,sizeof(sendbuf));
          }
        }
        close(sock);
    
    }
    
    void handle_sigpipe(int sig)
    {
      printf("recv a sig=%d
    ",sig);
    }
    
    int main(void)
    {
      signal(SIGPIPE,handle_sigpipe);
      int sock;
      if ((sock = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP)) < 0) //IPv4为PF_INET,IPv6为PF_INET6;TCP传输时为SOCK_STREAM, UDP位SOCK_DGRAM
        ERR_EXIT("socket");
      struct sockaddr_in servaddr;
      memset(&servaddr,0,sizeof(servaddr));
      servaddr.sin_family = AF_INET; //设置地址家族
      servaddr.sin_port = htons(5188); //设置端口
      servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); //设置地址
    
       if (connect(sock,(struct sockaddr*)&servaddr,sizeof(servaddr)) < 0)
        ERR_EXIT("connect");
    
        struct sockaddr_in localaddr;
        socklen_t addrlen = sizeof(localaddr);
        if (getsockname(sock , (struct sockaddr*)&localaddr , &addrlen) < 0 )
          ERR_EXIT("getsockname");
        printf("ip=%s port=%d
    ", inet_ntoa(localaddr.sin_addr) , ntohs(localaddr.sin_port));
       echo_cli(sock);
       return 0;
    }
  • 相关阅读:
    (转)ASP.NET(C#)FileUpload实现上传限定类型和大小的文件到服务器
    JavaWeb多文件上传及zip打包下载
    使用NeatUpload控件实现ASP.NET大文件上传
    jq+download+文件夹下载
    php大文件下载支持断点续传
    大文件断点上传 js+php
    JavaWeb实现文件上传下载功能实例解析
    【Java】Java批量文件打包下载zip
    Java实现FTP批量大文件上传下载篇1
    PHP之路——大文件上传
  • 原文地址:https://www.cnblogs.com/guaidaodark/p/5575585.html
Copyright © 2011-2022 走看看