网络字节顺序转换函数
不同机器存储数据的字节顺序不同,有大端模式(big-endian)和小端模式(little-endian)。TCP/IP协议规定在网络上传输数据必须采用大端模式(也即网络字节顺序)。Linux系统为大小端模式的转换提供了4个函数。其原型分别如下:
#include <netinet/in.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
htonl中的“h”代表host(主机),“n”代表net(网络),“l”代表long型,同理,“s”代表short型。
IP地址转换函数
通常人们习惯于使用字符串形式的网络地址,比如“198.22.104.3”。然而在网络上传输数据时,需要使用的是二进制形式,且为网络字节顺序的IP地址。Linux系统为网络地址的格式转换提供了一些列函数。原型如下:
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int inet_aton(const char *cp, struct in_addr *inp);
in_addr_t inet_network(const char *cp);
char * inet_ntoa(struct in_addr in);
- inet_aton()将参数cp所指向的字符串形式的IP地址转换为二进制的网络字节顺序的IP地址,转换后的结果存于参数inp所指向的空间中。
- inet_ntoa()将值为in的网络字节顺序形式的二进制IP地址转换成以“.”分隔的字符串形式,执行成功返回结果字符串的指针。
- inet_network()将参数cp所指向的字符串形式的网络地址转换为主机字节顺序形式的二进制IP地址。
#include <sys/types.h>
#include <sys/socket.h>
int getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen);
int setsockopt(int s, int level, int optname, const void *optval, socklen_t *optlen);
- 参数s为一个套接字。
- 参数level是进行套接字选项操作的层次。一般取SOL_SOCKET来进行与特定协议无关的操作。
- 参数optname是套接字选项的名称。
- 参数optval用来存放获得的套接字选项的值,或者是待设置的套接字选项的值。
- 参数optlen是该选项的长度。
SO_KEEPALIVE:如果没有设置此选项,那么及时TCP连接已经很长时间没有数据传输时,系统也不会检测这个连接是否还有效。对于服务器进程,如果某一个客户端非正常断开连接,则服务器进程将一直被阻塞等待。因此服务器端程序需要设置这个选项,如果某个客户端一段时间内没有反应则关闭该连接。
SOL_SOCKET还有若干其他选项,此处从略。
多路复用
在客户端/服务器模型中,服务器需要同时处理多个客户端的连接请求,此时就需要使用多路复用。实现多路复用最简单的方法是采用非阻塞方式套接字,服务器端不断地查询各个套接字的状态,如果有数据到达则读出数据,如果没有数据则查看下一个套接字。这种方法简单,但是,在轮询过程中浪费了大量的CPU实际,效率非常低下。
正确的做法是,服务器进程并不主动询问套接字的状态,而是向系统登记希望监视的套接字,然后阻塞。当套接字上有事件发生时(如有数据到达),系统通知服务器进程告知哪个套接字上发生了什么事件,服务器进程查询对应套接字并进行处理。套接字上没有事件发生时,服务器进程不会去查询套接字的状态,不会浪费CPU时间,提高了效率。使用select()函数可以实现这样的多路复用。该函数原型:
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
- 参数n是需要监视的文件描述符数,要监视的文件描述符值为0~n-1
- 参数readfds指定需要监视的可读文件描述符集合。当此集合中任一描述符上有数据到达时,系统将通知调用select函数的程序
- 参数writefds指定需要监视的可写文件描述符集合,当此集合中任一描述符可以发送数据时,调用进程将受到通知
- 参数exceptfds指定需要监视的异常文件描述符集合,当集合中任一描述符发生异常时,调用进程将受到通知
- 参数timeout指定了阻塞的时间,如果在此时间内监视的文件描述符上没任何事件发生,则函数select()将返回0;如果将timeout设置为NULL,则select()函数将一直阻塞;如果将timeout设为0,则此时相当于非阻塞方式,函数select()查询完文件描述符集合的状态后立即返回