利用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 ...
随机推荐
- Sublime Text3配置在可交互环境下运行python快捷键
安装插件 在Sublime Text3下面写代码感觉很不错,但是写Python的时候遇到了一些问题. 用Sublime Text3打开python文件,或者在Sublime Text3下写好pytho ...
- Bootstrap 模态框(Modal)插件
页面效果: html+js: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> ...
- IE8/9 JQuery.Ajax 上传文件无效
IE8/9 JQuery.Ajax 上传文件有两个限制: 使用 JQuery.Ajax 无法上传文件(因为无法使用 FormData,FormData 是 HTML5 的一个特性,IE8/9 不支持) ...
- zookeeper源码分析之六session机制
zookeeper中session意味着一个物理连接,客户端连接服务器成功之后,会发送一个连接型请求,此时就会有session 产生. session由sessionTracker产生的,sessio ...
- vs15 preview5 离线安装包
1.介绍 vs15是微软打造的新一代IDE,全新的安装方式.官网介绍如下(https://blogs.msdn.microsoft.com/visualstudio/2016/10/05/announ ...
- 敏捷转型历程 - Sprint3 Grooming
我: Tech Leader 团队:团队成员分布在两个城市,我所在的城市包括我有4个成员,另外一个城市包括SM有7个成员.另外由于我们的BA离职了,我暂代IT 的PO 职位.PM和我在一个城市,但他不 ...
- centos安装nodejs
1.下载安装nodejs wget http://nodejs.org/dist/v0.10.25/node-v0.10.25.tar.gz compat--c++ tar -xf node-v0.1 ...
- samba服务
安装samba服务步骤ps -e 查看进程ps -e | grep 文件名 管道符的使用rpm -qa 安装包的查看rpm -qa | grep samba 抓Samba安装包 注释:包与包之间有依赖 ...
- SpringMVC 数据校验
1.引入jar包 2.配置验证器 <!-- 配置验证器 --> <bean id="myvalidator" class="org.springfram ...
- MVC5在Mono上的各种坑
买了Macbook后,各种事情的纠缠,都没好好地用过OSX系统. 果断的装上了xcode和mono,还有monodevelop. 然后把项目移植到mono上运行,各种问题. 然后第一个问题来了 权限不 ...