【Linux 网络编程】TCP poll聊天程序

实现目标

【1】创建TCP服务器和客户端,实现简易聊天程序;
【2】单一进程,通过I/O复用poll函数实现;
【3】客户端/服务器任一结束,结束连接和对方进程。


poll函数

   poll函数和select函数调用的本质一样的,也是对所有监听文件描述符进行轮询,有事件发生则返回。与select不同的是,poll监听文件描述符数目没有限制,poll执行完不会清空文件描述符集合,也就是不需每次都重新装载文件描述符。因此,如果监听描述符数目大时,poll体现出来的效率要比select高。

函数原型

#include 

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

形参

参数 含义
fds 监听文件描述符事件集合
nfds 监听文件描述符数目
timeout 超时时间(毫秒),传入0立即返回;传入-1(INFTIM)为不超时阻塞

  其中fds是一个“struct pollfd”结构体,是实现poll I/O复用的关键。

 struct pollfd
 {
 	int fd;          /* 被监听文件描述符 */
 	short events;    /* 监听事件 */
 	short revents;   /* 监听返回的事件 */
 };

  监听事件是对需要监听的文件描述符进行事件注册,由用户设置;返回事件是用于存放poll返回的事件结果,如果poll返回值为负值(执行出错),返回事件是无效的,即使有相关事件置位,也不能使用该事件作为判断条件。

事件 含义 可作为设定事件(events) 可作为返回事件(revents)
POLLIN 普通或优先级带数据可读
POLLRDNORM 普通数据可读
POLLRDBAND 优先级带数据可读
POLLPRI 高优先级数据可读(比如TCP带外数据)
POLLOUT 数据可写
POLLWRNORM 普通数据可写
POLLWRBAND 优先级带数据可写
POLLRDHUP TCP连接被对端关闭,或者关闭了写操作
POPPHUP 发生挂起
POLLERR 发生错误
POLLNVAL 文件描述符没有打开

返回值

返回值 含义
负值 poll函数执行错误,错误原因存于errno中
正值 事件就绪文件描述符的总数量
0 等待超时,没有可读/写,或异常的文件

实现代码

服务器端(server)

#define _GNU_SOURCE 1		/* 使用“POLLRDHUP”宏需定义该宏 */

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

int main (int argc, char * argv[])
{
	int s_fd = 0, c_fd = 0, pid; 
	socklen_t addr_len;
	struct sockaddr_in s_addr, c_addr;
	char buf[1024];
	ssize_t size = 0;
	struct pollfd p_fds[5];  	/* 最大监听5个句柄 */
	int max_fd;
	int ret = 0;
	
	if(argc != 3)
	{
		printf("format error!\n");
		printf("usage: server 
\n"); exit(1); } /* 服务器端地址 */ s_addr.sin_family = AF_INET; s_addr.sin_port = htons(atoi(argv[2])); if(!inet_aton(argv[1], (struct in_addr *) &s_addr.sin_addr.s_addr)) { perror("invalid ip addr:"); exit(1); } /* 创建socket */ if((s_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket create failed:"); exit(1); } /* 端口重用,调用close(socket)一般不会立即关闭socket,而经历“TIME_WAIT”的过程 */ int reuse = 0x01; if(setsockopt(s_fd, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse, sizeof(int)) < 0) { perror("setsockopt error"); close(s_fd); exit(1); } /* 绑定地址 */ if(bind(s_fd, (struct sockaddr*)&s_addr, sizeof(s_addr)) < 0) { perror("bind error"); close(s_fd); exit(1); } /* 监听socket */ if(listen(s_fd, 5) < 0) { perror("listen error"); close(s_fd); exit(1); } addr_len = sizeof(struct sockaddr); /* poll 监听参数 */ for(ret = 0;ret < 5;ret++) { p_fds[ret].fd = -1; } p_fds[0].fd = STDIN_FILENO; p_fds[0].events = POLLIN; p_fds[0].revents = 0; p_fds[1].fd = s_fd; p_fds[1].events = POLLIN; p_fds[1].revents = 0; max_fd = 1; for(;;) { ret = poll(p_fds,max_fd+1,2000); /* 监听5个句柄;-1表示阻塞,不超时 */ if(ret < 0) { perror("poll error"); break; } else if(ret == 0) { continue; } if(((p_fds[1].revents&POLLIN) == POLLIN) && (p_fds[1].fd == s_fd)) { if(c_fd) { continue; } printf("waiting client connecting\r\n"); c_fd = accept(s_fd, (struct sockaddr*)&c_addr, (socklen_t *)&addr_len); if(c_fd < 0) { perror("accept error"); close(s_fd); break; } else { printf("connected with ip: %s and port: %d\n", inet_ntop(AF_INET,&c_addr.sin_addr, buf, 1024), ntohs(c_addr.sin_port)); } /* 将客户端加入监听集合中 */ p_fds[2].fd = c_fd; p_fds[2].events = POLLIN | POLLRDHUP | POLLPRI; p_fds[2].revents = 0; max_fd = 2; } if((p_fds[0].revents&POLLIN) == POLLIN && p_fds[0].fd == STDIN_FILENO) { fflush(stdout); memset(buf, 0, sizeof(buf)); size = read(STDIN_FILENO, buf, sizeof(buf) - 1); if(size > 0) { buf[size-1] = '\0'; } else { perror("read stdin error"); break; } if(!strncmp(buf, "quit", 4)) { printf("close the connect!\n"); break; } if(buf[0] == '\0') { printf("please enter message to send:\n"); continue; } size = write(c_fd, buf, strlen(buf)); if(size <= 0) { printf("message'%s' send failed!errno code is %d,errno message is '%s'\n",buf, errno, strerror(errno)); break; } printf("please enter message to send:\n"); } if((p_fds[2].revents&POLLRDHUP) == POLLRDHUP && p_fds[2].fd == c_fd) {/* disconnect */ printf("client disconnect!\n"); break; } if((p_fds[2].revents&POLLIN) == POLLIN && p_fds[2].fd == c_fd) { memset(buf, 0, sizeof(buf)); size = read(c_fd, buf, sizeof(buf) - 1); if(size > 0) { printf("message recv %dByte: \n%s\n",size,buf); } else if(size < 0) { printf("recv failed!errno code is %d,errno message is '%s'\n",errno, strerror(errno)); break; } else { printf("client disconnect!\n"); break; } } } close(s_fd); close(c_fd); return 0; }

客户端(client)

#define _GNU_SOURCE 1		/* 使用“POLLRDHUP”宏需定义该宏 */

#include 
#include 
#include 
#include 
#include  
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

int main (int argc, char * argv[])
{ 
	int c_fd,pid; 
	int ret = 0;
	struct sockaddr_in s_addr;
	socklen_t addr_len;
	char buf[1024]; 
	ssize_t size;
	fd_set s_fds;
	struct pollfd p_fds[5];  	/* 最大监听5个句柄 */
	int max_fd; 				/* 监听文件描述符中最大的文件号 */

	if(argc != 3)
	{
		printf("format error!\n");
		printf("usage: client 
\n"); exit(1); } /* 创建socket */ c_fd = socket(AF_INET, SOCK_STREAM, 0); if(c_fd < 0) { perror("socket create failed"); return -1; } /* 服务器端地址 */ s_addr.sin_family = AF_INET; s_addr.sin_port = htons(atoi(argv[2])); if(!inet_aton(argv[1], (struct in_addr *) &s_addr.sin_addr.s_addr)) { perror("invalid ip addr"); exit(1); } /* 连接服务器*/ addr_len = sizeof(s_addr); ret = connect(c_fd, (struct sockaddr*)&s_addr, addr_len); if(ret < 0) { perror("connect server failed"); exit(1); } /* poll 监听参数 */ for(ret = 0;ret < 5;ret++) { p_fds[ret].fd = -1; } p_fds[0].fd = STDIN_FILENO; p_fds[0].events = POLLIN; p_fds[0].revents = 0; p_fds[1].fd = c_fd; p_fds[1].events = POLLIN | POLLRDHUP | POLLPRI; p_fds[1].revents = 0; max_fd = 1; for(;;) { ret = poll(p_fds,max_fd+1,-1); /* 监听5个句柄;-1表示阻塞,不超时 */ if(ret < 0) { perror("poll error"); break; } else if(ret == 0) { continue; } if((p_fds[0].revents&POLLIN) == POLLIN && p_fds[0].fd == STDIN_FILENO) { fflush(stdout); memset(buf, 0, sizeof(buf)); size = read(STDIN_FILENO, buf, sizeof(buf) - 1); if(size > 0) { buf[size-1] = '\0'; } else { perror("read stdin error"); break; } if(!strncmp(buf, "quit", 4)) { printf("close the connect!\n"); break; } if(buf[0] == '\0') { printf("please enter message to send:\n"); continue; } size = write(c_fd, buf, strlen(buf)); if(size <= 0) { printf("message'%s' send failed!errno code is %d,errno message is '%s'\n",buf, errno, strerror(errno)); break; } printf("please enter message to send:\n"); } if((p_fds[1].revents&POLLRDHUP) == POLLRDHUP && p_fds[1].fd == c_fd) {/* disconnect */ printf("server disconnect!\n"); break; } if((p_fds[1].revents&POLLIN) == POLLIN && p_fds[1].fd == c_fd) { memset(buf, 0, sizeof(buf)); size = read(c_fd, buf, sizeof(buf) - 1); if(size > 0) { printf("message recv %dByte: \n%s\n",size,buf); } else if(size < 0) { printf("recv failed!errno code is %d,errno message is '%s'\n",errno, strerror(errno)); break; } else { printf("server disconnect!\n"); break; } } } close(c_fd); return 0; }

执行效果
【Linux 网络编程】TCP poll聊天程序_第1张图片

你可能感兴趣的:(C,linux网络编程,Linux应用编程)