服务器和客户端通过bind、listen、accept、select\poll\epoll建立连接的流程

服务器端流程

  1. 创建套接字

    int server_fd = socket(AF_INET, SOCK_STREAM, 0);
    
  2. 绑定套接字到特定IP地址和端口

    struct sockaddr_in address;
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY; // 绑定到所有可用的接口
    address.sin_port = htons(PORT); // 绑定到特定端口
    bind(server_fd, (struct sockaddr *)&address, sizeof(address));
    
  3. 开始监听传入的连接请求

    listen(server_fd, SOMAXCONN);
    
  4. 初始化select/poll/epoll

    • select:
      fd_set readfds;
      FD_ZERO(&readfds);
      FD_SET(server_fd, &readfds);
      int max_sd = server_fd;
      
    • poll:
      struct pollfd fds[1];
      fds[0].fd = server_fd;
      fds[0].events = POLLIN;
      
    • epoll:
      int epoll_fd = epoll_create1(0);
      struct epoll_event event;
      event.events = EPOLLIN;
      event.data.fd = server_fd;
      epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &event);
      
  5. 等待连接请求并处理

    • select:
      int activity = select(max_sd + 1, &readfds, NULL, NULL, NULL);
      if (FD_ISSET(server_fd, &readfds)) {
          int new_socket = accept(server_fd, (struct sockaddr *)&address, &addrlen);
          // 处理新连接
      }
      
    • poll:
      int activity = poll(fds, 1, -1);
      if (fds[0].revents & POLLIN) {
          int new_socket = accept(server_fd, (struct sockaddr *)&address, &addrlen);
          // 处理新连接
      }
      
    • epoll:
      struct epoll_event events[MAX_EVENTS];
      int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
      for (int i = 0; i < nfds; i++) {
          if (events[i].data.fd == server_fd) {
              int new_socket = accept(server_fd, (struct sockaddr *)&address, &addrlen);
              // 处理新连接
          }
      }
      

客户端流程

  1. 创建套接字

    int client_fd = socket(AF_INET, SOCK_STREAM, 0);
    
  2. 设置服务器地址和端口

    struct sockaddr_in server_address;
    server_address.sin_family = AF_INET;
    server_address.sin_port = htons(PORT);
    inet_pton(AF_INET, "server_ip_address", &server_address.sin_addr);
    
  3. 连接到服务器

    connect(client_fd, (struct sockaddr *)&server_address, sizeof(server_address));
    

总结

  1. 服务器端

    • 创建监听套接字。
    • 通过bind绑定监听套接字到特定IP地址和端口。
    • 通过listen开始监听传入的连接请求。
    • 使用selectpollepoll等待并处理连接请求。
    • 当有新的连接请求时,调用accept函数创建一个新的已连接套接字用于数据传输。
      • 新的套接字和监听套接字的ip和端口相同
  2. 客户端

    • 创建套接字。
    • 设置服务器的IP地址和端口。
    • 通过connect连接到服务器。

通过上述步骤,服务器和客户端可以成功建立TCP连接,并通过新的已连接套接字进行数据传输。

  • 11
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Unix网络编程中,selectpollepoll都是用于多路复用的I/O模型,可以同时监视多个文件描述符的可读、可写和异常事件。它们之间的主要区别如下: 1. select的文件描述符集合是使用数组来表示,而poll使用链表,epoll使用红黑树,因此在文件描述符数量较大的情况下,selectpoll的效率会随着文件描述符数量的增加而降低,而epoll则不会有太大的性能影响。 2. selectpoll每次调用时都需要将文件描述符集合从用户态拷贝到内核态,而epoll只需要在第一次调用时将文件描述符集合拷贝到内核态,之后只需要在需要修改时再次拷贝,因此epoll的性能更优。 3. selectpoll对于同一个文件描述符,如果既可以读也可以写,需要分别在读集合和写集合中进行标记,而epoll则可以在注册时指定感兴趣的事件类型,不需要分别标记。 下面是一个使用selectpollepoll进行通信的demo,该程序可以监听本地端口,并接受客户端连接请求,然后将客户端发送的数据原样返回。 ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/select.h> #include <sys/poll.h> #include <sys/epoll.h> #define PORT 8888 #define MAX_EVENTS 10 #define BUF_SIZE 1024 int main(int argc, char *argv[]) { int listen_fd, conn_fd; struct sockaddr_in serv_addr, cli_addr; socklen_t cli_len; char buf[BUF_SIZE]; int i, n; // 创建监听socket listen_fd = socket(AF_INET, SOCK_STREAM, 0); if (listen_fd < 0) { perror("socket"); exit(EXIT_FAILURE); } // 绑定地址和端口 memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); serv_addr.sin_port = htons(PORT); if (bind(listen_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { perror("bind"); exit(EXIT_FAILURE); } // 监听端口 if (listen(listen_fd, 10) < 0) { perror("listen"); exit(EXIT_FAILURE); } // 使用select进行通信 // fd_set read_fds; // FD_ZERO(&read_fds); // FD_SET(listen_fd, &read_fds); // while (1) { // fd_set tmp_fds = read_fds; // if (select(FD_SETSIZE, &tmp_fds, NULL, NULL, NULL) < 0) { // perror("select"); // exit(EXIT_FAILURE); // } // for (i = 0; i < FD_SETSIZE; i++) { // if (FD_ISSET(i, &tmp_fds)) { // if (i == listen_fd) { // // 有新连接 // cli_len = sizeof(cli_addr); // conn_fd = accept(listen_fd, (struct sockaddr *)&cli_addr, &cli_len); // if (conn_fd < 0) { // perror("accept"); // exit(EXIT_FAILURE); // } // printf("New connection from %s:%d\n", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port)); // FD_SET(conn_fd, &read_fds); // } else { // // 有数据可读 // n = read(i, buf, BUF_SIZE); // if (n <= 0) { // // 连接关闭 // close(i); // FD_CLR(i, &read_fds); // printf("Connection closed\n"); // } else { // // 发送数据 // write(i, buf, n); // } // } // } // } // } // 使用poll进行通信 // struct pollfd fds[MAX_EVENTS]; // fds[0].fd = listen_fd; // fds[0].events = POLLIN; // while (1) { // if (poll(fds, MAX_EVENTS, -1) < 0) { // perror("poll"); // exit(EXIT_FAILURE); // } // for (i = 0; i < MAX_EVENTS; i++) { // if (fds[i].revents & POLLIN) { // if (i == 0) { // // 有新连接 // cli_len = sizeof(cli_addr); // conn_fd = accept(listen_fd, (struct sockaddr *)&cli_addr, &cli_len); // if (conn_fd < 0) { // perror("accept"); // exit(EXIT_FAILURE); // } // printf("New connection from %s:%d\n", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port)); // fds[i].fd = conn_fd; // fds[i].events = POLLIN; // } else { // // 有数据可读 // n = read(fds[i].fd, buf, BUF_SIZE); // if (n <= 0) { // // 连接关闭 // close(fds[i].fd); // printf("Connection closed\n"); // fds[i].fd = -1; // } else { // // 发送数据 // write(fds[i].fd, buf, n); // } // } // } // } // } // 使用epoll进行通信 int epfd, nfds; struct epoll_event ev, events[MAX_EVENTS]; epfd = epoll_create(MAX_EVENTS); ev.events = EPOLLIN; ev.data.fd = listen_fd; epoll_ctl(epfd, EPOLL_CTL_ADD, listen_fd, &ev); while (1) { nfds = epoll_wait(epfd, events, MAX_EVENTS, -1); for (i = 0; i < nfds; i++) { if (events[i].data.fd == listen_fd) { // 有新连接 cli_len = sizeof(cli_addr); conn_fd = accept(listen_fd, (struct sockaddr *)&cli_addr, &cli_len); if (conn_fd < 0) { perror("accept"); exit(EXIT_FAILURE); } printf("New connection from %s:%d\n", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port)); ev.events = EPOLLIN; ev.data.fd = conn_fd; epoll_ctl(epfd, EPOLL_CTL_ADD, conn_fd, &ev); } else { // 有数据可读 n = read(events[i].data.fd, buf, BUF_SIZE); if (n <= 0) { // 连接关闭 close(events[i].data.fd); printf("Connection closed\n"); epoll_ctl(epfd, EPOLL_CTL_DEL, events[i].data.fd, NULL); } else { // 发送数据 write(events[i].data.fd, buf, n); } } } } return 0; } ``` 其中,使用注释掉的代码分别实现了使用selectpollepoll进行通信的功能。可以通过注释掉相应的代码来切换使用不同的I/O模型。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值