您的位置:首页 > 理论基础 > 计算机网络

UNIX网络编程之常用函数

2013-12-30 22:29 295 查看
1. 字节序转换函数

头文件<netinet/ih.h>

uint16_t htons(uint16_t host16bitvalue);

uint32_t htonl(uint32_t host32bitvalue);

uint16_t ntohs(uint16_t net16bitvalue);

uint32_t ntohl(uint32_t net32bitvalue);

2. 地址转换函数

头文件<arpa/inet.h>

2.1. IPv4定义的地址转换函数

//将ASCII字符串表示的点分十进制地址字符串转换为32-bit的网络字节序地址

//即转换后的地址即是网络字节序(big-endian)的32-bit整数

int inet_aton(const char *strptr,struct in_addr *addrptr);

//同inet_aton函数,区别在于255.255.255.255这个地址从编址上来说是一个

//合法的地址,但此函数返回错误,认为不合法

in_addr_t inet_addr(const char *strptr);

//功能与inet_aton正好相反

char* inet_ntoa(struct int_addr* inaddr);

2.2 IPv6定义的地址转换函数,兼容IPv4

//将ASCII字符串表示的点分十进制地址字符串转换为32-bit的网络字节序地址

int inet_pton(int family,const char* strptr,void *addrptr);

转换成功返回1,失败返回0

//与inet_pton正好相反

const char *inet_ntop(int family,const void *addrptr,char* strptr,size_t len);

注意strptr长度,建议为IPv4长度16,IPv6长度46,外部分配空间。如果成功,

返回值为strptr

其中"p"意指presentation的意思,"n"意指numericf

family为AF_INET或AF_INET6

3. 基本TCP套接字函数

头文件<sys/socket.h>

3.1 套接字对象创建函数socket(UDP也使用)

int socket(int family,int type,int protocol);

family:地址族,IPv4下编程用AF_INET

type:数据报类型

protocol:传输层协议类型:IPPROTO_TCP、IPPROTO_UDP、IPPROTO_SCTP

如果protocol设置为0,则将根据family和type的类型默认一个值,如

family为AF_INET,type为SOCK_STREAM,则默认为TCP协议,若type为SOCK_DGRAM,

则默认为UDP协议,注意:family与type的组合一定要是有效的,并非所有组合都是有效的。

如果创建成功,其返回一个很小的非负整数值,即大于等于0的数。

3.2 客户端connect函数

int connect(int sockfd,struct sockaddr* servaddr,socklen_t addrlen);

返回0表示成功,返回-1表失败,函数在连接成功,或者出现连接错后才返回。

TCP的三次握手在此函数中完成,函数成功返回0则已建立连接。如果从某个路由器回应了

一个"主机不可达"的ICMP错误,说明服务器没监听,或者地址有错,在有些异步连接的

情况下,函数立即返回,就需要根据具体的错误号作处理。

3.3 bind函数,绑定一个协议地址到一个套接字。

协议地址由一个IPv4地址或IPv6地址和一个公开的端口号组成。

int bind(int sockfd,const struct sockaddr *myaddr,socklen_t addrlen);

如果客户端或服务端没有调用此函数,则在客户端在调用connect或服务端调用listen

函数的时候,内核会自动分配一个临时端口号。一般来说,客户端无需调用,而由服务端调用。

如果在调用bind函数时,公开端口号设置为0,则内核分配一个临时端口号,如果IP地址写为0,

则由内核分配IP地址。

3.4 listen函数

int listen(int sockfd,int backlog);

主要是对backlog的理解,内核维持连接数队列的最大值。

内核维持着两个队列,一个是未完成连接的队列,即收到了SYN请求,但未完成三次握手,

另一个是已完成了三次握手的队列,即正式建立连接的队列,其状态均为ESTABLISHED。

注意不要把backlog设置为0,因为不同的系统对此值的实现是不一样的,由于历史原因,

有的代码设置为5,因为早期的网络应用并发量不大,可以满足需求,为了满足大并发量的需求,

如果新的操作系统或内核支持,应将backlog设置为更大,比如187-192之间。如果设置的值大于

内核支持的最大值,则系统自动切掉越出的值而不会报错,至于究竟要设置为多大没有一个标准。

如果两个队列已满(任何一个),则新来的SYN请求将被忽略而不发出任何回应,客户端只有超时重试。

如果还没有调用accept取出连接,但连接队列里的连接已有数据到来,则系统应该提供一个队列存放

到处来的数据,直到队列缓冲区满,此时,由于滑动窗口的作用,客户端不再发送数据,直到服务端

处理数据后可以接收数据。

3.5 accept函数

int accept(int sockfd,struct sockaddr * cliaddr,socklen_t *addrlen);

其中sockfd为监听套接字描述符,struct cliaddr为客户端地址如果对客户端地址不感兴趣,则将后面两参数设置为NULL,返回值为客户端套接字描述符。注意一个SOCKET选项TCP_DEFER_ACCEPT(linux中的选项)(FreeBSD中为SO_ACCEPTFILTER)defer accept,从字面上理解是推迟accept,实际上是当接收到第一个数据之后,才会创建连接。对于像HTTP等非交互式的服务器,这个很有意义,可以用来防御空连接攻击(只是建立连接,但是不发送任何数据)。

使用方法如下:

val = 5;

setsockopt(srv_socket->fd, SOL_TCP, TCP_DEFER_ACCEPT, &val, sizeof(val)) ;

里面 val 的单位是秒,注意如果打开这个功能,kernel 在 val 秒之内还没有收到数据,不会继续唤醒进程,而是直接丢弃连接。

如果服务器设置TCP_DEFER_ACCEPT选项后,服务器受到一个CONNECT请求后,三次握手之后,新的socket状态依然为SYN_RECV,而不是ESTABLISHED,操作系统不会Accept。

由于设置TCP_DEFER_ACCEPT选项之后,三次握手后状态没有达到ESTABLISHED,而是SYN_RECV。这个时候,如果客户端一直没有发送"数据"报文,服务器将重传SYN/ACK报文,

重传次数受net.ipv4.tcp_synack_retries参数控制,达到重传次数之后,才会再次进行setsockopt中设置的超时值,因此会出现SYN_RECV生存时间比设置值大一些的情况。

3.6 close函数

3.6 close函数

头文件<unistd.h>

int close(int sockfd);

调用close后,默认的做法是将套接字标记为关闭并立即返回,此后套接字标识符视为无效不再使用。

因TCP底层试图发送任何放在缓冲区的未发出数据,在close调用后,TCP连接就进入了四次挥手的中止环节。

如果要改变这种默认做法,必须要设置SO_LINGER选项。注意,调用close一次,其实是其引用记数减1,只

有当其引用记数减为0时,才真正的关闭套接字。关闭套接字后,未发送的数据继续发送到对方,但未读取的数据

将丢弃。

3.7 shutdown函数

头文件<sys/socket.h>

int shutdown(int sockfd,int howto);

正常中止一个连接是调用close函数,但是,调用shutdown函数可以避免调用close带来的两个限制。

1) 调用close只是减少了文件描述符的引用次数,仅当引用次数为零时才真正关闭连接,调用shutdown

可以立即初始化TCP连接的正常中止序列而不用考虑引用计数;

2) 调用close只能同时中止双向的连接,此时,如果对端还有数据需要发送过来,我们也无法接收。

shutdown函数的行为取决于howto参数

SHUT_RD:关闭读操作,任何调用读的操作都会失败,仍然保留到socket缓冲区中的数据将被丢弃。

SHUT_WR:关闭写操作,任何调用写的操作都会失败,但保留到socket缓冲区中未发出的数据将被

发送出去,并在最后一包数据的后面加上正常连接中止序列,调用此方法后无错误,则可确保数据

全部发送到对端。

SHUT_RDWR:相当于调用了两次shudown,一次SHUT_RD,一次SHUT_WR。

注意事项:在调用shutdown函数后,对于发送端(暂定为客户端),只能保证数据发送到了对端(暂订为服务端)并收到了应答,并不表明服务端已调用了read函数从缓冲区中读出了数据,如果需要明确知道服务端已读取了数据,需要在两端做一个简单的处理,即客户端在调用shutdown时,使用SHUT_WR参数以确保数据全部发出,但不关闭接收端,在shutdown后再调用read函数等待服务端正常关闭,当服务端read函数后,正常关闭连接,此时我们的客户端read会返回0,表明服务端读取了数据并处理,这实际上是利用了TCP协议的特点从应用的角度解决我们的问题。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: