I/O多路技术在下几种情况使用:
1.当一个客户端需要同时处理多个文件描述符的输入输出操作的时候(一般来说是标准的输入输出和网络套接字)
2.当程序需要同时运行多个套接字的操作的时候
3.如果一个TCP服务器程序同时处理正在侦听网络连接的套接字和已经连接好的套接字
4.如果一个服务器程序同时使用TCP和UDP协议
5.如果一个服务器同时使用多种服务并且每种服务可能使用不同的协议
当你一开始建立一个套接字描述符的时候,系统内核就被设置为阻塞状态。如果你不
想你的套接字描述符是处于阻塞状态的,那么你可以使用函数fcntl()。
#include <unistd.h>
#include <fcntl.h>
int fcntl (int fd, int cmd, long arg);
套接字选择项 select()函数
你写的服务器程序想监听客户端的连接,但是你同时又想从你以前已经建立过的连接
中来读取数据,Select()函数可以帮助你同时监视许多套接字
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int select(int numfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
参数:
numfds是readfds,writefds,exceptfds,中fd集合中文件描述符中最大的数字加上1
readfds中的fds集合将由select来监视是否可以读取
writefds中的fds集全将由select来监视是否可以写入
exceptfds中fds集合将由selcet来监视是否有例外发生
如果你想知道是是否可以从标准输入和一些套接字(sockfd)中读取数据,你就可以
把文件描述符和sockfd 加入readfds 中。numfds 的数值设成readfds 中文件描述符中最大的
那个加上一,也就是sockfd+1,当select()函数返回的时候,readfds 将会被修改用来告诉你哪一个文件描述符你可以用来读取数据。使用FD_ISSET() 宏,你可以选出select()函数执行的结果。
FD_ZERO(fd_set *set)将一个文件描述符集合清零
FD_SET(int fd, fd_set *set)将文件描述符fd 加入集合set 中。
FD_CLR(int fd, fd_set *set)将文件描述符fd 从集合set 中删除.
FD_ISSET(int fd, fd_set *set)测试文件描述符fd 是否存在于文件描述符set 中
timeout延迟时间设置,当时超过timeval参数所代表的时间长度,而还没有文件描述符满足要求,那么select()函数将返回
struct timeval
{
int tv_sec; //秒数
int tv_usec; //微秒,1秒=1000000微秒
};
注意:
1 如果你将 struct timeval 设置为0,则select()函数将会立即返回,同时返回在你的
集合中的文件描述符的状态。
2 如果你将 timeout 这个参数设置为NULL,则select()函数进入阻塞状态,除了等
待到文件描述符的状态变化,否则select()函数不会返回。
3.如果你的套接字描述符正在通过listen()函数侦听等待一个外来的网络连接,则你可以
使用select()函数(将套接字描述符加入readfds 集合中)来测试是否存在一个未经处理的新连接
TCP的带外数据
假设一个进程向一个TCP 套接字写入了N 个字节的数据,数据被TCP 套
接字的发送缓冲区缓存,等待被发送到网络上
进程使用以MSG_OOB 为参数的send()函数写入一个单字节的"带外数据",包
含一个ASCII 字符"a":
send(fd, “a”, 1, MSG_OOB);
/*****************************************************************/
Socket的地址查询函数
Socket的地址查询函数有很多, 分为主机(host), 网络(net), 协议(proto)和服务(serv), 这些函数完成各种地址查询功能. POSIX.1定义了两个新的函数: getaddrinfo和getnameinfo, 前者把主机名字和服务名字映射到一个地址, 后者将地址转换成主机名或服务器名.
这些函数返回的网络配置信息可能存放在许多地方. 它们可以保存在静态文件中(如/etc/hosts, /etc/services等), 或者可以由命名服务器(如DNS, NIS).
这些地址查询函数大致分为几类: 主机(host), 网络(net), 协议(proto), 服务(serv), 这几类的相关函数也是带有该类别的名字. 下面就这几类分别进行说明:
1. 主机信息查询:
struct hostent
{
char *h_name; /* name of host */
char **h_aliases; /* pointer to alternate host name array */
int h_addrtype; /* address type */
int h_length; /* length in bytes of address */
char **h_addr_list; /* pointer to array of network addresses */
...
...
};
头文件: <netdb.h>
函数原型:
struct hostent *gethostent();
void sethostent(int stayopen);
void endhostent();
说明:
gethostent: 打开数据文件, 如果数据文件打开则返回文件的下一个条目.
sethostent: 打开文件, 如果文件已打开, 将其回绕.
endhostent: 关闭文件.
2. 网络名字和网络号查询:
struct netent
{
char *n_name; /* network name */
char **n_aliases; /* pointer to alternate network name array */
int n_addrtype; /* address type */
uint32_t n_net; /* network number */
...
...
};
头文件: <netdb.h>
函数原型:
struct netent *getnetbyaddr(uint32_t net, int type);
struct netent *getnetbyname(const char *name);
struct netent *getnetent();
void setnetent(int stayopen);
void endnetent();
3. 协议名字和协议号查询:
struct protoent
{
char *p_name; /*protocol name */
char **p_aliases; /* pointer to alternate protocol name array */
int p_proto; /* protocol number */
...
...
};
头文件: <netdb.h>
函数原型:
struct protoent *getprotobyname(const char *name);
struct protoent *getprotobynumber(int proto);
struct protoent *getprotoent();
void setprotoent(int stayopen);
void endprotoent();
4. 服务查询:
struct servent
{
char *s_name; /*service name */
char **s_aliases; /* pointer to alternate service name array */
int s_port; /* port number */
char *s_proto; /* name of protocol */
...
...
};
头文件: <netdb.h>
函数原型:
struct servent *getservbyname(const char *name, const char *proto);
struct servent *getservbyport(int port, const char *proto);
struct servent *getservent();
void setservent(int stayopen);
void endservent();
5. getaddrinfo函数:
原型: int getaddrinfo(const char *restrict host,
const char *restrict service,
const struct addrinfo *restrict hint,
struct addrinfo **restrict res);
头文件: <sys/socket.h> <netdb.h>
返回值: 成功则返回0, 出错则返回非0错误代码.
参数:
host: 主机名字. 可以是节点名或者是点分十进制表示的主机地址.
service: 服务名字.
hint: 过滤地址的模板, 仅使用ai_family, ai_flags, ai_protocol和ai_socktype字段, 剩余字段必须设为0或NULL.
res: 保存查询结果的指针.
说明: 主机名字和服务名字可以都提供, 如果只提供一个, 另一个必须为空.
这个函数里面涉及到一个结构:
struct addrinfo
{
int ai_flags; /* customize behavior */
int ai_family; /* address family */
int ai_socktype; /* socket type */
int ai_protocol; /* protocol */
socklen_t ai_addrlen; /* length in bytes of address */
struct sockaddr *ai_addr; /* address */
char *ai_canonname; /* canonical name of host */
struct addrinfo *ai_next; /* net in list */
...
...
};
getaddrinfo函数返回一个该结构的链表, 我们可以看到该结构中含有一个next成员, 这些链表结点在不用之后, 都需要用freeaddrinfo来逐个释放.
原型: void freeaddrinfo(struct addrinfo *ai);
头文件: <sys/socket.h> <netdb.h>
addrinfo结构中的ai_flags有以下几种值:
AI_ADDRCONFIG: 查询配置的地址类型(IPv4或IPv6).
AI_ALL: 查找IPv4和IPv6地址(仅用于AI_V4MAPPED).
AI_CANONNAME: 需要一个规范名(而不是别名).
AI_NUMERICHOST: 以数字格式返回主机地址.
AI_NUMERICSERV: 以端口号返回服务.
AI_PASSIVE: socket地址用于监听绑定.
AI_V4MAPPED: 如果没有找到IPv6地址, 则返回映射到IPv6格式的IPv6地址.
如果getaddrinfo失败, 不能使用perror或strerror来生成错误信息, 应该使用gai_strerror将返回的错误码转换成错误信息:
原型: const char *gai_strerror(int error);
头文件: <netdb.h>
6. getnameinfo函数:
原型: int getnameinfo(const struct sockaddr *restrict addr, socklen_t alen,
char *restrict host, socklen_t hostlen,
char *restrict service, socklen_t servlen,
unsigned int flags);
头文件: <sys/socket.h> <netdb.h>
参数:
addr: socket地址.
alen: socket地址长度.
host: 主机名.
hostlen: 主机名长度.
service: 服务名.
servlen: 服务名长度.
flags: 指定转换的控制方式. 可以有如下几种:
NI_DGRAM: 服务基于数据报而非基于流.
NI_NAMEREQD: 如果找不到主机名字, 将其作为一个错误对待.
NI_NOFQDN: 对于本地主机, 仅返回完全限定域名的节点名部分.
NI_NUMERICHOST: 以数字形式而非名字返回主机地址.
NI_NUMERICSERV: 以数字形式而非名字返回服务地址(即端口号).
说明: 如果host或service非空, 它(们)将指向洋长度问该len字节的缓冲区用于存储返回的名. 如果为空则不返回该名.
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int inet_aton(const char *cp, struct in_addr *inp)
-----------------------------------------------------------------
函数执行成功时返回非零,转换结果存入指针inp指向的in_addr结构。这个结构定义我们在前面的文章里已经介绍过了。如果参数cp指向的IP地址不可用,则返回"0"。这就避免发生inet_addr()那样的问题。
#include <netdb.h>
struct servent * getservbyname(const char *servname, const char *protoname);
-----------------------------------------------------------------
它的作用就是转换指针servname指向的服务名为相应的整数表示的端口号,参数protoname表示服务使用的协议
HTTP(超文本传输协议)
客户端:超文本链接的页面
服务器端:每一个Web服务器上面都运行一个侦听TCP的80端口的进程来等待来自客户端的HTTP请求,当一个连接发生的时候,客户端发送一个请求,当服务器收到这个请求后,服务器将会客户端所请求的数据返回客户端。之后这个连接结束了。定义了请求和回应规则的协议称为'HTTP'.
URL组成 (HTPP://www.baidu.com/index.html为例)
协议名字,'HTTP://'
页面服务器名字(也可以是IP),‘www.baidu.com’
后面跟着是为页面在服务上面存放的绝对路径,'/index.html'
最后可能还跟服务器端口(可以省略,因为每一个协议都自己的标准端口)
当你需要获取一页面的时候,你可根据需要使用各种HTTP协议的方法:
GET 发出读取一个WEB页面的请求
HEAD 发出读取一个WEB页面头部的信息,并不请求真正的数据
PUT 发出存储一个WEB页面信息的指令
POST 为一个已经被命名的文件添加信息
DELETE 删除一个WEB页面
LINK 连接上两个存在的资源
UNLINK 将已经连上资源的连接终止