fcntl
#include<stdio.h>
#include<fcntl.h>
#include<errno.h>
#include<unistd.h>
void SetNonBlock(int fd)
{
int f1 = fcntl(fd,F_GETFL); //获取文件描述符的属性,非阻塞读取
if(f1 < 0)
{
perror("fcntl error!\n");
return;
}
fcntl(fd,F_SETFL,f1 | O_NONBLOCK);
}
int main()
{
char c = 0;
SetNonBlock(0);//标准输入
while(1)
{
sleep(1);//每隔1秒读
ssize_t s = read(0,&c,1);//读的内容放到接受缓冲区c,读1个,返回值:读的字节数
if(s > 0)
{
printf("%c\n",c);
}
else if(s < 0 && errno == EAGAIN)//EAGAIN=再次尝试=try again=宏=11
{
printf("read cond not ok!\n");//读取条件没有准备好,缓冲区里无数据
}
else
{
perror("read error!\n");
}
printf(".........................\n");
}
return 0;
}
文件描述符:从0开始的连续小整数。
Select
Server.cc:
#include"SelectServer.hpp"
void Usage(std::string proc)
{
cout << "Usage :\n\t" << proc << "port"<< endl;
}
Server port,只输入端口号,无IP
int main(int argc,char *argv[])
{
if(argc != 2)
{
Usage(argv[0]);
exit(1);
}
//argv[1])=端口号
SelectServer *ssvr = new SelectServer(atoi(argv[1]));
ssvr->InitServer();
ssvr->Start();
return 0;
}
Sock.hpp
#pragma once
#include<stdlib.h>
#include<strings.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/select.h>
#include<iostream>
#include<string>
using namespace std;
#define BACKLOG 5
class Sock
{
public:
static int Socket()
{
int sock = socket(AF_INET,SOCK_STREAM,0);
if(sock < 0)
{
cerr << "socket error!" << endl;
exit(2);
}
return sock;
}
static void Bind(int sock,int port)
{
struct sockaddr_in local;
bzero(&local,sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(port);
local.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(sock,(struct sockaddr*)&local,sizeof(local)) < 0)
{
cerr << "bind error!"<< endl;
exit(3);
}
}
static void Listen(int sock)
{
if(listen(sock,BACKLOG) < 0)
{
cerr << "listen error!"<< endl;
exit(4);
}
}
static int Accept(int sock)
{
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
int fd = accept(sock,(struct sockaddr*)&peer,&len);
if(fd < 0)
{
cerr << "accept error!"<<endl;
}
return fd;
}
static void SetsockOpt(int sock)
{
//server:主动断开连接的时候会进入time_wait状态,所以要端口复用
int opt = 1;
setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
}
};
SelectServer.hpp
#pragma once
#include"Sock.hpp"
#define DFL_PORT 8080
#define NUM (sizeof(fd_set)*8)//比特位个数
#define DFL_FD -1
class SelectServer
{
private:
int port;
int lsock;
//辅助的数组:保存已经打开的文件描述符
int fd_array[NUM];
public:
SelectServer(int _p = DFL_PORT)//构造
:port(_p)
{
}
void InitServer()
{
for(int i = 0;i < NUM;++i)
{
//初始化辅助数组
fd_array[i] = DFL_FD;
}
lsock = Sock::Socket();
Sock::SetsockOpt(lsock); //将listen socket 设置为端口复用
Sock::Bind(lsock,port);
Sock::Listen(lsock);
fd_array[0] = lsock;
}
void AddFd2Array(int sock)
{
int i = 0;
for( ; i < NUM; i++)
{
if(fd_array[i] ==DFL_FD){
break;
}
}
一直来新链接,数组满了
if(i == NUM)
{
cerr << "fd array is full,close sock" << endl;
close(sock);关文件描述符
}
else
{
fd_array[i] = sock;
cout << "fd: " << sock << " add to select..."<< endl;
}
}
void DefFdFromArray(int index)
{
if(index >=0 && index < NUM){
fd_array[index] = DFL_FD;
}
}
//处理已经就绪的文件描述符rfds
void HandlerEvents(fd_set *rfds)
{
for(int i =0;i < NUM;++i)
{
//遍历合法文件描述符,非法=有的文件描述符可能已经关闭
if(fd_array[i] == DFL_FD)
{
continue;
}
//走到这里说明都是有效的文件描述符,FD_ISSET=判断该文件描述符是否属于rfds集合
if(FD_ISSET(fd_array[i],rfds))
{
//read ready=读事件就绪
//read ready就绪依旧存在两种情况,1:数据准备就绪,2:accept有新链接
if(fd_array[i] == lsock)
//link event accept有新链接
{
int sock = Sock::Accept(lsock);
if(sock >= 0){
//sock ok=文件描述符(连接)已建立,不一定立即发数据,不直接读取,否则可能阻塞,添加到数组AddFd2Array
cout << "get a new link ... "<< endl;
AddFd2Array(sock);有效文件描述符放入数组
}
}
//date ready event=数据准备就绪
else
{
char buffer[1024];
ssize_t s = recv(fd_array[i],buffer,sizeof(buffer),0);///读时非阻塞,因为recv的返回值=真实读到的大小,读的内容放入buffer
if(s > 0){
buffer[s] = 0;
cout << "client# " << buffer << endl;
}
else if(s == 0){
客户端已关闭连接,so我也关闭连接
cout << "client quit" << endl;
close(fd_array[i]);
//文件描述符从全局数组fd_array中删除
DefFdFromArray(i);
}
else{
cout << "fd error!"<< endl;
close(fd_array[i]);
DefFdFromArray(i);
}
}
}
}
}
//最开始启动Start后,只有1个listen socket套接字,把listen socket(lsock)套接字添加到select
void Start()
{
int maxfd = DFL_FD;
for(;;)
{
//每1次都要重新的去设置位图结构的fd_set
fd_set rfds;//rfds=对象、变量、在栈
FD_ZERO(&rfds);
cout << "fd_array: ";
for(int i = 0;i < NUM; i++)
{
if(fd_array[i] != DFL_FD)
{
cout <<fd_array[i]<< " ";
FD_SET(fd_array[i],&rfds);合法文件描述符添加到辅助数组
if(maxfd < fd_array[i])
{
更新最大的fd
maxfd = fd_array[i];
}
}
}
cout << endl;
//这个timeout也是一个输入、输出型参数,第一次会等待5s,但是由于第一次没有事件发生timeout减为0,第二次及以后都不在等待,所以要每次进来都设置一次
printf("begin select...\n");
//struct timeval timeout = {5,0};
switch(select(maxfd+1,&rfds,nullptr,nullptr, nullptr))
{
case 0:
cout << "timeout"<< endl;
break;
case -1:
cerr << "select error"<< endl;
break;
default:
//此时的select的文件描述符时大于0
//success
HandlerEvents(&rfds);
break;
}
}
}
~SelectServer()
{
}
};
makefile:
server:server.cc
g++ -o $@ $^ -std=c++11
.PHONY:clean
clean:
rm -f server
[s@VM-8-3-centos Select]$ ./Server 8081
fd_array: 3
begin select. . .
get a new link ...
fd: 4 add to select.. .
fd_array: 3 4
begin select. . .
client#^]
fd_array: 3 4
begin select. ..
client#你好
[s@VM-8-3-centos Select]$./Server 8081
fd_array: 3
begin select. . .
get a new link .
fd: 4 add to select. . .
fd_array: 3 4
begin select. . .
client#]
fd_array:3 4
begin select. ..
client#你好
poll
poll.cc
#include<iostream>
#include<poll.h>
#include<unistd.h>
using namespace std;
int main()
{
struct pollfd rfds;
rfds.fd = 0;
rfds.events = POLLIN;
//rfds.revents = 0 这个参数是系统自己返回的
char buf[1024] = {
0};
cout << "poll begin"<< endl;
while(true){
switch(poll(&rfds,1,1000)){
case 0://键盘没有输入时
cout << "time out!"<< endl;
break;
case -1:
cerr << "poll error"<< endl;
break;
default:
cout << "events happen!"<< endl;
if(rfds.fd == 0 && (rfds.revents & POLLIN)){
//0号文件描述符的读事件已经就绪
ssize_t s = read(0,buf,sizeof(buf));
buf[s] = 0;
cout << "echo# "<< buf << endl;
}
}
}
}
[shang@VM-0-8-centos make2]$ g++ poll.cc
[shang@VM-0-8-centos make2]$ ./a.out
poll begin
a
events happen!
echo# a
文章评论