Linux网络编程:Select

#include <asm-generic/socket.h>
#include <cctype>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <netinet/in.h>
#include <strings.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include "sock_wrapper.h"

const static uint16_t kPort = 4433;
const static char *kIpStr = "192.168.3.20";

int main() {
    
    

  char buffer[BUFSIZ];

  int listenFd = socket(AF_INET, SOCK_STREAM, 0);

  int reuse = 1;
  setsockopt(listenFd, SOL_SOCKET, SO_REUSEADDR, (const void *)&reuse, sizeof(reuse));

  sockaddr_in addr;
  bzero(&addr, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_port = htons(kPort);
  inet_pton(AF_INET, kIpStr, &addr.sin_addr);

  int ret = bind(listenFd, (const struct sockaddr *)&addr, sizeof(addr));
  if (ret < 0) {
    
    
    perror("bind error.\n");
    return -1;
  }

  //设置最同时监听上限
  ret = listen(listenFd, 125);
  if (ret < 0) {
    
    
    perror("list error.\n");
    return -1;
  }

  //在linux上是一个大小为1024的bit数组,大小由FD_SETSIZE决定
  fd_set all_set;

  //可以理解成对刚分配的all_set内存进行初始化操作
  //这一步是必须的,否则可能出现内存问题
  FD_ZERO(&all_set);
  //将对应的文件描述符,加入到set中
  FD_SET(listenFd, &all_set);

  int maxFd = listenFd;

  while (1) {
    
    
    fd_set r_set = all_set;
    //第一个参数是监听的三个文件描述符集合的最大文件描述符 +1
    //第二个参数是一个传入传出参数:传入:要监听读取事件的文件描述符表,传出:有读取事件发生的文件描述符
    //其他的分别是写入事件,异常事件,没有的话可以传空
    //最后一个参数:表示阻塞等待的时间,如果为NULL表示一直阻塞,他是一个指针类型
    //返回值表示:发生事件的全部文件描述符个数
    int selectNum = select(maxFd + 1, &r_set, nullptr, nullptr, NULL);
    if (selectNum < 0) {
    
    
      perror("select error\n");
      exit(-1);
    }

    //判断是否是监听socket上有读事件,如果是,则应该调用accept Api
    if (FD_ISSET(listenFd, &r_set)) {
    
    
      sockaddr_in clientAddr;
      bzero(&clientAddr, sizeof(clientAddr));
      socklen_t clientLen = sizeof(clientAddr);
      int connFd = accept(listenFd, (struct sockaddr *)&addr, &clientLen);
      if (connFd < 0) {
    
    
        perror("accept error\n");
        exit(0);
      }
      //将新接入联通的文件描述符加入到监听观察集合中
      FD_SET(connFd, &all_set);
      //检测更新最大文件描述符
      if (maxFd < connFd) {
    
    
        maxFd = connFd;
      }
      //表示只有一个文件描述符上有事件发生,并且这个事件赢被处理,因此不需要在后续的处理
      if (selectNum == 1) {
    
    
        continue;
      }
    }

    //客户端连接文件描述符上存在事件发生,则循环遍历所以监听的文件描述符
    for (int connFd = listenFd + 1; connFd < maxFd + 1; ++connFd) {
    
    
      if (FD_ISSET(connFd, &r_set)) {
    
    
        int readNum = read(connFd, (void *)buffer, sizeof(buffer));
        if (readNum < 0) {
    
    
          perror("read_bytes error.");
          exit(-1);
        }

        //读取的数为0时,表示对端socket关闭了
        if (readNum == 0) {
    
    
          //这里有个问题,当connFd关闭时,如果是当前最大文件描述符,则没有更新最大文件描述符
          close(connFd);
          //将文件描述符从set中移除
          FD_CLR(connFd, &all_set);
          printf("close client \n");
          break;
        }

        //处理数据
        printf("server receive: %s\n",buffer);
        for (int i = 0; i < readNum; ++i) {
    
    
          buffer[i] = toupper(buffer[i]);
        }

        //结果会写给对端
        int writeNum = write(connFd, buffer, readNum);
        if (writeNum < 0) {
    
    
          perror("write error");
          exit(-1);
        } else {
    
    
          printf("server response success.\n");
        }
      }
    }
  }
  close(listenFd);
  return 0;
}

猜你喜欢

转载自blog.csdn.net/xiwenhec/article/details/129391926