网络编程——I/O复用

int select( int nfds, fd_set FAR* readfds, fd_set * writefds, fd_set * exceptfds, const struct timeval * timeout);

  nfds:是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1。在Windows中这个参数的值无所谓,可以设置不正确。

  readfds:(可选)指针,指向一组等待可读性检查的套接口。

  writefds:(可选)指针,指向一组等待可写性检查的套接口。

  exceptfds:(可选)指针,指向一组等待错误检查的套接口。

  timeout:select()最多等待时间,对阻塞操作则为NULL。

  如果对readfds、writefds或exceptfds中任一个组类不感兴趣,可将它置为空NULL。

在socket.h头文件中共定义了四个宏来操作描述字集。

  FD_ZERO(fd_set *fdset);将指定的文件描述符集清空,在对文件描述符集合进行设置前,必须对其进行初始化,如果不清空,由于在系统分配内存空间后,通常并不作清空处理,所以结果是不可知的。
     FD_SET(fd_set *fdset);用于在文件描述符集合中增加一个新的文件描述符。 
     FD_CLR(fd_set *fdset);用于在文件描述符集合中删除一个文件描述符。 
     FD_ISSET(int fd,fd_set *fdset);用于测试指定的文件描述符是否在该集合中。

服务端代码:(客户端基本不变)

  1 #include "common.h"
  2
  3 struct cli_t
  4 {
  5     int cfd;
  6     struct sockaddr_in caddr;
  7     struct cli_t *next;
  8 };
  9
 10 typedef struct stu//学生结构体
 11 {
 12     int id;
 13     char name[20];
 14     int score;
 15     struct stu *next;
 16 }Stu,*PStu;
 17
 18 void fun(int sig)//捕捉信号
 19 {
 20     printf("连接中断\n");
 21     return;
 22 }
 23
 24 unsigned int ListLength(PStu ptr)  //计算链表长度
 25 {
 26     unsigned int l=0;
 27     while(ptr!=NULL)
 28     {
 29         l++;
 30         ptr=ptr->next;
 31     }
 32     return l;
 33 }
 34
 35 PStu ListSort(PStu ptr) //链表排序
 36 {
 37     PStu sorthead=NULL;
 38     unsigned int l=ListLength(ptr);
 39     while(l>0)
 40     {
 41         PStu p1=ptr;
 42         PStu p2=p1;
 43         int max=p1->score;
 44         while(p1!=NULL)
 45         {
 46             if(p1->score>max)
 47             {max=p1->score; p2=p1;}
 48             p1=p1->next;
 49         }
 50         PStu p3=ptr;
 51         if (p3==p2)
 52             ptr=p3->next;
 53         else
 54         {   while(p3->next!=p2)
 55             p3=p3->next;
 56
 57             p3->next=p2->next;
 58         }
 59
 60         PStu r;
 61         p2->next=NULL;
 62         if(sorthead==NULL)
 63             sorthead=p2;
 64         else
 65             r->next=p2;
 66
 67         r=p2;
 68         l--;
 69     }
 70     return sorthead;
 71 }
 72
 73 void show(PStu head)//打印链表函数
 74 {
 75     printf("学号\t姓名\t分数\n");
 76     while(head!=NULL)
 77     {
 78         printf("%d\t%s\t%d\n",head->id,head->name,head->score);
 79         head=head->next;
 80     }
 81     return ;
 82 }
 83
 84 int read_file(int agv)//接受信息(子线程)
 85 {
 86     PStu head=NULL;
 87     Stu *ptr;
 88     PStu r;
 89     int nfd;
 90     int ret;
 91     nfd=(int)agv;//接收传参nfd
 92
 93     signal(SIGPIPE,fun);
 94     printf("read...\n");
 95     while(1)
 96     {
 97         ptr=malloc(sizeof(Stu));
 98         ret=read(nfd,ptr,sizeof(Stu));
 99         if(ret<0)
100         {
101             perror("read");return -1;
102         }
103         if(ret==0)
104         {
105             head=ListSort(head);
106             show(head);
107             printf("read over\n");
108             return 0;
109         }
110         if(ret>0);//把链表读出来
111         {
112             ptr->next=NULL;
113             if(head==NULL)
114             {
115                 head=ptr;
116             }
117             else
118             {
119                 r->next=ptr;
120             }
121             r=ptr;
122         }
123     }
124     return 1;
125 }
126
127 int main()//主线程,一直工作,接收客户端
128 {
129     PStu head=NULL;
130     pthread_t pthid;
131     int ret;
132     int fd,nfd;
133     struct sockaddr_in  saddr,caddr;
134     int addr_len;
135     signal(SIGPIPE,fun);
136     fd = socket(AF_INET,SOCK_STREAM,0);
137     if(fd<0)
138     {
139         perror("socket");
140         return -1;
141     }
142     saddr.sin_family = AF_INET;
143     saddr.sin_port   = htons(9000);
144     inet_pton(AF_INET,"192.168.6.128",&saddr.sin_addr.s_addr);
145     ret = bind(fd,(struct sockaddr*)&saddr,sizeof(saddr));
146     if(ret<0)
147     {
148         perror("bind");
149         goto END;
150     }
151     ret = listen(fd,20);
152     if(ret<0)
153     {
154         perror("listen");
155         goto END;
156     }
157
158     fd_set set,rset;//创建文件描述符集合
159     int *pfd;
160     struct cli_t *chead=NULL;
161     struct cli_t *pcli;
162     int maxfd;
163
164     FD_ZERO(&set);//将文件描述符集清空
165     FD_SET(fd,&set);//在文件描述符集合中增加一个新的文件描述符
166     maxfd=fd;
167
168     while(1)//循环接收多个客户端
169     {
170         rset=set;//把set的文件描述符拷贝给reset,防止set发生改变
171         printf("select...\n");
172         ret=select(maxfd+1,&rset,NULL,NULL,NULL);//对读进行操作等待(无限等待)
173         printf("select over && ret= %d\n",ret);
174
175         if(ret<0)
176         {
177             perror("select");
178             break;
179         }
180         if(FD_ISSET(fd,&rset))//检查fd是否在文件描述符集中, select将更新这个集合,只保留符合条件的套节字在这个集合里面
181         {
182             //接收连接
183             addr_len = sizeof(caddr);
184             printf("accept..\n");
185             nfd = accept(fd,(struct sockaddr*)&caddr,&addr_len);
186             if(nfd<0)
187             {
188                 perror("accept");
189             }
190             printf("accept over..\n");
191             //加入集合
192             FD_SET(nfd,&set);//把nfd加入到set集合中
193             if(nfd>maxfd)
194                 maxfd=nfd;
195
196             // 加入链表
197             pcli=malloc(sizeof(struct cli_t));
198             pcli->cfd=nfd;
199             pcli->caddr=caddr;
200             pcli->next=chead;
201             chead=pcli;
202         }
203         for(pcli=chead;pcli!=NULL;pcli=pcli->next)//循环遍历,哪个客户端准备好了(写入并传输了数据就读出数据)
204         {
205             int tfd=pcli->cfd;
206             if(!FD_ISSET(tfd,&rset))//防止客户端没写入数据时阻塞在read里面
207                 continue;
208             ret=read_file(tfd);
209             if(ret<=0)
210             {
211                 printf("read ret =0 tcp broken\n");
212                 FD_CLR(tfd,&set);//客户端读取完毕且关闭,清空集合
213             }
214         }printf("asd");
215     }
216 END:
217     close(fd);
218     return 0;
219 }

原文地址:https://www.cnblogs.com/it8343/p/9280642.html

时间: 2024-08-29 21:37:43

网络编程——I/O复用的相关文章

Linux网络编程:端口复用

在<绑定( bind )端口需要注意的问题>提到:一个网络应用程序只能绑定一个端口( 一个套接字只能绑定一个端口 ). 实际上,默认的情况下,如果一个网络应用程序的一个套接字 绑定了一个端口( 占用了 8000 ),这时候,别的套接字就无法使用这个端口( 8000 ), 验证例子如下: #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #i

linux网络编程函数——地址复用setsockopt()

1.setsockopt()作用及简介 socket关闭之后并不会立即收回,而是要经历一个TIME_WAIT的阶段.此时对这个端口进行重新绑定就会出错.要想立即绑定端口,需要先设置 SO_REUSEADDR. 或者在closesocket的时候,使用setsockopt设置SO_DONTLINGER.才会消除TIME_WAIT时间,用setsockopt()函数即可实现. #include <sys/types.h>          /* See NOTES */#include <s

Linux网络编程——I/O复用之poll函数

一.回顾前面的select select优点: 目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点 select缺点: 1.每次调用 select(),都需要把 fd 集合从用户态拷贝到内核态,这个开销在 fd 很多时会很大,同时每次调用 select() 都需要在内核遍历传递进来的所有 fd,这个开销在 fd 很多时也很大. 2.单个进程能够监视的文件描述符的数量存在最大限制,在 Linux 上一般为 1024,可以通过修改宏定义甚至重新编译内核的方式提升这一限制,但是这样也会造成效

Linux网络编程-IO复用技术

IO复用是Linux中的IO模型之一,IO复用就是进程预先告诉内核需要监视的IO条件,使得内核一旦发现进程指定的一个或多个IO条件就绪,就通过进程进程处理,从而不会在单个IO上阻塞了.Linux中,提供了select.poll.epoll三种接口函数来实现IO复用. 1.select函数 #include <sys/select.h> #include <sys/time.h> int select(int nfds, fd_set *readfds, fd_set *writef

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

想详细彻底地了解poll或看懂下面的代码请参考<Linux网络编程--I/O复用之poll函数> 代码: #include <string.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/select.h> #include <sys/time.h> #include <sys/socket.h> #incl

Linux网络编程——端口复用(多个套接字绑定同一个端口)

在<绑定( bind )端口需要注意的问题>提到:一个网络应用程序只能绑定一个端口( 一个套接字只能绑定一个端口 ). 实际上,默认的情况下,如果一个网络应用程序的一个套接字 绑定了一个端口( 占用了 8000 ),这时候,别的套接字就无法使用这个端口( 8000 ), 验证例子如下: [objc] view plaincopy #include <stdio.h> #include <stdlib.h> #include <string.h> #inclu

Unix网络编程(六)高级I/O技术之复用技术 select

I/O复用技术 本文将讨论网络编程中的高级I/O复用技术,将从下面几个方面进行展开: a. 什么是复用技术呢? b. 什么情况下需要使用复用技术呢? c. I/O的复用技术的工作原理是什么? d. select, poll and epoll的实现机制,以及他们之间的区别. 下面我们以一个背景问题来开始: 包括在以前的文章中我们讨论的案例都是阻塞式的I/O包括(fgetc/getc, fgets/gets),即当输入条件未满足时进程会阻塞直到满足之后进行读取,但是这样导致的一个 问题是如果此时进

TCP/IP 网络编程 (抄书笔记 5) -- select 和 IO 复用

TCP/IP 网络编程 (抄书笔记 5) – select 和 IO 复用 TCP/IP 网络编程 (抄书笔记 5) – select 和 IO 复用 利用 fork() 生成子进程 可以达到 服务器端可以同时响应多个 客户端的请求, 但是这样做有缺点: 需要大量的运算和内存空间, 每个进程都要有独立的内存空间, 数据交换也很麻烦 (IPC, 如管道) IO 复用: 以太网的总线结构也是采用了 复用技术, 如果不采用, 那么两两之间就要直接通信 网络知识 int server_sock; int

【unix网络编程第三版】阅读笔记(五):I/O复用:select和poll函数

本博文主要针对UNP一书中的第六章内容来聊聊I/O复用技术以及其在网络编程中的实现 1. I/O复用技术 I/O多路复用是指内核一旦发现进程指定的一个或者多个I/O条件准备就绪,它就通知该进程.I/O复用适用于以下场合: (1) 当客户处理多个描述符(一般是交互式输入或网络套接字),必须适用I/O复用 (2) 当一个客户处理多个套接字时,这种情况很少见,但也可能出现 (3) 当一个TCP服务器既要处理监听套接字,又要处理已连接套接字,一般就要使用I/O复用 (4) 如果一个服务器既要适用TCP,