Linux网络编程——tcp并发服务器(I/O复用之select

http://blog.csdn.net/lianghe_work/article/details/46519633

多线程、多进程相比,I/O复用最大的优势是系统开销小,系统不需要建立新的进程或者线程,也不必维护这些线程和进程。


代码示例:

[csharp] view plain copy
  1. #include <stdio.h>   
  2. #include <unistd.h>  
  3. #include <stdlib.h>  
  4. #include <errno.h>  
  5. #include <string.h>  
  6. #include <sys/socket.h>  
  7. #include <sys/types.h>  
  8. #include <netinet/in.h>  
  9. #include <arpa/inet.h>  
  10. #include <sys/select.h>  
  11.  
  12. #define SERV_PORT 8080  
  13. #define LIST 20                //服务器最大接受连接  
  14. #define MAX_FD 10              //FD_SET支持描述符数量  
  15.   
  16.   
  17. int main(int argc, char *argv[])  
  18. {  
  19.     int sockfd;  
  20.     int err;  
  21.     int i;  
  22.     int connfd;  
  23.     int fd_all[MAX_FD]; //保存所有描述符,用于select调用后,判断哪个可读  
  24.       
  25.     //下面两个备份原因是select调用后,会发生变化,再次调用select前,需要重新赋值  
  26.     fd_set fd_read;    //FD_SET数据备份  
  27.     fd_set fd_select;  //用于select  
  28.   
  29.     struct timeval timeout;         //超时时间备份  
  30.     struct timeval timeout_select;  //用于select  
  31.       
  32.     struct sockaddr_in serv_addr;   //服务器地址  
  33.     struct sockaddr_in cli_addr;    //客户端地址  
  34.     socklen_t serv_len;  
  35.     socklen_t cli_len;  
  36.       
  37.     //超时时间设置  
  38.     timeout.tv_sec = 10;  
  39.     timeout.tv_usec = 0;  
  40.       
  41.     //创建TCP套接字  
  42.     sockfd = socket(AF_INET, SOCK_STREAM, 0);  
  43.     if(sockfd < 0)  
  44.     {  
  45.         perror("fail to socket");  
  46.         exit(1);  
  47.     }  
  48.       
  49.     // 配置本地地址  
  50.     memset(&serv_addr, 0, sizeof(serv_addr));  
  51.     serv_addr.sin_family = AF_INET;         // ipv4  
  52.     serv_addr.sin_port = htons(SERV_PORT);  // 端口, 8080  
  53.     serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); // ip  
  54.   
  55.     serv_len = sizeof(serv_addr);  
  56.       
  57.     // 绑定  
  58.     err = bind(sockfd, (struct sockaddr *)&serv_addr, serv_len);  
  59.     if(err < 0)  
  60.     {  
  61.         perror("fail to bind");  
  62.         exit(1);  
  63.     }  
  64.   
  65.     // 监听  
  66.     err = listen(sockfd, LIST);  
  67.     if(err < 0)  
  68.     {  
  69.         perror("fail to listen");  
  70.         exit(1);  
  71.     }  
  72.       
  73.     //初始化fd_all数组  
  74.     memset(fd_all, -1, sizeof(fd_all));  
  75.   
  76.     fd_all[0] = sockfd;   //第一个为监听套接字  
  77.       
  78.     FD_ZERO(&fd_read);  // 清空  
  79.     FD_SET(sockfd, &fd_read);  //将监听套接字加入fd_read  
  80.   
  81.     int maxfd = fd_all[0];  //监听的最大套接字  
  82.       
  83.     while(1){  
  84.       
  85.         // 每次都需要重新赋值,fd_select,timeout_select每次都会变  
  86.         fd_select = fd_read;  
  87.         timeout_select = timeout;  
  88.           
  89.         // 检测监听套接字是否可读,没有可读,此函数会阻塞  
  90.         // 只要有客户连接,或断开连接,select()都会往下执行  
  91.         err = select(maxfd+1, &fd_select, NULL, NULL, NULL);  
  92.         //err = select(maxfd+1, &fd_select, NULL, NULL, (struct timeval *)&timeout_select);  
  93.         if(err < 0)  
  94.         {  
  95.                 perror("fail to select");  
  96.                 exit(1);  
  97.         }  
  98.   
  99.         if(err == 0){  
  100.             printf("timeout\n");  
  101.         }  
  102.           
  103.         // 检测监听套接字是否可读  
  104.         if( FD_ISSET(sockfd, &fd_select) ){//可读,证明有新客户端连接服务器  
  105.               
  106.             cli_len = sizeof(cli_addr);  
  107.               
  108.             // 取出已经完成的连接  
  109.             connfd = accept(sockfd, (struct sockaddr *)&cli_addr, &cli_len);  
  110.             if(connfd < 0)  
  111.             {  
  112.                 perror("fail to accept");  
  113.                 exit(1);  
  114.             }  
  115.               
  116.             // 打印客户端的 ip 和端口  
  117.             char cli_ip[INET_ADDRSTRLEN] = {0};  
  118.             inet_ntop(AF_INET, &cli_addr.sin_addr, cli_ip, INET_ADDRSTRLEN);  
  119.             printf("----------------------------------------------\n");  
  120.             printf("client ip=%s,port=%d\n", cli_ip,ntohs(cli_addr.sin_port));  
  121.               
  122.             // 将新连接套接字加入 fd_all 及 fd_read  
  123.             for(i=0; i < MAX_FD; i++){  
  124.                 if(fd_all[i] != -1){  
  125.                     continue;  
  126.                 }else{  
  127.                     fd_all[i] = connfd;  
  128.                     printf("client fd_all[%d] join\n", i);  
  129.                     break;  
  130.                 }  
  131.             }  
  132.               
  133.             FD_SET(connfd, &fd_read);  
  134.               
  135.             if(maxfd < connfd)  
  136.             {  
  137.                 maxfd = connfd;  //更新maxfd  
  138.             }  
  139.           
  140.         }  
  141.           
  142.         //从1开始查看连接套接字是否可读,因为上面已经处理过0(sockfd)  
  143.         for(i=1; i < maxfd; i++){  
  144.             if(FD_ISSET(fd_all[i], &fd_select)){  
  145.                 printf("fd_all[%d] is ok\n", i);  
  146.                   
  147.                 char buf[1024]={0};  //读写缓冲区  
  148.                 int num = read(fd_all[i], buf, 1024);  
  149.                 if(num > 0){  
  150.   
  151.                     //收到 客户端数据并打印  
  152.                     printf("receive buf from client fd_all[%d] is: %s\n", i, buf);  
  153.                       
  154.                     //回复客户端  
  155.                     num = write(fd_all[i], buf, num);  
  156.                     if(num < 0){  
  157.                         perror("fail to write ");  
  158.                         exit(1);  
  159.                     }else{  
  160.                         //printf("send reply\n");  
  161.                     }  
  162.                       
  163.                 }  
  164.                 else if(0 == num){ // 客户端断开时  
  165.                       
  166.                     //客户端退出,关闭套接字,并从监听集合清除  
  167.                     printf("client:fd_all[%d] exit\n", i);  
  168.                     FD_CLR(fd_all[i], &fd_read);  
  169.                     close(fd_all[i]);  
  170.                     fd_all[i] = -1;  
  171.                       
  172.                     continue;  
  173.                 }  
  174.                   
  175.             }else {  
  176.                 //printf("no data\n");                    
  177.             }  
  178.         }  
  179.     }  
  180.       
  181.     return 0;  
  182. }  

运行结果:


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/384189.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux下的I/O复用与epoll详解

http://www.cnblogs.com/lojunren/p/3856290.html 前言 I/O多路复用有很多种实现。在linux上&#xff0c;2.4内核前主要是select和poll&#xff0c;自Linux 2.6内核正式引入epoll以来&#xff0c;epoll已经成为了目前实现高性能网络服务器的必备技术。尽管他们的使用方法不尽相…

数据结构--顺序栈和链式栈

http://www.cnblogs.com/jingliming/p/4602458.html 栈是一种限定只在表尾进行插入或删除操作,栈也是线性表表头称为栈的底部,表尾称为栈的顶部,表为空称为空栈&#xff0c;栈又称为后进先出的线性表,栈也有两种表示:顺序栈与链式栈顺序栈是利用一组地址连续的存储单元&#xf…

数据结构--双链表的创建和操作

http://www.cnblogs.com/jingliming/p/4602144.html#0-tsina-1-42616-397232819ff9a47a7b7e80a40613cfe1 一、双向链表的定义 双向链表也叫双链表&#xff0c;是链表的一种&#xff0c;它的每个数据结点中都有两个指针&#xff0c;分别指向直接后继和直接前驱。所以&#xff0c…

MYSQL错误代码#1045 Access denied for user 'root'@'localhost'

http://blog.csdn.net/lykezhan/article/details/70880845 遇到MYSQL“错误代码#1045 Access denied for user rootlocalhost (using password:YES)” 需要重置root账号权限密码&#xff0c;这个一般还真不好解决。 不过&#xff0c;这几天调试的时候真的遇到了这种问题&#x…

常量变量以及循环

常量 1.三目运算词 三字母词表达字符???([??)]??<{??>} 2.循环 1).数组元素以及变量在内存中的分配顺序 2)goto语句应用 //电脑关机程序 #include<stdio.h> #include <stdlib.h> #include <string.h> #include <windows.h> int ma…

Linux 环境 C语言 操作MySql 的接口范例

http://www.cnblogs.com/wunaozai/p/3876134.html 接上一小节&#xff0c;本来是计划这一节用来讲数据库的增删改查&#xff0c;但是在实现的过程中&#xff0c;出现了一点小问题&#xff0c;也不是技术的问题&#xff0c;就是在字符界面上比较不好操作。比如要注册一个帐号&a…

数组相关运算

数组的初始化 数组及指针在内存中的存储 一维数组在内存中的存储 有关数组的运算 //一维数组 int a[] {1,2,3,4}; printf("%d\n",sizeof(a));//16这里的a表示的是整个数组,计算出的是整个数组的大小,单位为byte printf("%d\n",sizeof(a 0));/*a没有单独…

gets fgets 区别

http://www.cnblogs.com/aexin/p/3908003.html 1. gets与fgets gets函数原型&#xff1a;char*gets(char*buffer);//读取字符到数组&#xff1a;gets(str);str为数组名。 gets函数功能&#xff1a;从键盘上输入字符&#xff0c;直至接受到换行符或EOF时停止&#xff0c;并将读取…

Shuffle'm Up——简单模拟

【题目描述】 A common pastime for poker players at a poker table is to shuffle stacks of chips. Shuffling chips is performed by starting with two stacks of poker chips, S1 and S2, each stack containing C chips. Each stack may contain chips of several diff…

Fire!——两个BFS

【题目描述】 【题目分析】 看到题目后很清楚是两个BFS&#xff0c;可是我觉得对于火的BFS可以转换成判断&#xff0c;我的做法是将火的位置全部记录下来&#xff0c;然后判断某个位置距离每个火的步数是否小于当前步数&#xff0c;可是错了&#xff0c;还不清楚为什么&#x…

函数调用过程(栈桢)

栈桢 首先来看一段代码 #include<stdio.h> int add(int x, int y) {int z x y;return z; } int main() {int a 10;int b 20;int ret add(a, b);printf("ret %d\n",ret);return 0; } 此处是为了给a,b分别开辟空间,这时栈桢如图所示 两条push命令将a,b变…

整型数据存储

//代码1 #include<stdio.h> int main() {char a -1;signed char b -1;unsigned char c -1;printf("a %d, b %d, c %d", a, b, c);return 0; } 1000 0000 0000 0001 -> -1源码 1111 1111 1111 1110 -> -1反码 1111 1111 1111 1111 -> -1补码 对于…

HDU - 4578Transformation——线段树+区间加法修改+区间乘法修改+区间置数+区间和查询+区间平方和查询+区间立方和查询

【题目描述】 HDU - 4578Transformation Problem Description Yuanfang is puzzled with the question below: There are n integers, a1, a2, …, an. The initial values of them are 0. There are four kinds of operations. Operation 1: Add c to each number between ax …

[C++基础]034_C++模板编程里的主版本模板类、全特化、偏特化(C++ Type Traits)

http://www.cnblogs.com/alephsoul-alephsoul/archive/2012/10/18/2728753.html 1. 主版本模板类 首先我们来看一段初学者都能看懂&#xff0c;应用了模板的程序&#xff1a; 1 #include <iostream>2 using namespace std;3 4 template<class T1, class T2>5 clas…

使用openssl的md5库

http://blog.csdn.net/sinat_35297665/article/details/78244523 在linux机器上&#xff0c;有一个命令可以计算出文件的md5值&#xff0c;那就是md5sum&#xff0c;如果没有的话&#xff0c;就需要安装RPM包&#xff1a;coreutils。 现在我们使用openssl的库也可以方便的计算出…

Socket网络编程--小小网盘程序(1)

http://www.cnblogs.com/wunaozai/p/3886588.html 这个系列是准备讲基于Linux Socket进行文件传输。简单的文件传输就是客户端可以上传文件&#xff0c;可以从服务器端下载文件。就这么两个功能如果再加上身份验证&#xff0c;就成了FTP服务器了&#xff0c;如果对用户的操作再…

HDU - 4348To the moon——主席树+区间修改

HDU - 4348To the moon 【题目描述】 【题目分析】 题目中说明每次更新后时间都会加1&#xff0c;而且还会需要查询以前的区间&#xff0c;还会需要返回以前的时间&#xff0c;所以是很裸的主席树。区间查询的话我们同样需要用到lazy标记 通过这道题我发现线段树的操作还是很灵…

进入一个目录需要那些权限

1.文件访问者的分类 文件的访问者具体可分为以下几类&#xff1a; (1)拥有者 (2)组拥有者 (3)其他用户 这些都代表什么意思呢&#xff1f; 其中r表示只读&#xff0c;w表示只写&#xff0c;x表示可执行&#xff0c;第一个字母代表了文件的类型&#xff0c;其中文件类型可以分为…

Socket网络编程--小小网盘程序(2)

http://www.cnblogs.com/wunaozai/p/3887728.html 这一节将不会介绍太多的技术的问题&#xff0c;这节主要是搭建一个小小的框架&#xff0c;为了方便接下来的继续编写扩展程序。本次会在上一小节的基础上加上一个身份验证的功能。 因为网盘程序不像聊天程序&#xff0c;网盘是…

Linux下的重要目录

1.bin目录 主要防止系统下的各种必备可执行文件 2./proc 目录 这个目录相当于Windows下的计算机系统信息查看以及进程动态查看&#xff0c;可以查看计算机信息&#xff0c;用来存放当前计算机上的进程信息 3./sys 目录 (1)其中block目录用于存放块设备文件 (2)bus存放总线类型…