基本套接字编程(4) -- poll篇
1. poll技术
1.1 函数原型
include <poll.h> int poll ( struct pollfd * fds, unsigned int nfds, int timeout);
返回:若有就绪描述符则为其数目,若超时则为0,若出错则为-1
第一个参数:是指向一个结构数组第一个元素的指针。每个数组元素都是一个pollfd结构,用于指定测试某个给定描述符fd的条件。
- POLLIN 普通或优先级带数据可读。
- POLLRDNORM 普通数据可读。
- POLLRDBAND 优先级带数据可读。
- POLLPRI 高优先级数据可读。
- POLLOUT 普通数据可写。
- POLLWRNORM 普通数据可写不会导致阻塞。
- POLLWRBAND 优先级带数据可写。
- POLLMSGSIGPOLL 消息可用。
此外,只能用于revents域中的标志还有:
- POLLER 指定的文件描述符发生错误。
- POLLHUP 指定的文件描述符挂起事件。
- POLLNVAL 指定的文件描述符非法。
这些事件在events域中无意义,因为它们在合适的时候总是会从revents中返回。
- POLLIN | POLLPRI等价于select()的读事件;
- POLLOUT |POLLWRBAND等价于select()的写事件;
- POLLIN等价于POLLRDNORM |POLLRDBAND,而POLLOUT则等价于POLLWRNORM
例如,要同时监视一个文件描述符是否可读和可写,我们可以设置 events为POLLIN |POLLOUT。在poll返回时,我们可以检查revents中的标志,对应于文件描述符请求的events结构体。如果POLLIN事件被设置,则文件描述符可以被读取而不阻塞。如果POLLOUT被设置,则文件描述符可以写入而不导致阻塞。这些标志并不是互斥的:它们可能被同时设置,表示这个文件描述符的读取和写入操作都会正常返回而
| timeout参数值 | 说明 |
| INFTIM 0 >0 |
永远等待 立即返回,不阻塞进程 等待指定数目的毫秒数 |
1.2 返回值和错误代码
(1)成功时,poll()返回结构体中revents域不为0的文件描述符个数;
- EBADF 一个或多个结构体中指定的文件描述符无效。
- EFAULTfds 指针指向的地址超出进程的地址空间。
- EINTR 请求的事件之前产生一个信号,调用可以重新发起。
- EINVALnfds 参数超出PLIMIT_NOFILE值。
- ENOMEM 可用内存不足,无法完成请求。
回顾在select技术中,我们就常量FD_SETSIZE以及每个描述符集中最大描述符数目相比每个进程中最大描述符数目展开讨论。而在poll技术中,便不再有此问题,因为分配一个pollfd结构的数组并把该数组中元素的数目通知内核成了调用者的责任。内核不再需要知道类似fd_set的固定大小的数据类型。
2. TCP回射程序实例
2.1 server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <sys/socket.h>
#include <poll.h>
#include <limits.h> /*for OPEN_MAX*/
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <fcntl.h> #ifndef OPEN_MAX
#define OPEN_MAX 1024
#endif #ifndef INFTIM
#define INFTIM -1
#endif #define PORT 8888
#define MAX_LINE 2048
#define LISTENQ 20 int main(int argc , char **argv)
{
int i, maxi, listenfd, connfd, sockfd; int nready; ssize_t n, ret; struct pollfd client[OPEN_MAX]; char buf[MAX_LINE]; socklen_t clilen; struct sockaddr_in servaddr , cliaddr; /*(1) 得到监听描述符*/
listenfd = socket(AF_INET , SOCK_STREAM , 0); /*(2) 绑定套接字*/
bzero(&servaddr , sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(PORT); bind(listenfd , (struct sockaddr *)&servaddr , sizeof(servaddr)); /*(3) 监听*/
listen(listenfd , LISTENQ); /*(4) 设置poll*/
client[0].fd = listenfd;
client[0].events = POLLRDNORM;
for(i=1 ; i<OPEN_MAX ; ++i)
{
client[i].fd = -1;
}//for
maxi = 0; /*(5) 进入服务器接收请求死循环*/
while(1)
{
nready = poll(client , maxi+1 , INFTIM); if(client[0].revents & POLLRDNORM)
{
/*接收客户端的请求*/
clilen = sizeof(cliaddr); printf("\naccpet connection~\n"); if((connfd = accept(listenfd , (struct sockaddr *)&cliaddr , &clilen)) < 0)
{
perror("accept error.\n");
exit(1);
}//if printf("accpet a new client: %s:%d\n", inet_ntoa(cliaddr.sin_addr) , cliaddr.sin_port); /*将客户链接套接字描述符添加到数组*/
for(i=1 ; i<OPEN_MAX ; ++i)
{
if(client[i].fd < 0)
{
client[i].fd = connfd;
break;
}//if
}//for if(OPEN_MAX == i)
{
perror("too many connection.\n");
exit(1);
}//if /*该描述符等待的事件*/
client[i].events = POLLRDNORM;
if(i > maxi)
maxi = i; if(--nready < 0)
continue;
}//if for(i=1; i<=maxi ; ++i)
{
if((sockfd = client[i].fd) < 0)
continue;
/*该链接描述符实际发生的事件*/
if(client[i].revents & (POLLRDNORM | POLLERR))
{
/*处理客户请求*/
printf("\nreading the socket~~~ \n"); bzero(buf , MAX_LINE);
if((n = read(sockfd , buf , MAX_LINE)) <= 0)
{
close(sockfd);
client[i].fd = -1;
}//if
else{
printf("clint[%d] send message: %s\n", i , buf);
if((ret = write(sockfd , buf , n)) != n)
{
printf("error writing to the sockfd!\n");
break;
}//if
}//else
if(--nready <= 0)
break;
}//if
}//for
}//while
exit(0);
}
2.2 client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <fcntl.h> #define PORT 8888
#define MAX_LINE 2048 int max(int a , int b)
{
return a > b ? a : b;
} /*readline函数实现*/
ssize_t readline(int fd, char *vptr, size_t maxlen)
{
ssize_t n, rc;
char c, *ptr; ptr = vptr;
for (n = 1; n < maxlen; n++) {
if ( (rc = read(fd, &c,1)) == 1) {
*ptr++ = c;
if (c == '\n')
break; /* newline is stored, like fgets() */
} else if (rc == 0) {
*ptr = 0;
return(n - 1); /* EOF, n - 1 bytes were read */
} else
return(-1); /* error, errno set by read() */
} *ptr = 0; /* null terminate like fgets() */
return(n);
} /*普通客户端消息处理函数*/
void str_cli(int sockfd)
{
/*发送和接收缓冲区*/
char sendline[MAX_LINE] , recvline[MAX_LINE];
while(fgets(sendline , MAX_LINE , stdin) != NULL)
{
write(sockfd , sendline , strlen(sendline)); bzero(recvline , MAX_LINE);
if(readline(sockfd , recvline , MAX_LINE) == 0)
{
perror("server terminated prematurely");
exit(1);
}//if if(fputs(recvline , stdout) == EOF)
{
perror("fputs error");
exit(1);
}//if bzero(sendline , MAX_LINE);
}//while
} int main(int argc , char **argv)
{
/*声明套接字和链接服务器地址*/
int sockfd;
struct sockaddr_in servaddr; /*判断是否为合法输入*/
if(argc != 2)
{
perror("usage:tcpcli <IPaddress>");
exit(1);
}//if /*(1) 创建套接字*/
if((sockfd = socket(AF_INET , SOCK_STREAM , 0)) == -1)
{
perror("socket error");
exit(1);
}//if /*(2) 设置链接服务器地址结构*/
bzero(&servaddr , sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(PORT);
if(inet_pton(AF_INET , argv[1] , &servaddr.sin_addr) < 0)
{
printf("inet_pton error for %s\n",argv[1]);
exit(1);
}//if /*(3) 发送链接服务器请求*/
if(connect(sockfd , (struct sockaddr *)&servaddr , sizeof(servaddr)) < 0)
{
perror("connect error");
exit(1);
}//if /*调用消息处理函数*/
str_cli(sockfd);
exit(0);
}
2.3 运行结果
服务器端:
基本套接字编程(4) -- poll篇的更多相关文章
- 基本套接字编程(3) -- select篇
1. I/O复用 我们学习了I/o复用的基本知识,了解到目前支持I/O复用的系统调用有select.pselect.poll.epoll.而epoll技术以其独特的优势被越来越多的应用到各大企业服务器 ...
- 基本套接字编程(7) -- udp篇
1. UDP概述 UDP 是User Datagram Protocol的简称, 中文名是用户数据报协议,是OSI(Open System Interconnection,开放式系统互 ...
- 基本套接字编程(1) -- tcp篇
1. Socket简介 Socket是进程通讯的一种方式,即调用这个网络库的一些API函数实现分布在不同主机的相关进程之间的数据交换. 几个定义: (1)IP地址:即依照TCP/IP协议分配给本地主机 ...
- 网络编程[第二篇]基于udp协议的套接字编程
udp协议下的套接字编程 一.udp是无链接的 不可靠的 而上篇的tcp协议是可靠的,会有反馈信息来确认信息交换的完成与否 基于udp协议写成的服务端与客户端,各司其职,不管对方是否接收到信息, ...
- 入门级:怎么使用C#进行套接字编程(一)
翻译一篇简单的文章学习下基础,此文针对我等对socket只听说未尝试阶段的水平. How to C# Socket programming C#通过他的命名空间像System.Net和System.N ...
- 【unix网络编程第三版】阅读笔记(二):套接字编程简介
unp第二章主要将了TCP和UDP的简介,这些在<TCP/IP详解>和<计算机网络>等书中有很多细致的讲解,可以参考本人的这篇博客[计算机网络 第五版]阅读笔记之五:运输层,这 ...
- Linux Socket 原始套接字编程
对于linux网络编程来说,可以简单的分为标准套接字编程和原始套接字编程,标准套接字主要就是应用层数据的传输,原始套接字则是可以获得不止是应用层的其他层不同协议的数据.与标准套接字相区别的主要是要开发 ...
- C++网络套接字编程TCP和UDP实例
原文地址:C++网络套接字编程TCP和UDP实例作者:xiaojiangjiang 1. 创建一个简单的SOCKET编程流程如下 面向有连接的套接字编程 服务器: 1) 创建套接字(so ...
- Python黑帽编程2.8 套接字编程
Python黑帽编程2.8 套接字编程 套接字编程在本系列教程中地位并不是很突出,但是我们观察网络应用,绝大多数都是基于Socket来做的,哪怕是绝大多数的木马程序也是如此.官方关于socket编程的 ...
- 探索UDP套接字编程
UDP和TCP处于同一层网络模型中,也就是运输层,基于二者之上的应用有很多,常见的基于TCP的有HTTP.Telnet等,基于UDP有DNS.NFS.SNMP等.UDP是无连接,不可靠的数据协议服务, ...
随机推荐
- vc6开发ActiveX并发布全攻略(三)(转)
一.环境: windows xp sp3 Microsoft VC++ 6.0 二.制作文件 打开iexpress.exe(windows提供的一个向导式cab制作工具,位置:C:\WINDOWS\s ...
- (String) 205.Isomorphic Strings
Given two strings s and t, determine if they are isomorphic. Two strings are isomorphic if the chara ...
- JS中数组Array的用法
js数组元素的添加和删除一直比较迷惑,今天终于找到详细说明的资料了. var arr = new Array(); // 初始化数组arr[0] = "aaa";arr[1] = ...
- 45. Scramble String
Scramble String Given a string s1, we may represent it as a binary tree by partitioning it to two no ...
- Oracle中的表构造导出到word Sql语句
select * from ( SELECT t1.Table_Name AS "表名称", t3.comments AS "表说明", t1.Column_ ...
- 剑指Offer:面试题28——字符串的排列(java实现)(待序)
问题描述: 输入一个字符串,按字典序打印出该字符串中字符的所有排列.例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba. 结果请按字母 ...
- Linux ToolChain (二) --- Linker (1)链接选项 -L -rpath -rpath-link
一.动态库的链接和链接选项-L,-rpath-link,-rpath (1). 现代连接器在处理动态库时将链接时路径(Link-time path)和运行时路径(Run-time path)分开, 用 ...
- oracle分配权限:一个用户访问另一个用户的表
相当于alias(别名),比如把user1.table1在user2中建一个同义词table1 create synonym table1 for user1.table1; 这样当你在user2中查 ...
- Qlikview 的服务器
服务器管理 1 , Create a job 1.1 转到 Documents 分页 1.2 从左边目录搜索到 需要执行Job的qvw报表,如 "getting start.qvw" ...
- DataAdapter与DataSet的使用
1.创建数据库连接: 2.创建数据适配器(Adapter); 3.创建容器数据集(DataSet); 4.从数据集中取出指定表: 5.遍历表数据并输出: using System; using Sys ...