主要讲解一个完整TCP客户、服务器程序所需要的基本套接字函数。所需函数如图4-1所示(图片来源于linux一站式编程):
一、基本函数
1. socket函数
socket可以看成是用户进程与内核网络协议栈的编程接口。
socket不仅可以用于本机的进程间通信,在进程间通信,用共享内存的方式是最快的,但是基于socket的通信移植性较好,在目前分布式几乎都是用的基于socket的进程间通信,还可以用于网络上不同主机的进程间通信。
#include <sys/socket.h>
原型: int socket(int family, int type, int protocal);
参数:
family:AF_INET,AF_INET6,AF_LOCAL。。。
type: SOCK_STREAM,SOCK_DGRAM。。。
protocal: 正常为0。
2. connect函数
用来建立与TCP服务器端的连接,3次握手的开始。
原型:
int connect(int sockfd, const struct socket *servaddr, socklen_t len);
参数:
sockfd:socket()返回值
servaddr:服务器套接字地址
len:sruct长度
3. bind函数
服务器端绑定众所周知的端口号,用于客户端发起连接
原型:
int bind(int sockfd, const struct socket *myaddr, socklen_t len);
参数:
如上
4. listen函数
listen函数不是阻塞式的,它只是告诉内核打开某端口监听,真正“监听”的是内核,而不是我们的服务端程序。
listen把第一个参数套接字转换成监听套接字
(1) 主动套接字,导致套接字状态CLOSED到LISTEN状态
(2)指定套接字排队的最大连接数(两个队列之和)
原型:
int listen(int socket, int backlog);
参数:
backlog: 两个队列之和,如下图所示
$ man listen看看,里面有一段话:
If the backlog argument is greater than the value in /proc/sys/net/core/somaxconn, then it
is silently truncated to that value; the default value in this file is 128. In kernels
before 2.4.25, this limit was a hard coded value, SOMAXCONN, with the value 128.
用http://www.cnblogs.com/xiaouisme/archive/2012/07/08/2581810.html这篇文章的结果:
当传参backlog的值< somaxconn时,已完成连结队列的数量最多就是backlog的值。未完成连结队列数量在10左右摇摆。
当传参backlog的值 >= somaxconn时,已完成连结队列的数量最多就是somaxconn。未完成连结队列数量同上。
结论噢:在ubuntu12.04上,backlog的值就是已完成连结队列的值,此值受限于somaxconn。
确切的说backlog应该是最大已经完成的连接数
5. accept函数
从已经连接的队列头取出一个已经完成的连接。
原型:
int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen)
参数:
cliaddr: 对端的协议地址,可以为空指针,表示对客户端信息不敢兴趣
addrlen: 值结果传递,struct的字节数
说明:
返回值为内核生成胡全新套接字描述符,表示与客户端的tcp连接,可以对该描述符读写。
6.close函数
关闭套接字,默认行为套接字标记为关闭, 立即返回(非阻塞)到调用进程,套接字描述符不能write,read。
TCP将尝试发送已经在缓冲区中的数据,可以用SO_LINGER改变这一行为。
原型:
int close(int sockfd) //成功返回0,否则返回-1
说明:
close函数只是相应的描述符引用计数减一。因此在并发服务器中,子进程中要关闭listendfd。
for(; ; ){
connfd = accept(...);
if((pid == fork()) == 0){
close(listenfd); //子进程关闭监听描述符自作主张
doit(connfd);
close(connfd);
}
close(connfd);//父进程关闭描述符
}
7. getsockname和getpeername函数
for(; ; ){
connfd = accept(...);
if((pid == fork()) == 0){
close(listenfd); //子进程关闭监听描述符自作主张
doit(connfd);
close(connfd);
}
close(connfd);//父进程关闭描述符
}
获取对应的描述符协议地址,必须是已经连接的描述符。
原型:
int getsockname(int fd, struct sockaddr * addr, socklen_t *addrlen);
int getpeername(int fd, struct sockaddr *addr, socklen_t *addrlen);