socket编程 -- epoll模型服务端/客户端通信的实现

心已赠人 2022-08-20 08:18 354阅读 0赞

本例实现如下功能:
支持多客户端与一个服务端进行通信,客户端给服务端发送字符串数据,服务端将字符串中小写转为大写后发送回客户端,客户端打印输出经转换后的字符串。
例如:发送abcde,打印输出ABCDE
服务端源码如下:

  1. /*server.c*/
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <netinet/in.h>
  6. #include <arpa/inet.h>
  7. #include <sys/epoll.h>
  8. #include <errno.h>
  9. #define BUFSIZE 666
  10. #define SERV_PORT 8000
  11. #define OPEN_MAX 1024
  12. int main()
  13. {
  14. int i, j, maxi, listenfd, connfd, sockfd;
  15. int nready, efd, res;
  16. ssize_t n;
  17. char buf[BUFSIZE], str[INET_ADDRSTRLEN];
  18. socklen_t clilen;
  19. int client[OPEN_MAX];
  20. struct sockaddr_in cliaddr, servaddr;
  21. struct epoll_event tep, ep[OPEN_MAX];//监听事件
  22. /*分配一个网络通信套接字,监听文件描述符listenfd*/
  23. listenfd = socket(AF_INET, SOCK_STREAM, 0);
  24. /*初始化 IP类型 端口*/
  25. bzero(&servaddr, sizeof(servaddr));
  26. servaddr.sin_family = AF_INET;
  27. servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
  28. servaddr.sin_port = htons(SERV_PORT);
  29. /*将listenfd绑定服务端地址*/
  30. bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
  31. /*监听请求*/
  32. listen(listenfd, 20);
  33. /*将客户端标识初始化为-1*/
  34. for(i = 0; i < OPEN_MAX; i++){
  35. client[i] = -1;
  36. }
  37. maxi = -1;
  38. /*告诉内核要监听的文件描述符个数 OPEN_MAX = 1024*/
  39. efd = epoll_create(OPEN_MAX);
  40. if(efd == -1){
  41. perror("epoll_create");
  42. }
  43. tep.events = EPOLLIN;/*监听文件描述符的可读事件*/
  44. tep.data.fd = listenfd;/*设置为监听的文件描述符*/
  45. /*控制epoll监控的文件描述符上的事件*/
  46. res = epoll_ctl(efd, EPOLL_CTL_ADD/*注册新的fd到efd*/, listenfd, &tep);
  47. if(res == -1)
  48. perror("epoll_ctl");
  49. for(;;){
  50. /*等待所监控文件描述符上有事件的产生,阻塞监听*/
  51. nready = epoll_wait(efd, ep, OPEN_MAX, -1);
  52. if(nready == -1)
  53. perror("epoll_wait");
  54. for(i = 0; i < nready; i++){
  55. if(!(ep[i].events & EPOLLIN))/*若不是EPOLLIN事件,不做往下的处理*/
  56. continue;
  57. if(ep[i].data.fd == listenfd){ /*若是EPOLLIN,执行连接,接受请求*/
  58. clilen = sizeof(cliaddr);
  59. /*接受请求,分配新文件描述符connfd进行通信*/
  60. connfd = accept(listenfd, (struct sockaddr*)&cliaddr, &clilen);
  61. printf("received from %s at PORT %d\n", (char*)inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)), ntohs(cliaddr.sin_port));
  62. /*若将此新客户端添加至客户端集中*/
  63. for(j = 0; j < OPEN_MAX; j++)
  64. if(client[j] < 0){
  65. client[j] = connfd;
  66. break;
  67. }
  68. if(j == OPEN_MAX)
  69. perror("客户端超过限制");
  70. if(j > maxi)
  71. maxi = j;//保证maxi为最大文件描述符
  72. tep.events = EPOLLIN;
  73. tep.data.fd = connfd;
  74. res = epoll_ctl(efd, EPOLL_CTL_ADD/*注册新的connfd到efd*/, connfd, &tep);
  75. if(res == -1)
  76. perror("epoll_ctl");
  77. }else{
  78. /*处理efd中监听的客户端请求*/
  79. sockfd = ep[i].data.fd;
  80. n = read(sockfd, buf, BUFSIZE);
  81. if(n == 0){ /*读取若为空*/
  82. for(j = 0; j <= maxi; j++){
  83. if(client[j] == sockfd){
  84. client[j] = -1;
  85. break;
  86. }
  87. }
  88. /*清除对sockfd文件描述符事件的监听*/
  89. res = epoll_ctl(efd, EPOLL_CTL_DEL, sockfd, NULL);
  90. if(res == -1)
  91. perror("epoll_ctl");
  92. close(sockfd);
  93. printf("client[%d] closed connection\n", j);
  94. }else{
  95. /*非空则处理客户端信息*/
  96. for(j = 0; j<n; j++)
  97. buf[j] = toupper(buf[j]);
  98. /*写入与客户端通信的文件描述符sockfd*/
  99. write(sockfd, buf, n);
  100. }
  101. }
  102. }
  103. }
  104. /*关闭监听*/
  105. close(listenfd);
  106. close(efd);
  107. return 0;
  108. }

客户端源码如下:

  1. /*client.c*/
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <unistd.h>
  6. #include <netinet/in.h>
  7. #include <arpa/inet.h>
  8. #include <sys/epoll.h>
  9. #include <errno.h>
  10. #define BUFSIZE 666
  11. #define SERV_PORT 8000
  12. #define OPEN_MAX 1024
  13. int main(int argc, char *argv[])
  14. {
  15. struct sockaddr_in servaddr;
  16. char buf[BUFSIZE];
  17. int sockfd, n;
  18. sockfd = socket(AF_INET, SOCK_STREAM, 0);
  19. bzero(&servaddr, sizeof(servaddr));
  20. servaddr.sin_family = AF_INET;
  21. inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
  22. servaddr.sin_port = htons(SERV_PORT);
  23. /*连接服务端*/
  24. connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
  25. while(fgets(buf, BUFSIZE, stdin) != NULL){
  26. /*通过sockfd给服务端发送数据*/
  27. write(sockfd, buf, strlen(buf));
  28. n = read(sockfd, buf, BUFSIZE);
  29. if(n == 0)
  30. printf("the other side has been closed.\n");
  31. else/*打印输出服务端传过来的数据*/
  32. write(STDOUT_FILENO, buf, n);
  33. }
  34. close(sockfd);
  35. return 0;
  36. }

编译及执行

在终端1先执行:

  1. yu@ubuntu:~/Linux/217/epoll$ ls
  2. client.c server.c
  3. yu@ubuntu:~/Linux/217/epoll$ gcc -o client client.c
  4. yu@ubuntu:~/Linux/217/epoll$ gcc -o server server.c
  5. yu@ubuntu:~/Linux/217/epoll$ ./server

另开一终端2:

  1. yu@ubuntu:~/Linux/217/epoll$ ./client
  2. hey how are you
  3. HEY HOW ARE YOU
  4. bye
  5. BYE

再另开一终端3:

  1. yu@ubuntu:~/Linux/217/epoll$ ./client
  2. i am here 007
  3. I AM HERE 007
  4. hehe
  5. HEHE

最后开一终端4:

  1. yu@ubuntu:~/Linux/217/epoll$ ./client
  2. i am 008
  3. I AM 008
  4. zai jian
  5. ZAI JIAN

关闭3个客户端(CTR+C)后服务端窗口:

  1. yu@ubuntu:~/Linux/217/epoll$ ./server
  2. received from 127.0.0.1 at PORT 58385
  3. received from 127.0.0.1 at PORT 58386
  4. received from 127.0.0.1 at PORT 58387
  5. client[2] closed connection
  6. client[1] closed connection
  7. client[0] closed connection

这里写图片描述

发表评论

表情:
评论列表 (有 0 条评论,354人围观)

还没有评论,来说两句吧...

相关阅读