利用select实现IO多路复用TCP服务端
一、相关函数
1. int select(int maxfdp, fd_set *readset, fd_set *writeset, fd_set *exceptset,struct timeval *timeout);
int maxfdp: 该参数是指集合中所有文件描述符的范围, 即所有文件描述符的最大值加1;
fd_set *readset: 该参数是我们所关心的文件是否可读的文件描述符的集合, 如果这个集合中有个文件可读了,那select返回一个大于0的数,表示有文件可读了
fd_set *writeset:......可写......
fd_set *exceptset: ......异常发生......
timeval *timeout:该参数是select的超时参数,这个参数使select处于三种状态:(1)timeout传入NULL,则select一直等到文件状态有变化时才返回,这段时间一直处于阻塞状态。(2):timeout 传入0,则select会立即返回(非阻塞),如果文件状态有变化则返回一个大于0的值没有变化则返回0;(3)timeout传入一个大于0的数,则select在timeout时间内阻塞,一旦文件状态有变化就会返回,超时后不管怎样都会返回值同样是文件状态右边话就返回一个大于0的值,无变化则返回0;
timeval的结构:
struct timeval{
long tv_sec; /*秒 */
long tv_usec; /*微秒 */
}
select返回一个小于0的数则说明出错。
2.fd_set
fd_set类型变量每一位代表了一个描述符。我们也可以认为它只是一个由很多二进制位构成的数组,如下图所示:

操作fd_set的宏有:
#include <sys/select.h>
int FD_ZERO(int fd, fd_set *fdset);
int FD_CLR(int fd, fd_set *fdset);
int FD_SET(int fd, fd_set *fd_set);
int FD_ISSET(int fd, fd_set *fdset);
FD_ZERO宏将一个 fd_set类型变量的所有位都设为 0,使用FD_SET将变量的某个位置位。清除某个位时可以使用 FD_CLR,我们可以使用FD_ISSET来测试某个位是否被置位(即状态是否发生了变化)。
当声明了一个文件描述符集后,必须用FD_ZERO将所有位置零。之后将我们所感兴趣的描述符所对应的位置位
二、TCP服务端代码
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h>
#include "wrap.h"
#define MAXLINE 80
#define SERV_PORT 8001
int main(int argc, char **argv)
{
int i, maxi, maxfd, listenfd, connfd, sockfd;
int nready, client[FD_SETSIZE];
ssize_t n;
fd_set rset, allset;
char buf[MAXLINE];
char str[INET_ADDRSTRLEN];
socklen_t cliaddr_len;
struct sockaddr_in
cliaddr, servaddr;
listenfd = Socket(AF_INET, SOCK_STREAM, );
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
Listen(listenfd, );
maxfd = listenfd;
maxi = -;
for (i = ; i < FD_SETSIZE; i++)
client[i] = -; /* -1 indicates available entry */
FD_ZERO(&allset);
FD_SET(listenfd, &allset);
for ( ; ; ) {
rset = allset; /* structure assignment */
nready = select(maxfd+, &rset, NULL, NULL, NULL);
if (nready < )
perr_exit("select error");
if (FD_ISSET(listenfd, &rset)) { /* new client connection */
cliaddr_len = sizeof(cliaddr);
connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);
printf("received from %s at PORT %d\n",
inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
ntohs(cliaddr.sin_port));
for (i = ; i < FD_SETSIZE; i++)
if (client[i] < ) {
client[i] = connfd; /* save descriptor */
break;
}
if (i == FD_SETSIZE) {
fputs("too many clients\n", stderr);
exit();
}
FD_SET(connfd, &allset); /* add new descriptor to set */
if (connfd > maxfd)
maxfd = connfd; /* for select */
if (i > maxi)
maxi = i; /* max index in client[] array */
if (--nready == )
continue; /* no more readable descriptors */
}
for (i = ; i <= maxi; i++) {
/* check all clients 714 for data */
if ( (sockfd = client[i]) < )
continue;
if (FD_ISSET(sockfd, &rset)) {
if ( (n = Read(sockfd, buf, MAXLINE)) == ) {
Close(sockfd);
FD_CLR(sockfd, &allset);
client[i] = -;
} else {
int j;
for (j = ; j < n; j++)
buf[j] = toupper(buf[j]);
Write(sockfd, buf, n);
}
if (--nready == )
break; /* no more readable descriptors */
}
}
}
}
备:封装原始linux函数wrap.c:
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h> void perr_exit(const char *s)
{
perror(s);
exit();
} int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr)
{
int n;
again:
if ((n = accept(fd, sa, salenptr)) < ) {
if ((errno == ECONNABORTED) || (errno == EINTR))
goto again;
else
perr_exit("accept error");
} return n;
} void Bind(int fd, struct sockaddr *sa, socklen_t salen)
{
if (bind(fd, sa, salen) < )
perr_exit("bind error");
} void Connect(int fd, const struct sockaddr *sa, socklen_t salen)
{
if (connect(fd, sa, salen) < )
perr_exit("connent error");
} void Listen(int fd, int backlog)
{
if (listen(fd, backlog) < )
perr_exit("listen error");
} int Socket(int family, int type, int protocol)
{
int n;
if ((n = socket(family, type, protocol)) < )
perr_exit("socket error");
return n;
} ssize_t Read(int fd, void *ptr, size_t nbytes)
{
ssize_t n;
again:
if ((n = read(fd, ptr, nbytes)) < ) {
if (errno == EINTR)
goto again;
else
return -;
} return n;
} ssize_t Write(int fd, const void *ptr, size_t nbytes)
{
ssize_t n;
again:
if ((n = write(fd, ptr, nbytes)) == -) {
if (errno == EINTR)
goto again;
else
return -;
} return n;
} void Close(int fd)
{
if (close(fd) == -)
perr_exit("close error");
} ssize_t Readn(int fd, void *vptr, size_t n)
{
size_t nleft;
ssize_t nread;
char *ptr; ptr = vptr;
nleft = n;
while (nleft > ) {
if ((nread = read(fd, ptr, nleft)) < ) {
if (errno == EINTR)
nread = ;
else
return -;
} else if (nread == ) {
break;
}
nleft -= nread;
ptr += nread;
} return n - nleft;
} ssize_t Writen(int fd, const void *vptr, size_t n)
{
size_t nleft; ssize_t nwritten;
const char *ptr; ptr = vptr;
nleft = n;
while (nleft > ) {
if ((nwritten = write(fd, ptr, nleft)) <= ) {
if (nwritten < && errno == EINTR)
nwritten = ;
else
return -;
} nleft -= nwritten;
ptr += nwritten;
} return n;
} static ssize_t my_read(int fd, char *ptr)
{
static int read_cnt;
static char *read_ptr;
static char read_buf[]; if (read_cnt <= ) {
again:
if ((read_cnt = read(fd, read_buf, sizeof(read_buf))) < ) {
if (errno == EINTR)
goto again;
return -;
} else if (read_cnt == )
return ;
read_ptr = read_buf;
}
read_cnt--;
*ptr = *read_ptr++;
return ;
} ssize_t Readline(int fd, void *vptr, size_t maxlen)
{
ssize_t n, rc;
char c, *ptr; ptr = vptr;
for (n = ; n < maxlen; n++) {
if ((rc = my_read(fd, &c)) == ) {
*ptr++ = c;
if (c == '\n')
break;
} else if (rc == ) {
*ptr = ;
return n - ;
} else {
return -;
}
}
*ptr = ;
return n;
}
利用select实现IO多路复用TCP服务端的更多相关文章
- 基于Select模型的Windows TCP服务端和客户端程序示例
最近跟着刘远东老师的<C++百万并发网络通信引擎架构与实现(服务端.客户端.跨平台)>,Bilibili视频地址为C++百万并发网络通信引擎架构与实现(服务端.客户端.跨平台),重新复习下 ...
- 进程池与线程池、协程、协程实现TCP服务端并发、IO模型
进程池与线程池.协程.协程实现TCP服务端并发.IO模型 一.进程池与线程池 1.线程池 ''' 开进程开线程都需要消耗资源,只不过两者比较的情况下线程消耗的资源比较少 在计算机能够承受范围内最大限度 ...
- python GIL全局解释器锁,多线程多进程效率比较,进程池,协程,TCP服务端实现协程
GIL全局解释器锁 ''' python解释器: - Cpython C语言 - Jpython java ... 1.GIL: 全局解释器锁 - 翻译: 在同一个进程下开启的多线程,同一时刻只能有一 ...
- 子进程回收资源两种方式,僵尸进程与孤儿进程,守护进程,进程间数据隔离,进程互斥锁,队列,IPC机制,线程,守护线程,线程池,回调函数add_done_callback,TCP服务端实现并发
子进程回收资源两种方式 - 1) join让主进程等待子进程结束,并回收子进程资源,主进程再结束并回收资源. - 2) 主进程 “正常结束” ,子进程与主进程一并被回收资源. from multipr ...
- GIL全局解释锁,死锁,信号量,event事件,线程queue,TCP服务端实现并发
一.GIL全局解释锁 在Cpython解释器才有GIL的概念,不是python的特点 在Cpython解释器中,同一个进程下开启的多线程,同一时刻只能有一个线程执行,无法利用多核优势. 1.GIL介绍 ...
- Java网络编程(TCP服务端)
/* * TCP服务端: * 1.创建服务端socket服务,并监听一个端口 * 2.服务端为了给客户端提供服务,获取客户端的内容,可以通过accept方法获取连接过来的客户端对象 * 3.可以通过获 ...
- TCP服务端开发为例--web开发不同url请求走不同control方法
拿java的web开发为例子,相信有很多小伙伴是做j2EE开发的,htpp请求,json数据传输都是工作中经常用的,查询请求,添加请求,修改请求前端配个url,例如https://localhost/ ...
- TCP服务端开发为例--web开发不同url请求为何会走不同方法
拿java的web开发为例子,相信有很多小伙伴是做j2EE开发的,htpp请求,json数据传输都是工作中经常用的,查询请求,添加请求,修改请求前端配个url,例如https://localhost/ ...
- Python中的Tcp协议应用之TCP服务端-线程版
利用线程实现,一个服务端同时服务多个客户端的需求. TCP服务端-线程版代码实现: import socket import threading def handle_client_socket(ne ...
随机推荐
- 从Script到Code Blocks、Code Behind到MVC、MVP、MVVM
刚过去的周五(3-14)例行地主持了技术会议,主题正好是<UI层的设计模式——从Script.Code Behind到MVC.MVP.MVVM>,是前一天晚上才定的,中午花了半小时准备了下 ...
- 线性数据结构之栈——Stack
Linear data structures linear structures can be thought of as having two ends, whose items are order ...
- gulp详细入门教程
本文链接:http://www.ydcss.com/archives/18 gulp详细入门教程 简介: gulp是前端开发过程中对代码进行构建的工具,是自动化项目的构建利器:她不仅能对网站资源进行优 ...
- WebApi接口 - 响应输出xml和json
格式化数据这东西,主要看需要的运用场景,今天和大家分享的是webapi格式化数据,这里面的例子主要是输出json和xml的格式数据,测试用例很接近实际常用情况:希望大家喜欢,也希望各位多多扫码支持和点 ...
- SignalR SelfHost实时消息,集成到web中,实现服务器消息推送
先前用过两次SignalR,但是中途有段时间没弄了,今天重新弄,发现已经忘得差不多了,做个笔记! 首先创建一个控制台项目Nuget添加引用联机搜索:Microsoft.AspNet.SignalR.S ...
- CSharpGL(33)使用uniform块来优化对uniform变量的读写
CSharpGL(33)使用uniform块来优化对uniform变量的读写 +BIT祝威+悄悄在此留下版了个权的信息说: Uniform块 如果shader程序变得比较复杂,那么其中用到的unifo ...
- 利用Select2优化@Html.ListBoxFor显示,学会用MultiSelectList
最近需要用到多选框,Asp.Net MVC自带的@Html.ListBox或@Html.ListBoxFor的显示效果太差,于是找到了Select2进行优化,并正式了解了多选框的操作方法. 首先介绍多 ...
- SAP CRM 用户界面对象类型和设计对象
在CRM中的用户界面对象类型的帮助下,我们可以做这些工作: 进行不同的视图配置 创建动态导航 从设计层控制字段标签.值帮助 控制BOL对象的属性的可视性 从导航栏访问自定义组件 一个用户界面对象类型之 ...
- 如何区别exists与not exists?
1.exists:sql返回结果集为真:not exists:sql不返回结果集为真.详解过程如图: exists not exists
- asp.net core 实战之 redis 负载均衡和"高可用"实现
1.概述 分布式系统缓存已经变得不可或缺,本文主要阐述如何实现redis主从复制集群的负载均衡,以及 redis的"高可用"实现, 呵呵双引号的"高可用"并不是 ...