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

转载 2014年05月06日 11:36:03

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;       //如果没有新客户端连接,继续循环   
  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;      //如果没有新客户端有数据,跳出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

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

Linux下的单进程多用户TCP服务器,采用select方法实现。 [cpp] view plaincopy /**************************************...
  • nodeathphoenix
  • nodeathphoenix
  • 2013年08月28日 16:26
  • 1494

Linux网络编程——tcp并发服务器(多进程)

一、tcp并发服务器概述 一个好的服务器,一般都是并发服务器(同一时刻可以响应多个客户端的请求)。并发服务器设计技术一般有:多进程服务器、多线程服务器、I/O复用服务器等。 二、多...
  • lianghe_work
  • lianghe_work
  • 2015年06月15日 15:02
  • 4837

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

/************************************************* * File name   : server.c * Description : 单进程并发服务器...
  • cuiyifang
  • cuiyifang
  • 2013年03月06日 11:21
  • 679

TCP/IP网络编程 基于Linux编程_4 --多线程服务器端的实现

线程基本概念前面我们讲过多进程服务器,但我们知道它开销很大,因此我们才引入线程,我们可以把它看成是一种轻量级进程。它相比进程有如下几个优点: 线程的创建和上下文切换开销更小且速度更快。 线程间交换数据...
  • u010223072
  • u010223072
  • 2015年10月24日 16:46
  • 1515

java网络编程基于TCP的多客户端连接服务器

一、使用多线程实现多客户端连接服务端代码: package com.test.net; import java.io.IOException; import java.io.InputStream;...
  • xnf1991
  • xnf1991
  • 2016年09月02日 15:05
  • 2595

Linux网络编程--tcp服务器

一、做为 TCP 服务器需要具备的条件呢? 具备一个可以确知的地址( bind() ):相当于我们要明确知道移动客服的号码,才能给他们电话; 让操作系统知道是一个服务器,而不是客户端( listen(...
  • lianghe_work
  • lianghe_work
  • 2015年06月12日 09:48
  • 1618

实现TCP并发服务器之三(select函数)

前面两篇介绍用进程方式和线程方式实现并发服务,其实里面调用send,read,accept函数都会导致阻塞,而linux的select函数可以使我们在程序中同时监听多个文件描述符的读写状态。程序会停在...
  • ciaos
  • ciaos
  • 2012年07月04日 08:08
  • 1957

Linux网络编程——tcp并发服务器(多线程)

tcp多线程并发服务器 多线程服务器是对多进程服务器的改进,由于多进程服务器在创建进程时要消耗较大的系统资源,所以用线程来取代进程,这样服务处理程序可以较快的创建。据统计,创建线程与创建进程要快 ...
  • lianghe_work
  • lianghe_work
  • 2015年06月15日 15:27
  • 8244

[Linux网络编程] 循环服务器的实现

一、循环服务器的定义 循环服务器描述了在一个时刻只处理一个请求的服务器实现方式,通过在单线程内设置循环控制实现对多个客户端请求的逐一响应,这种服务器的设计、编程、调试和修改往往比较容易去实现。在循环执...
  • zhengqijun_
  • zhengqijun_
  • 2016年11月17日 19:39
  • 725

Linux网络编程——tcp并发服务器(poll实现)

想详细彻底地了解poll或看懂下面的代码请参考《Linux网络编程——I/O复用之poll函数》 代码: #include #include #include #include #incl...
  • lianghe_work
  • lianghe_work
  • 2015年06月17日 17:13
  • 3079
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Linux网络编程:TCP服务器(单进程多用户),使用select方法实现
举报原因:
原因补充:

(最多只允许输入30个字)