Linux网络编程:TCP服务器(单进程多用户),使用select方法实现

【Linux网络编程】TCP并发服务器的实现(IO多路复用select) TCP的服务器默认的就是一个循环服务器,原因是有两个阻塞 accept函数 和 recv函数之间会相互影响。本文是TCP并发服务器、客户端的实现以及实例的详细解释。使用了IO多路复用select函数进行TCP并发服务器的构建。希望博客对您有所帮助。 ......... 阅读详情

Linux下的单进程多用户TCP服务器,采用select方法实现。

  1. /************************************************* 
  2. * File name   : server.c 
  3. * Description : 单进程并发服务器 
  4. * Author      : sg131971@qq.com 
  5. * Version     : V1.0 
  6. * Date        :  
  7. * Compiler    : arm-linux-gcc-4.4.3 
  8. * Target      : mini2440(Linux-2.6.32) 
  9. * History     :  
  10. *   <author>  <time>   <version >   <desc> 
  11. *************************************************/  
  12. #include <stdio.h>  
  13. #include <string.h>  
  14. #include <unistd.h>  
  15. #include <sys/types.h>  
  16. #include <sys/socket.h>  
  17. #include <netinet/in.h>  
  18. #include <arpa/inet.h>  
  19. #include <sys/time.h>  
  20. #include <stdlib.h>  
  21.   
  22. #define PORT 1234               //服务器端口  
  23. #define BACKLOG 5               //listen队列中等待的连接数  
  24. #define MAXDATASIZE 1024        //缓冲区大小  
  25.   
  26. typedef struct _CLIENT  
  27. {  
  28.     int fd;                     //客户端socket描述符  
  29.     char name[20];              //客户端名称  
  30.     struct sockaddr_in addr;    //客户端地址信息结构体  
  31.     char data[MAXDATASIZE];     //客户端私有数据指针  
  32. } CLIENT;  
  33.   
  34. void process_client(CLIENT * client, char *recvbuf, int len);   //客户请求处理函数  
  35.   
  36. /************************************************* 
  37. * Function    : main() 
  38. * Description :  
  39. * Calls       : process_client() 
  40. * Called By   :  
  41. * Input       :  
  42. * Output      :  
  43. * Return      :  
  44. *************************************************/  
  45. void main(int argc ,char **argv)  
  46. {  
  47.     int i, maxi, maxfd, sockfd;  
  48.     int nready;  
  49.     ssize_t n;  
  50.     fd_set rset, allset;        //select所需的文件描述符集合  
  51.     int listenfd, connectfd;    //socket文件描述符  
  52.     struct sockaddr_in server;  //服务器地址信息结构体  
  53.   
  54.     CLIENT client[FD_SETSIZE];  //FD_SETSIZE为select函数支持的最大描述符个数  
  55.     char recvbuf[MAXDATASIZE];  //缓冲区  
  56.     int sin_size;               //地址信息结构体大小  
  57.   
  58.     if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)  
  59.     {                           //调用socket创建用于监听客户端的socket  
  60.         perror("Creating socket failed.");  
  61.         exit(1);  
  62.     }  
  63.   
  64.     int opt = SO_REUSEADDR;  
  65.     setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));  //设置socket属性  
  66.   
  67.     bzero(&server, sizeof(server));  
  68.     server.sin_family = AF_INET;  
  69.     server.sin_port = htons(PORT);  
  70.     server.sin_addr.s_addr = htonl(INADDR_ANY);  
  71.   
  72.     if (bind(listenfd, (struct sockaddr *)&server, sizeof(struct sockaddr)) == -1)  
  73.     {                           //调用bind绑定地址  
  74.         perror("Bind error.");  
  75.         exit(1);  
  76.     }  
  77.   
  78.     if (listen(listenfd, BACKLOG) == -1)  
  79.     {                           //调用listen开始监听  
  80.         perror("listen() error\n");  
  81.         exit(1);  
  82.     }  
  83.   
  84.     //初始化select  
  85.     maxfd = listenfd;  
  86.     maxi = -1;  
  87.     for (i = 0; i < FD_SETSIZE; i++)  
  88.     {  
  89.         client[i].fd = -1;  
  90.     }  
  91.     FD_ZERO(&allset);           //清空  
  92.     FD_SET(listenfd, &allset);  //将监听socket加入select检测的描述符集合  
  93.   
  94.     while (1)  
  95.     {  
  96.         struct sockaddr_in addr;  
  97.         rset = allset;  
  98.         nready = select(maxfd + 1, &rset, NULL, NULL, NULL);    //调用select  
  99.         printf("Select() break and the return num is %d. \n", nready);  
  100.   
  101.         if (FD_ISSET(listenfd, &rset))  
  102.         {                       //检测是否有新客户端请求  
  103.             printf("Accept a connection.\n");  
  104.             //调用accept,返回服务器与客户端连接的socket描述符  
  105.             sin_size = sizeof(struct sockaddr_in);  
  106.             if ((connectfd =  
  107.                  accept(listenfd, (struct sockaddr *)&addr, (socklen_t *) & sin_size)) == -1)  
  108.             {  
  109.                 perror("Accept() error\n");  
  110.                 continue;  
  111.             }  
  112.   
  113.             //将新客户端的加入数组  
  114.             for (i = 0; i < FD_SETSIZE; i++)  
  115.             {  
  116.                 if (client[i].fd < 0)  
  117.                 {  
  118.                     char buffer[20];  
  119.                     client[i].fd = connectfd;   //保存客户端描述符  
  120.                     memset(buffer, '0', sizeof(buffer));  
  121.                     sprintf(buffer, "Client[%.2d]", i);  
  122.                     memcpy(client[i].name, buffer, strlen(buffer));  
  123.                     client[i].addr = addr;  
  124.                     memset(buffer, '0', sizeof(buffer));  
  125.                     sprintf(buffer, "Only For Test!");  
  126.                     memcpy(client[i].data, buffer, strlen(buffer));  
  127.                     printf("You got a connection from %s:%d.\n", inet_ntoa(client[i].addr.sin_addr),ntohs(client[i].addr.sin_port));  
  128.                     printf("Add a new connection:%s\n",client[i].name);  
  129.                     break;  
  130.                 }  
  131.             }  
  132.               
  133.             if (i == FD_SETSIZE)  
  134.                 printf("Too many clients\n");  
  135.             FD_SET(connectfd, &allset); //将新socket连接放入select监听集合  
  136.             if (connectfd > maxfd)  
  137.                 maxfd = connectfd;  //确认maxfd是最大描述符  
  138.             if (i > maxi)       //数组最大元素值  
  139.                 maxi = i;  
  140.             if (--nready <= 0)  
  141.                 continue;       //如果没有其它I/O描述符读或写准备就绪,只有listenfd就绪,继续循环  
  142.         }  
  143.   
  144.         for (i = 0; i <= maxi; i++)  
  145.         {  
  146.             if ((sockfd = client[i].fd) < 0)    //如果客户端描述符小于0,则没有客户端连接,检测下一个  
  147.                 continue;  
  148.             // 有客户连接,检测是否有数据  
  149.             if (FD_ISSET(sockfd, &rset))  
  150.             {  
  151.                 printf("Receive from connect fd[%d].\n", i);  
  152.                 if ((n = recv(sockfd, recvbuf, MAXDATASIZE, 0)) == 0)  
  153.                 {               //从客户端socket读数据,等于0表示网络中断  
  154.                     close(sockfd);  //关闭socket连接  
  155.                     printf("%s closed. User's data: %s\n", client[i].name, client[i].data);  
  156.                     FD_CLR(sockfd, &allset);    //从监听集合中删除此socket连接  
  157.                     client[i].fd = -1;  //数组元素设初始值,表示没客户端连接  
  158.                 }  
  159.                 else  
  160.                     process_client(&client[i], recvbuf, n); //接收到客户数据,开始处理  
  161.                 if (--nready <= 0)  
  162.                     break;      //如果没有其它I/O描述符读或写准备就绪,跳出for循环回到while循环  
  163.             }  
  164.         }  
  165.     }  
  166.     close(listenfd);            //关闭服务器监听socket       
  167. }  
  168.   
  169. /************************************************* 
  170. * Function    : process_client() 
  171. * Description : 处理客户端连接函数 
  172. * Calls       :  
  173. * Called By   : main() 
  174. * Input       :  
  175. * Output      :  
  176. * Return      :  
  177. *************************************************/  
  178. void process_client(CLIENT * client, char *recvbuf, int len)  
  179. {  
  180.     char sendbuf[MAXDATASIZE];  
  181.     int i;  
  182.   
  183.     printf("Received client( %s ) message: %s\n", client->name, recvbuf);  
  184.      
  185.     for (i = 0; i < len - 1; i++)  
  186.     {  
  187.         sendbuf[i] = recvbuf[len - i - 2];  
  188.     }  
  189.       
  190.     sendbuf[len - 1] = '\0';  
  191.   
  192.     send(client->fd, sendbuf, strlen(sendbuf), 0);  
  193. }  


测试结果:

Linux网络编程之socket:使用select函数实现并发处理 一、当我们使用单进程单连接且使用readline修改后的客户端程序,去连接使用readline修改后的服务器端程序,会出现一个有趣的现象,先来看输出: 先运行服务器端,再运行客户端。 阅读详情

相关推荐

Linux 下使用select 实现TCP server和TCP client

基于TCP(面向链接)的socket编程 Server端的流程如下: 1 创建Socket套接字 2 将套接字绑定到一个本地地址(IP)和端口(port)上 3 将套接字设置为监听模式,准备接收客户端请求(listen) 4 等待客户端请求,当请求到来后,接受链接请求,返回一个新的对应于此次链接的套接字(accept) 5 用返回的套接字和客户端进行通信(send/recv ...

whuer_xiaojie的博客 3410

基于select函数的单进程并发服务器程序----TCP

下面是一个简单的时间服务器程序。 #include #include #include #include #include #include #include #include #include #define MAX_LISTEN 5 #define PORT 1234 i

曹绍坚(嵌入式学生) 978

大模型驱动时序量化因子自动优化:从WorldQuant回测到实战策略

本文探讨了如何利用大模型驱动时序量化因子的自动优化,构建从创意生成到实战验证的完整流程。通过结合WorldQuant回测平台,搭建自动化流水线,详细拆解了因子生成、回测验证与迭代优化的核心模块。文章分享了从初版实现到进阶优化的实践经验,并指出提示词工程与领域知识注入是关键,旨在实现人机智能协作,提升量化策略研发效率。

wdx012345的博客 802

使用select实现TCP并发服务器模型

本期主要分享的是对于select的使用,使用select实现TCP并发服务器模型,由于之前所用到的技术知识只能够支撑我们进行单个访问,但是有了select之后呢,我们就能够实现多用户进行访问;这也是非常符合客观需求的;

weixin_58016534的博客 2310

解决西门子Step 7 basic 找不到许可证问题

你在使用博图软件是否遇到过Step 7 basic 找不到许可证的问题呢!不言怕,今天我就来告诉你该如何处理吧!看步骤: 1.删除原授权的License: 打开Automation License Manager应用,然后我们可以发现仅有的License Key前面有一个时钟,说明它已经过期不可用,我们就要去寻求新的Key. 打开“我的电脑”,选择C盘,点击上方查看 点击选项–>查看 然...

让技术没有门槛 14万+

【分享】获取微信通讯录python代码形式实现

微信号”, “昵称”, “地区”, “备注”, “标签”, “签名”, “来源”5. 找到最后一个空格所在的位置(后一个就是真正的联系人了)当前微信号与上一个相同的时候,说明到底部了,可以跳出循环。3. 滚动鼠标到最顶部(防止已经滚动了一部分了)7.记录下上一个联系人的微信号。7. 无限循环按键盘下箭头。获取详情中的以下元素内容。6. 点击第一个联系人。4. 获取联系人列表。

程序员老狼专注开发aigc或客服系统应用 2892

【Linux网络编程】select函数实现TCP并发服务器

使用select函数来完成并发服务器

imysy_22_的博客 1691

风力发电机缺陷检测平台

风力发电机缺陷检测平台相关内容展示

weixin_39779415的博客 1110

基于TCP/IP的局域网多用户通信

引言 由于因特网的迅速流行,越来越多的应用程序具备了在网上与其它程序通信的能力。从WIN95开始微软把网络功能融进了它的操作系统,使得应用程序网络通信能力更为普及。因此,微软的TCP/IP协议也就成为网络应用程序基于的首选协议。 一般采用TCP/IP协议的应用程序只实现了单用户与服务器间点对点的连接,而本文在VC6.0的环境下,运用了了多线程以及共享数据结构技术,不仅实现了多用户与服务器间

chinabinlang的专栏 1万+

从零构建:如何用MATLAB打造你的第一个Stewart平台运动学仿真器

本文详细介绍了如何使用MATLAB从零构建一个六自由度Stewart平台运动学仿真器,涵盖基础理论、环境配置、运动学建模、动态轨迹规划和实时可视化实现。通过MATLAB的Robotics System Toolbox和Simulink等工具,读者可以掌握六自由度Stewart平台的逆运动学求解、正运动学求解及仿真系统集成,适用于飞行模拟器和精密定位等应用场景。

peach的博客 864

opencv-meshgrid

使用opencv::repeat函数和std::iota函数完成meshgrid功能。 X Y Z 通俗的说就是根据Z的大小生成X矩阵,Y矩阵

yuegooxi的博客 1607

C++网络编程:select IO多路复用及TCP服务器开发

在高并发网络编程中,高效处理大量连接至关重要。传统的阻塞式I/O模型已无法满足需求,而IO多路复用技术则提供了优雅的解决方案。本文将深入探讨C++网络编程中常用的IO多路复用技术——select,剖析其原理和使用方法,并通过一个完整的TCP服务器示例,演示如何利用select构建高性能的网络应用。

Lion_Long的博客 2686

计算机等级考试(二级WPS)---真题14套

目录 真题14一、选择题二、WPS文字三、WPS表格四、WPS演示21.打开考生文件夹下的素材文档 “WPS.docx”(.docx 为文件扩展名),后续操作均基于此文件,否则不得分。小周正在整理一份关于极光的文档,请按要求帮助她完成文档的排版工作。1.将全文中的手动换行符全部替换为中文句号并回车,删除文中的空行和空格。2.将正文第 2 段(“在我國的古書…… 極光”)中的繁体字转换成简体字,并为其设置 “正文” 样式。3.对文档样式进行如下设置:4.为 “1.3 极光的形态” 一节中列出的五种极光形态特征

2401_82750246的博客 1165

Linux网络编程,TCP 多用户的链接问题。select函数的简单运用

描述:关于tcp多用户的来链接问题: 首先需要了解select大致工作步骤:select是相当于一个等待函数,当参数中的集合里面的文件描述符有动作的时候,select函数会把有动作的文件描述符留下来,其余的全部清空掉,select的第三个参数,表示读 所以: 对于tcp的多用户的链接思路: server端:将server_fd加入集合中,如果server_fd有动作就说明 有客户端想要链接,就调用accept函数。accept函数会把想要 链接的客户端的client_fd 返回,将client_fd放入一个

weixin_44261839的博客 429

【UG\NX二次开发】多个带ui的dll怎么整合到一个dll

NX二次开发-群精选」专栏收录了那些曾散落在聊天对话框里的精彩的知识分享,这都是被遗落的宝贵财富。让我们一起向大佬学习,充满激情地进步,不断超越自己。底部联系方式,欢迎您加入一起交流。有大佬知道多个带ui的dll怎么整合到一个dll吗。

王牌飞行员_里海的博客 283

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

I/O 多路复用之 select:从原理到 TCP 服务器实现

本文详细解析了select I/O多路复用机制及其实现。select作为最早的I/O复用技术,通过监视多个文件描述符,解决了传统阻塞I/O的效率问题。文章首先分析了I/O复用的必要性,然后深入讲解select函数参数、fd_set操作及工作流程,并完整实现了一个基于select的TCP服务器。该服务器通过辅助数组管理连接,区分处理新连接和数据事件,展示了select的核心应用。最后,文章对比了select的优缺点:虽然跨平台且实现简单,但在高并发场景下存在性能限制,为后续理解epoll等高效机制奠定了基础。

2401_83603768的博客 1176

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

linux网络编程之socket(九):使用select函数改进客户端/服务器端程序

一、当我们使用单进程单连接且使用readline修改后的客户端程序,去连接使用readline修改后的服务器端程序,会出现一个有趣的现象,先来看输出: 先运行服务器端,再运行客户端, simba@ubuntu:~/Documents/code/linux_programming/UNP/socket$ ./echoser_recv_peek  recv connect ip=127.0.0.

Meditation 2万+

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

Socket网络编程:TCP与UDP

socket网络编程:TCP与UDP

Edward2022的博客 8907

【Linux网络】select函数

在Linux网络编程中,select 函数是一种非常有用的IO多路复用技术,它允许程序监视多个文件描述符(file descriptors),以等待一个或多个文件描述符变得“就绪”(ready),比如有数据可读、写操作可能不阻塞或者出现了异常条件。这对于提高网络服务器等需要同时处理多个网络连接的应用程序的效率至关重要。select()函数允许程序监视多个文件描述符,等待所监视的一个或者多个文件描述符变为“准备好”的状态。

2301_79181030的博客 2139

TCP/IP网络编程:TCP服务器的基础搭建以及使用io复用优化构建

基于io复用创建tcp服务器。select/poll/epoll入门

m0_73939403的博客 1745

【Linux网络】深入理解Linux IO多路复用:从本质到select服务器实战

在上一篇文章中,我们详细讲解了 Linux 下的五种 IO 模型,其中IO 多路复用是目前高并发服务器的基石。它用一个线程就能同时处理成百上千个连接,彻底解决了阻塞 IO"一个连接一个线程" 的性能瓶颈。而select,作为 Linux 系统上第一个实现 IO 多路复用的系统调用,虽然现在已经逐渐被更高效的epoll取代,但它是理解所有多路复用技术的基础。几乎所有操作系统都实现了select接口,掌握它不仅能帮你理解多路复用的核心思想,也是面试中绕不开的考点。本文将从最底层的多路转接本质讲起,带你彻底搞懂s

会当凌绝顶,一览众山小 6423

玩转Liunx系统select函数:单进程实现多路IO转接,打造高可用多客户端服务器

本文介绍了使用Linux系统的select函数实现单进程多路IO转接的方法,通过监听多个文件描述符的事件,打造高可用的多客户端服务器。文章首先解释了多路IO转接的概念和select函数的作用,然后详细剖析了select实现多客户端服务器的核心原理,包括文件描述符集合的操作和双集合设计技巧。通过Mermaid流程图直观展示了完整的实现流程,并提供了核心代码片段和关键注释。该方法避免了多进程/多线程的资源消耗,以轻量级方式实现并发处理,适合轻量级服务器开发。

软件科学-郝学胜 801
上一篇: 了解 Boost 单元测试框架
下一篇: Linux API和c运行时库等关系
nodeathphoenix
博客等级 码龄16年 97粉丝 99原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值