关于epoll的介绍已经在这篇博客中介绍了 下面将展示这个具体实现
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <poll.h>
int main(int argc,char *argv[])
{
int udpfd = 0;
int ret = 0;
struct pollfd fds[2];//监测文件描述结构体数组:2个 状态分离 所以需要两个数组
//用于存放server和client端的地址
struct sockaddr_in saddr;
struct sockaddr_in caddr;
//初始化
bzero(&saddr,sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(8000);
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
bzero(&caddr,sizeof(caddr));
caddr.sin_family = AF_INET;
caddr.sin_port = htons(8000);
//创建套接字
if( (udpfd = socket(AF_INET,SOCK_DGRAM, 0)) < 0)
{
perror("socket error");
exit(-1);
}
//套接字端口绑字
if(bind(udpfd, (struct sockaddr*)&saddr, sizeof(saddr)) != 0)
{
perror("bind error");
close(udpfd);//绑定失败的话就直接关闭套接字
exit(-1);
}
printf("input: \"sayto 192.168.220.X\" to sendmsg to somebody\033[32m\n");
fds[0].fd = 0; //标准输入描述符 数组一
fds[1].fd = udpfd; //udp描述符 数组二
/*
//这里会展示pollfd结构体
struct pollfd{
int fd; //文件描述符
short events; //等待的事件
short revents; //实际发生的事件
};
*/
fds[0].events = POLLIN; // 普通或优先级带数据可读
fds[1].events = POLLIN; // 普通或优先级带数据可读
while(1)
{
// 监视并等待多个文件(标准输入,udp套接字)描述符的属性变化(是否可读)
// 没有属性变化,这个函数会阻塞,直到有变化才往下执行,这里没有设置超时
/*
成功时,poll() 返回结构体中 revents 域不为 0 的文件描述符个数;如果在超时前没有任何事件发生,poll()返回 0,失败时,poll() 返回 -1,
*/
write(1,"UdpQQ:",6);//向标准输出输出UdpQQ
ret = poll(fds, 2, -1); //监听两个数组 由于timeout参数设置为-1,所以说会一直等待到有时间发生
if(ret == -1){ // 出错
perror("poll()");
}
else if(ret > 0){ // 准备就绪的文件描述符
char buf[100] = {0}; //缓冲区
if( ( fds[0].revents & POLLIN ) == POLLIN ){ // 标准输入 通过位与操作 类似于select函数的ISSET
fgets(buf, sizeof(buf), stdin);//从键盘输入
buf[strlen(buf) - 1] = '\0';
if(strncmp(buf, "sayto", 5) == 0)//出现sayto 后 就将其后面的ip地址赋值给addr
{
char ipbuf[16] = "";
inet_pton(AF_INET, buf+6, &caddr.sin_addr);//给addr套接字地址再赋值.
printf("\rsay to %s\n",inet_ntop(AF_INET,&caddr.sin_addr,ipbuf,sizeof(ipbuf)));
continue;
}
else if(strcmp(buf, "exit")==0)//判断输入 exit 出现时候直接退出
{
close(udpfd);
exit(0);
}
sendto(udpfd, buf, strlen(buf),0,(struct sockaddr*)&caddr, sizeof(caddr));
}
else if( ( fds[1].revents & POLLIN ) == POLLIN ){ //udp套接字
struct sockaddr_in addr;
char ipbuf[INET_ADDRSTRLEN] = "";
socklen_t addrlen = sizeof(addr);
bzero(&addr,sizeof(addr));
//显示输出
recvfrom(udpfd, buf, 100, 0, (struct sockaddr*)&addr, &addrlen);
printf("\r\033[31m[%s]:\033[32m%s\n",inet_ntop(AF_INET,&addr.sin_addr,ipbuf,sizeof(ipbuf)),buf);
}
}
else if(0 == ret){ // 超时
printf("time out\n");
}
}
return 0;
}
关于怎样测试 参照
添加链接描述
下面加入关于tcp并发服务器(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>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <poll.h>
#include <errno.h>
#define OPEN_MAX 100
int main(int argc, char *argv[])
{
//1.创建tcp监听套接字
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
//2.绑定sockfd
struct sockaddr_in my_addr;
bzero(&my_addr, sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(8000);
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(sockfd, (struct sockaddr *)&my_addr, sizeof(my_addr));
//3.监听listen
listen(sockfd, 10);
//4.poll相应参数准备
struct pollfd client[OPEN_MAX];
int i = 0, maxi = 0;
for(;i<OPEN_MAX; i++)
client[i].fd = -1;//初始化poll结构中的文件描述符fd 初始化的时候将其都设置为-1
client[0].fd = sockfd;//需要监测的描述符
client[0].events = POLLIN;//普通或优先级带数据可读
//5.对已连接的客户端的数据处理
while(1)
{
write(1,"UdpQQ:",6);
int ret = poll(client, maxi+1, -1);//对加入poll结构体数组所有元素进行监测 timeout 设置位-1 就是阻塞等待
//5.1监测sockfd(监听套接字)是否存在连接
if((client[0].revents & POLLIN) == POLLIN )//进行位操作 判断是否有事件
{
struct sockaddr_in cli_addr;
int clilen = sizeof(cli_addr);
int connfd = 0;
//5.1.1 从tcp完成连接中提取客户端
connfd = accept(sockfd, (struct sockaddr *)&cli_addr, &clilen);
char buf[100] = {0}; //缓冲区
fgets(buf, sizeof(buf), stdin);//从键盘输入
buf[strlen(buf) - 1] = '\0';
if(strncmp(buf, "sayto", 5) == 0)//出现sayto 后 就将其后面的ip地址赋值给addr
{
char ipbuf[16] = "";
inet_pton(AF_INET, buf+6, &cli_addr.sin_addr);//给addr套接字地址再赋值.
printf("\rsay to %s\n",inet_ntop(AF_INET,&cli_addr.sin_addr,ipbuf,sizeof(ipbuf)));
continue;
}
else if(strcmp(buf, "exit")==0)//判断输入 exit 出现时候直接退出
{
close(connfd);
exit(0);
}
sendto(connfd, buf, strlen(buf),0,(struct sockaddr*)&caddr, sizeof(caddr));
//5.1.2 将提取到的connfd放入poll结构体数组中,以便于poll函数监测
for(i=1; i<OPEN_MAX; i++)//因为client[0]已经放了sockfd 所以从位置1开始
{
if(client[i].fd < 0)//如果这个位置是初始化的-1 那么说明这个位置空缺
{
client[i].fd = connfd;
client[i].events = POLLIN;
break;
}
}
//5.1.3 maxi更新
if(i > maxi)//如果i超出了最大值 就将i赋值给最大值
maxi = i;
//5.1.4 如果没有就绪的描述符,就继续poll监测,否则继续向下看
if(--ret <= 0)//如果poll没有监听到事件 那么返回继续监听
continue;
}
//5.2继续响应就绪的描述符
for(i=1; i<=maxi; i++)
{
if(client[i].fd < 0)
continue;
if(client[i].revents & (POLLIN | POLLERR))//通过位操作 判断是否有请求
{
int len = 0;
char buf[128] = "";
//5.2.1接受客户端数据
if((len = recv(client[i].fd, buf, sizeof(buf), 0)) < 0)
{
if(errno == ECONNRESET)//tcp连接超时、RST 判断是否报错
{
close(client[i].fd);//关闭这个文件描述符
client[i].fd = -1;//重新将这个位置置为-1
}
else
perror("read error:");//输出读取错误
}
else if(len == 0)//客户端关闭连接
{
close(client[i].fd);
client[i].fd = -1;
}
else//正常接收到服务器的数据
printf("\r\033[31m[%s]:\033[32m%s\n",inet_ntop(AF_INET,&cli_addr.sin_addr,ipbuf,sizeof(ipbuf)),buf);
//5.2.2所有的就绪描述符处理完了,就退出当前的for循环,继续poll监测
if(--ret <= 0)
break;
}
}
}
return 0;
}