(四) 用poll实现TCP服务端

1.首先,大概说一下poll函数的工作大概:(包含在#include<poll.h>当中)

        poll 是通过监听一个结构体变量来对 文件描述符进行监听的(当然,socket也算是一个文件描述符)

        这个结构体的大概结构是:

       (!!如果不想了解poll工作原理的。可以直接拉下去看代码)

struct pollfd   //这个结构体一般会被定义为结构体数组
{
   int fd;        //要监听的文件描述符
   short events;  //期待fd发生的事件   ,其中 events把他设置成 POLLIN就够用了,表示检测该fd有没有可读数据
   short revents; //fd实际发生的事件
};		

而poll()是通过监听上述结构体数组 来监听一堆文件描述符的
2.再来了解poll()函数详细信息:
    (一)首先是他的输入参数:
            int poll(struct pollfd *name,int num,int timeout);
              一共有三个,分别是监听的结构体数组的数组名,实际监听着多少个结构体数组成员,阻塞时间控制参数。
             其中timeout参数 ==0,是非阻塞,== -1 是阻塞,>0是阻塞 xx毫秒
   (二)然后是他的返回值
          poll()函数的返回值   ,表示同时有响应的文件描述符个数。
接下来看看 poll()的大致使用例子:

3.给出代码:(在这之前先看看代码的大致实现逻辑)
首先把 监听套接字sock 和 标准输入 先放到 监听的结构体数组中
while(1)循环是必须的,在循环中,用poll()进行监听,然后判断是sock是否响应,是的话,就调用accept来获取连接进来的客户端的信息,并把接收到的socket放到结构体数组中。否则就继续往下(判断是否标准输入响应,是就读信息),再往下,判断是哪些客户端socket发生可读响应,有的话,就读。

//服务端
#include"myhead.h"
#define CLI_NUM 10
char rbuf[50];
char wbuf[50];
char ipbuf[50];
int recv_num;

struct pollfd pollfd[CLI_NUM];

void write_all(char *wbuf,int max)
{
	int i = 2;
	for(i;i<(max+2);i++)
	{
		write(pollfd[i].fd,wbuf,50);
	}
}

int main()
{

	int sockfd;
	int size,port;
	int new_sock;
	int on = 1;
	int i;
	
	int max = 0;
	int ret;
	struct sockaddr_in saddr;
	struct sockaddr_in caddr;
	size = sizeof(struct sockaddr_in);

	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(8888);
	saddr.sin_addr.s_addr = htonl(INADDR_ANY);

	sockfd = socket(AF_INET,SOCK_STREAM,0);
	setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));

	bind(sockfd,(struct sockaddr*)&saddr,sizeof(struct sockaddr));
	listen(sockfd,10);

	//init pollfd
	for(i=0;i<10;i++)    //把pollfd置 -1,当然不一定是-1,只是文件描述符不可能为负,所以就置成一个负数
	{
		pollfd[i].fd = -1;
	}

	

	pollfd[0].fd = sockfd;
	pollfd[0].events = POLLIN;

	pollfd[1].fd = STDIN_FILENO;
	pollfd[1].events = POLLIN;
	int len = sizeof(struct sockaddr);
	while(1)
	{
		puts("waiting poll");
		ret = poll(pollfd,max+2,-1);            //ret表示发生响应的文件描述符个数,接下来就要对这些响应的文件描述符都处理一次
		if(ret ==0)
		{
			perror("timeout");
			exit(-1);
		}

		if((pollfd[0].revents& POLLIN) == POLLIN)
		{
			puts("waiting accept");
			new_sock = accept(sockfd,(struct sockaddr*)&caddr,&len);
			
			write(new_sock,"get",4);
			if(new_sock>0)
			{
				//fcntl(new_sock,F_SETFL,fcntl(new_sock,F_GETFL)|O_NONBLOCK);
				printf("newfd =%d\n",new_sock);
				i = 2;
				for(i;i<CLI_NUM;i++)
				{
					if(pollfd[i].fd != -1)
						continue;
					else if(pollfd[i].fd == -1)
					{
						pollfd[i].fd = new_sock;
						pollfd[i].events = POLLIN;
						max +=1;
						printf("max=%d\n",max);
						ret-=1;                    //每处理一个就 -1
						if(ret <= 0 )              //要是响应的文件描述符都处理完了,就退出循环。
						{
							break;
						}
					
					}
				}
			}
		}

		if((pollfd[1].revents & POLLIN) == POLLIN)  //是否从键盘有信息读入
		{
			bzero(wbuf,50);
			scanf("%s",wbuf);
			write_all(wbuf,max);   
			ret-=1;                      //每处理一个就 -1
			if(ret<=0)
				continue;
		}

		for(i=2;i<CLI_NUM && pollfd[i].fd!=-1;i++)  //接收来自其他客户端的信息
		{
			
			if((pollfd[i].revents & POLLIN)==POLLIN)
			{
				
				puts("enter read");
				bzero(rbuf,50);
				recv_num = recv(pollfd[i].fd,rbuf,50,0);
				if(recv_num == 0)                          //当recv_num 为0,表示客户端已经退出了
				{
					printf("client close\n");
					close(pollfd[i].fd);
					pollfd[i].fd = -1;                 //既然客户端退出了,那原本该客户端占着的位置就重新置 -1
					continue;
				}
				printf("%s,from:%d\n",rbuf,pollfd[i].fd);
				write_all(rbuf,max);
				ret -=1;               
				if(ret<=0)
					break;
			}
		}
	
	}
}

接下来是客户端:
//客户端相对而言就比较简单
#include"myhead.h"
char rbuf[50];
char wbuf[50];
char ipbuf[50];

int main()
{
	int sockfd;
	int ret,port;
	struct pollfd pollfd[2];
	struct sockaddr_in saddr;
	int size = sizeof(struct sockaddr_in);
	bzero(&saddr,size);

	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(8888);
	saddr.sin_addr.s_addr = inet_addr("192.168.152.128");

	sockfd = socket(AF_INET,SOCK_STREAM,0);
	ret=connect(sockfd,(struct sockaddr*)&saddr,sizeof(struct sockaddr));
	if(ret == 0)
	{
		inet_ntop(AF_INET,(void*)&saddr.sin_addr.s_addr,ipbuf,50);
		port = ntohs(saddr.sin_port);
		printf("%s,%d\n",ipbuf,port);
	}
	pollfd[0].fd = STDIN_FILENO;
	pollfd[0].events = POLLIN;

	pollfd[1].fd = sockfd;
	pollfd[1].events = POLLIN;
	while(1)
	{
		poll(pollfd,2,-1);
		if((pollfd[0].revents & POLLIN)==POLLIN)
		{
			puts("message from keyboard");
			bzero(wbuf,50);
			scanf("%s",wbuf);
			write(sockfd,wbuf,50);
		}
	
		if((pollfd[1].revents & POLLIN)==POLLIN)
		{
			bzero(rbuf,50);
			read(sockfd,rbuf,50);
			printf("%s\n",rbuf);
		}
	}
	return 0;
}














评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值