Linux网络编程之IO复用——Select
Linux网络编程之IO复用——Select
IO复用使得程序能够同时监听多个文件描述符,可以大大提高程序的性能。
select 系统调用
select系统调用的用途是:在一段指定的时间内,监听用户感兴趣的文件描述符上的可读、可写和异常事件。
#include
int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds,struct timeval* timeout);
/*
* 成功时返回就绪文件描述符总数
* 如果没有任何文件描述符就绪,select返回0
* 失败返回-1
*/
nfds参数指定被监听的文件描述符的总数,一般设置为select监听的所有文件描述符最大值+1,因为文件描述符从0开始。
readfds、writefds、exceptfds参数分别指向可读、可写和异常等事件对应的文件描述符集合。
应用程序调用select时,通过这三个参数传入自己感兴趣的文件描述符,select调用返回时,内核将修改它们来通知应用程序哪些文件描述符 已就绪。
因为内核会对其进行修改,所以就需要进行保存。
这三个参数对应的fd_set其实就是一个整形数组,该数组的每个元素的每一位标记一个文件描述符。
-timeout设置select的超时时间。
修改fd_set结构体
#include
FD_ZERO(fd_set* fdset);//清除fdset所有位
FD_SET(int fd, fd_set* fdset);//设置fdset的位fd
FD_CLR(int fd, fd_set* fdset);//清除fdset的位fd
int FD_ISSET(int fd, fd_ste* fdset);//测试fdset的位fd是否被设置
头文件 tcp_socet.h
#pragma once
#pragma once
#include
#include
#include
#include
#include
#include
#include
#define BUFF_SIZE 128
using namespace std;
class Socket
{
public:
Socket()
{
sockfd_ = socket(PF_INET, SOCK_STREAM, 0);
assert(sockfd_ >= 0);
}
int Get_socket()
{
return sockfd_;
}
~Socket()
{
close(sockfd_);
}
protected:
int sockfd_;
};
class Socket_Ser :public Socket
{
public:
Socket_Ser(const char* ip, int port = 6000, int backlog = 5)
{
struct sockaddr_in address;
memset(&address, 0, sizeof(address));
address.sin_family = AF_INET;
address.sin_port = htons(port);
address.sin_addr.s_addr = inet_addr(ip);
int ret = bind(sockfd_, (struct sockaddr*)&address, sizeof(address));
assert(ret != -1);
ret = listen(sockfd_, backlog);
assert(ret != -1);
}
int Accept()
{
struct sockaddr_in client;
socklen_t len = sizeof(client);
int connfd = accept(sockfd_, (struct sockaddr*)&client, &len);
return connfd;
}
};
class Socket_Cli :public Socket
{
public:
Socket_Cli(const char* ip, int port = 6000)
{
struct sockaddr_in address;
memset(&address, 0, sizeof(address));
address.sin_family = AF_INET;
address.sin_port = htons(port);
address.sin_addr.s_addr = inet_addr(ip);
int ret = connect(sockfd_, (struct sockaddr*)&address, sizeof(address));
assert(ret != -1);
}
void Over_connect()
{
close(sockfd_);
}
};
头文件tcp_select.h
#pragma once
#include "tcp_socket.h"
class Select
{
public:
Select()
{
FD_ZERO(&readfds_);
FD_ZERO(&exceptfds_);
FD_ZERO(&writefds_);
}
void Using_Select(int serfd)
{
int ret = select(serfd + 1, &readfds_, NULL, NULL, NULL);
if (ret < 0)
{
cout << "selcet failure" << endl;
}
}
public:
void Join_Read(int connfd)
{
FD_SET(connfd, &readfds_);
}
void Join_Write(int connfd)
{
FD_SET(connfd, &writefds_);
}
void Join_Except(int connfd)
{
FD_SET(connfd, &exceptfds_);
}
public:
int Judge_Read(int connfd)
{
return FD_ISSET(connfd, &readfds_);
}
int Judge_Write(int connfd)
{
return FD_ISSET(connfd, &writefds_);
}
int Judge_Except(int connfd)
{
return FD_ISSET(connfd, &exceptfds_);
}
public:
void Zero()
{
FD_ZERO(&readfds_);
FD_ZERO(&exceptfds_);
FD_ZERO(&writefds_);
}
private:
fd_set readfds_;
fd_set writefds_;
fd_set exceptfds_;
};
主文件tcp_select.cpp
#include "tcp_select.h"
#include
void Deal_connect(Socket_Ser& ser, Select& sel, list& mylist)
{
int sockfd = ser.Get_socket();
for (auto it = mylist.begin();it!=mylist.end();it++)
{
if (sel.Judge_Read(*it))//当发生读事件
{
if (*it == sockfd)//服务器接受新连接
{
int connfd = ser.Accept();
mylist.push_front(connfd);
cout << "new client:" << connfd << " connet!" << endl;
}
else//客户端发送消息
{
char buffer[BUFF_SIZE];
memset(buffer, '\0', BUFF_SIZE);
int ret = recv(*it, buffer, BUFF_SIZE - 1, 0);
if (strcmp(buffer, "end") == 0) break;
cout << "recv form " << *it << ":" << buffer<< endl;
send(*it, "ok", 2, 0);
}
}
}
}
int Get_max(Select& sel,list& mylist)//得到最大的文件描述符值,并重新设置读事件数组
{
sel.Zero();
auto it = mylist.begin();
int max = *it;
for (; it != mylist.end(); it++)
{
sel.Join_Read(*it);
if (*it > max)
{
max = *it;
}
}
return max;
}
int main(int argc,char* argv[])
{
if (argc <= 1)
{
cout << "error! please input again" << endl;
return 1;
}
list cli_list;//存储连接的套接字
Socket_Ser ser(argv[1]);
Select sel;
cli_list.push_front(ser.Get_socket());
while (1)
{
int maxfd = Get_max(sel,cli_list);
sel.Using_Select(maxfd);
Deal_connect(ser, sel, cli_list);
}
return 0;
}
参考文献
[1]游双.Linux高性能服务器编程.机械工业出版社,2043.5.
Linux网络编程之IO复用——Select相关教程
Linux命令
Linux命令 1、压缩和解压缩 # 压缩 createtar -zcvf 压缩包名 压缩的文件和目录tar -zcvf 1.tar.gz * # 把当前目录下所有文件和目录 打包压缩成1.tar.gz # 解压缩 xtar -zxvf -z # gzip 算法格式-c # 打包-x # 解包-v # 显示详细过程-f # 指定压缩包名字 2、
Linux常用命令一一网络通讯篇
Linux常用命令一一网络通讯篇 Linux常用命令中,要想查看一下网络通讯信息,比如说ifconfig、netstat等,如果你还不会使用,那么最好花几分钟学习一下。 telnet 远端登入 功能:执行telnet指令开启终端机阶段作业,并登入远端主机。 telnet 命令用于远端登入
Linux之重定向
Linux之重定向 1:标准输出 重定向 :把标准输出 重定向到新文件 标准错误不适用 会覆盖已有文件 无屏显,内容都存到httpd 2::追加符,不覆盖原有文件。 1) tee:类似,但只能从管道接收数据 tee -a:类似追加符 tee和tee -a都会屏显,号,追加符不屏显 tee和的
linux centos下安装最新版Vim8.2
linux centos下安装最新版Vim8.2 linux centos下安装最新版Vim8.2 首先卸载低版本Vim yum remove vim 然后下载最新版Vim8.2文件 并上传 vim官网:https://www.vim.org/ Vim8.2下载:https://ftp.nluug.nl/pub/vim/unix/vim-8.2.tar.bz2 下载得到文件:vim-8.2
linux安装rabbitmq-记一次心酸历程
linux安装rabbitmq-记一次心酸历程 环境:linux-centos7.x-64位 JDK1.8 rabbitmq版本:rabbitmq-server-3.8.5-1.el7.noarch 附加:linux宝塔面板 参考: 1.https://www.cnblogs.com/hnusthuyanhua/p/13528217.html 2.https://www.cnblogs.com/shihaiming/p/11
Win7下使用U盘安装linux Ubuntu16.04双系统图文教程
Win7下使用U盘安装linux Ubuntu16.04双系统图文教程 Ubuntu 16.04LTS(长期支持版) 镜像: 下载地址: ( Ubuntu中国下载地址:) 根据自己计算机的配置信息下载(本人下载的是的 64位的) 百度下载 ultraISO软件安装并打开 (傻瓜式安装就可以了) 开始写入
Linux网络通信—Socket(TCP实现)
Linux网络通信—Socket(TCP实现) 一、socket概述 为了简化开发通信程序的工作,由Berkely学校开发了一套网络通信程序的API函数标准。 二、SOCKET分类 流式套接字(SOCK_STREAM) 流式的套接字可以提供可靠的、面向连接的通讯流。它使用了TCP协议。TCP 保证
Win10 在Microsoft Store安装Kali linux时,报错0x8007019e的解
Win10 在Microsoft Store安装Kali linux时,报错0x8007019e的解决方法 微软商店安装Kali linux 时报错如下: Installing, this may take a few minutes.WslRegisterDistribution failed with error: 0x8007019eThe Windows Subsystem for Linux optional corr