上一篇博客用多线程实现服务端和多个客户端的通信,但是在实际应用中如果服务端有高并发的需求,多线程并不是一个好选择。

实现高并发的一种方法是IO多路复用,也就是select,poll,epoll等等。

于是我采用epoll再修改了服务端,实现单线程服务多个客户端。

服务端:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <fcntl.h> // open function
  4. #include <unistd.h> // close function
  5. #include <sys/socket.h>
  6. #include <netinet/in.h>
  7. #include <string.h>
  8. #include <pthread.h>
  9. #include <errno.h>
  10. #include <sys/epoll.h>
  11.  
  12. const int PORT = ;
  13. /*
  14. listen_loop(): epoll监听套接字,作不同处理
  15. accept_conn(): 新的客户端连接进来,执行accept,将fd加入epoll set
  16. recv_message(): recv并且重复输出一份给客户端
  17. */
  18. void listen_loop();
  19. void accept_conn(unsigned int sock_fd, unsigned int epollfd);
  20. void recv_message(unsigned int sock_fd);
  21.  
  22. int main(void) {
  23. int sock_fd;
  24. struct sockaddr_in server_addr;
  25.  
  26. //初始化socket
  27. sock_fd = socket(AF_INET, SOCK_STREAM, );
  28. if (sock_fd < ) {
  29. perror("socket:");
  30. return ;
  31. }
  32.  
  33. //编辑地址
  34. memset(&server_addr, , sizeof(server_addr));
  35. server_addr.sin_family = AF_INET;//ipv_4
  36. server_addr.sin_port = htons(PORT);//监听端口8888
  37. server_addr.sin_addr.s_addr = INADDR_ANY;//本地的任意地址
  38.  
  39. //绑定然后监听
  40. if (bind(sock_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < ) {
  41. perror("bind:");
  42. return ;
  43. }
  44. if (listen(sock_fd, ) < ) {
  45. perror("listen");
  46. return ;
  47. }
  48.  
  49. listen_loop(sock_fd);
  50.  
  51. return ;
  52. }
  53. void accept_conn(unsigned int sock_fd, unsigned int epollfd) {
  54. struct sockaddr_in clientaddr;
  55. struct epoll_event event;
  56. socklen_t len = sizeof(struct sockaddr);
  57. int accept_fd = ;
  58.  
  59. accept_fd = accept(sock_fd, (struct sockaddr*)&clientaddr, &len);
  60.  
  61. if (accept_fd <= ) {
  62. perror("accept error");
  63. return;
  64. }
  65.  
  66. //将新建连接加入epoll set
  67. event.data.fd = accept_fd;
  68. event.events = EPOLLIN | EPOLLET | EPOLLRDHUP;
  69. epoll_ctl(epollfd, EPOLL_CTL_ADD, accept_fd, &event);
  70. return;
  71. }
  72.  
  73. void recv_message(unsigned int sock_fd) {
  74. char recv_buf[], send_buf[];
  75.  
  76. memset(recv_buf, , sizeof(recv_buf));
  77. memset(send_buf, , sizeof(send_buf));
  78.  
  79. recv(sock_fd, recv_buf, sizeof(recv_buf), );
  80. fputs(recv_buf, stdout);
  81. strcpy(send_buf, recv_buf);
  82. send(sock_fd, send_buf, sizeof(send_buf), );
  83.  
  84. return;
  85. }
  86. void listen_loop(unsigned int sock_fd)
  87. {
  88. int epollfd, i, ret;
  89. int timeout = ;
  90. struct epoll_event event;
  91. struct epoll_event eventList[];
  92.  
  93. /*创建epoll监听事件*/
  94. epollfd = epoll_create();
  95. event.events = EPOLLIN | EPOLLET;
  96. event.data.fd = sock_fd;
  97.  
  98. /*注册epoll监听事件.*/
  99. if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sock_fd, &event) < ) {
  100. printf("register epoll event err !");
  101. return;
  102. }
  103.  
  104. while () {
  105. ret = epoll_wait(epollfd, eventList, , timeout);
  106.  
  107. /*epoll事件错误.*/
  108. if (ret < ) {
  109. printf("epoll event err!");
  110. break;
  111. }
  112. /*无事件返回.*/
  113. else if (ret == ) {
  114. continue;
  115. }
  116.  
  117. /*epoll返回事件.*/
  118. for (i = ; i < ret; i++) {
  119. /*epoll 错误*/
  120. if ((eventList[i].events & EPOLLERR) || (eventList[i].events & EPOLLHUP) || !(eventList[i].events & EPOLLIN)) {
  121. printf("epoll error\n");
  122. close(eventList[i].data.fd);
  123. exit(-);
  124. }
  125.  
  126. //half connection
  127. if (eventList[i].events & EPOLLRDHUP) {
  128. printf("//one client close the conne.//\n");
  129. close(eventList[i].data.fd);
  130. }
  131.  
  132. /*accept事件*/
  133. if (eventList[i].data.fd == sock_fd) {
  134. accept_conn(sock_fd, epollfd);
  135. }
  136. /*非sock_fd则为其他事件.*/
  137. else {
  138. recv_message(eventList[i].data.fd);
  139. }
  140. }
  141. }
  142. close(epollfd);
  143. close(sock_fd);
  144. return;
  145. }

Linux下socket通信和epoll的更多相关文章

  1. Linux 下socket通信终极指南(附TCP、UDP完整代码)

    linux下用socket通信,有TCP.UDP两种协议,网上的很多教程把两个混在了一起,或者只讲其中一种.现在我把自己这两天研究的成果汇总下来,写了一个完整的,适合初学者参考,也方便自己以后查阅. ...

  2. Linux下socket通信和多线程

    服务端socket流程:socket() –> bind() –> listen() –> accept() –> 读取.发送信息(recv,send等) 客户端socket流 ...

  3. (8)Linux(客户端)和Windows(服务端)下socket通信实例

    Linux(客户端)和Windows(服务端)下socket通信实例: (1)首先是Windows做客户端,Linux做服务端的程序 Windows   Client端 #include <st ...

  4. Linux下进程通信的八种方法

    Linux下进程通信的八种方法:管道(pipe),命名管道(FIFO),内存映射(mapped memeory),消息队列(message queue),共享内存(shared memory),信号量 ...

  5. linux下socket编程实例

    linux下socket编程实例一.基本socket函数Linux系统是通过提供套接字(socket)来进行网络编程的.网络的socket数据传输是一种特殊的I/O,socket也是一种文件描述符.s ...

  6. (转)Linux下select, poll和epoll IO模型的详解

    Linux下select, poll和epoll IO模型的详解 原文:http://blog.csdn.net/tianmohust/article/details/6677985 一).Epoll ...

  7. Linux下Socket编程的端口问题( Bind error: Address already in use )

    Linux下Socket编程的端口问题( Bind error: Address already in use ) 在进行linux网络编程时,每次修改了源代码并再次编译运行时,常遇到下面的地使用错误 ...

  8. linux下串口通信与管理

    linux下的串口与windows有一些区别,下面将介绍一下linux下串口通信管理 查看是否支持USB串口: #lsmod | grep usbserial 如果没有信息:sudo apt-get ...

  9. UNIX下socket通信 - UDP通信

    一.UNIX下socket通信: socket套接字是一种可以进行网络通信的内核对象,它是一个唯一的标示符,一般称它为socket描述符. 注意:UDP通信需要客户端先发送消息,服务端先进行等待客户端 ...

随机推荐

  1. super函数的用法

    1.创建一个类. # .创建一个类 class Bird: def __init__(self): self.hungry =True def eat(self): if self.hungry: p ...

  2. Day 18 正则表达式.

    一.字符 .匹配除换行符以外的任意字符. \w 匹配字母数字或者下划线. \s 匹配任意的空白符 \d 匹配数字 \n 匹配一个换行符 \t 匹配一个制表符 ^ 匹配字符串的开始. $ 匹配字符串的结 ...

  3. 前端入门CSS(2)

    参考: https://www.cnblogs.com/liwenzhou/p/7999532.html 背景属性 /*背景颜色*/background-color: red; /*背景图片*/ ba ...

  4. biz_platform项目过程

    1.前台界面主要采用React框架.通过Ajax方式将数据与tornado服务器交互.以下代码为请求后台数据. var ThisPage = React.createClass({ render: f ...

  5. Spring MVC+MySQL保存中文变成乱码

    环境:MySQL,Spring MVC3.2.0,jQuery v2.0.3,使用JdbcTemplate访问数据库,相当于全套Spring解决方案. 现象 直接使用表单POST,或者使用jQuery ...

  6. AQS源码泛读,梳理设计流程(jdk8)

    一.AQS介绍 AQS(AbstractQueuedSynchronizer)抽象队列同步器,属于多线程编程的基本工具:JDK对其定义得很详细,并提供了多种常用的工具类(重入锁,读写锁,信号量,Cyc ...

  7. c malloc分配内存

    php中的内存分配有用类似emalloc这样的函数,emalloc实际上是C语言中的malloc的一层封装,php启动后,会向OS申请一块内存,可以理解为内存池,以后的php分配内存都是在这块内存池中 ...

  8. 冒泡排序实现(Java)

    冒泡排序是一种交换排序,它的基本思路是: 两两比较相邻记录的关键字,如果反序则交换,知道没有反序的记录位置. 依次比较相邻的两个数,将小数放在前面,大数放在后面.即在第一趟:首先比较第1个和第2个数, ...

  9. org.apache.ibatis.executor.loader.javassist.JavassistProxyFactory$EnhancedResultObjectProxyImpl and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.

    当我用Springboot和mybatis进行延迟加载时候报出如下的错误: org.apache.ibatis.executor.loader.javassist.JavassistProxyFact ...

  10. Android4.0 Launcher 源码分析1——Launcher整体结构

    1.Launcher整体结构 桌面程序其实并不包含桌面壁纸,桌面壁纸其实是由 WallpaperManagerService来提供,整个桌面其实是叠加在整个桌面壁纸上的另外一个层. 1.1 WorkS ...