Elementary TCP socket
摘自:《Unix Network Programming, Volumn 3》chapter 4.
# include <sys/socket.h>
int socket (int family, int type, int protocol);
Family:协议族(AF_INET, AF_INET6, AF_LOCAL, AF_ROUTE, AF_KEY等)当前linux可支持更多协议族。
AF_KEY:支持加密保护的数据安全
type:协议类型(SOCK_STREAM, SOCK_DGRAM, SOCK_RAW)
protocol:大多数情况下为0,除了raw sockets
AF_UNIX区别于AF_LOCAL??????
Linux支持SOCK_PACKET类型,用于操作2层数据包(datalink)。packet(7)显示,不推荐使用
AF_ 在大多数系统中等同于 PF_
# include <sys/socket.h>
int connect(int sockfd, const struct sockaddr_in *addr, socketlen_t len);
Connect 主要执行tcp的3次握手(to be established),在发出ACK报文后,connect()就返回了。在此之前,不需要客户端执行bind,内核将自动为其指定ip及port
Connect的错误返回值:
1. 服务器不响应(主叫方发出SYN后,75s内无响应)ETIMEOUT
2. 服务器返回RST(服务器的被动端口未打开hard error)ECONNREFUSED
3. 中间路由器响应目标不可达(soft error),也必须等待75s,期间内核继续发送SYN包。EHOSTUNREACH ENETUNREACH
注意:如果connect返回错误,则该sockfd将不再可用,如需再次申请建立连接,必须:close(sockfd); socket(sockfd2); connect(sockfd2);
# include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen);
绑定(bind)用于指定本地sockaddr到套接口
服务器通常需要使用bind来绑定特定的port来监听用户的连接申请。例外:
RPC通过使用端口映射(PORT MAP)来查询所用的RPC端口。
如果服务器未使用bind绑定ip,则使用客户端的目的ip填充到sockaddr。也就是说,在客户端连接到服务器之前,sockaddr的值是未确定的。客户端的本地套接口地址是由路由表决定的
port=0
htonl(INADDR_ANY)
注意:posix.1g规定,sockaddr的sin_port和sin_addr必须是以网络字节顺序表示,虽然INADDR_ANY在大多数情况下为0,但从规范上讲,应该使用上面的例子
在<netinet/in.h>中定义的常数”INADDR_xxx”都是主机顺序,应使用htonl()进行转换
在bind()执行完成后,本机的sockaddr必须通过getsockname()获得,bind()本身并不返回该值
# include <sys/socket.h>
int listen(int sockfd, int backlog);做两件事:
1. 将无连接的sockfd转为被动套接口(passive socket),内核将能接受指向sockfd的连接请求。
2. 将该套接口的连接队列置为backlog
内核为每个监听套接口维护2个队列:incomplete和complete
incomplete队列是指,没有完成3次握手的队列。
只有在accept()调用返回后,一个处于complete queue的entry才被清除。而且在此之前所获得的有效数据已保存在TCP接收缓存中
一般系统对backlog的值都有上限,backlog不要设为0(各个系统的实现不一致)。
linux的backlog可以设置为0,表示不限制连接数。但是这含有bug
Linux下的backlog默认为1024
传统上,backlog表示2个队列的和;
linux2.2以后,backlog设为完成队列的值,未完成队列的值由内核管理,通过tcp_max_sys_backlog (sysctl)指定。(这是为了放置网络上的syn攻击塞满早期的整个backlog)
# include <sys/socket.h>
int accept(int sockfd, struct sockaddr *cliaddr, socklen_t * addrlen);
返回客户端sockaddr
一般listened socket存在于整个服务器进程中,connected socket 存在于每个子进程。
只有超级用户才有权限绑定1-1024口。
fork()后子进程继承父进程sockfd,子进程exec后,如果FD_CLOEXEC为0(默认),则sockfd不会关闭。可以用fcntl()修改。
在子进程exec后,唯一获得sockaddr的函数是getsockname(), getpeername()
iterative server
concurrent server
调用close(sockfd)后: (counter == 0) ? shutdown(sockfd) : (counter-1)
常用并行服务器模型:
Socket();
Bind();
Listen();
While (1) {
Accept();
Pid=Fork();
If (pid==0) { //child process
Close(listenfd);
Do sth;
Close(connfd);
}
If (pid>0) { //parent process
Close(connfd);
}
}
注意:以上的父进程如果省略close(connfd),后果:
1. 父进程的套接口描述符将越来越大,直到OPEN_MAX
2. 子进程并没有发送FIN,连接依然存在。父进程的连接数一直增大到backlog后,不再接受任何连接
close()进行的操作:(如果counter为0)
1.关闭socket的读、写端
2.发送send buffer中的数据,并且发送FIN
3.并且不会等待对端返回ACK而直接返回。(见p188,figure7.6)
可以通过使用shutdown(),SO_LINGER参数来更改。具体区别见p191,figure7.10
# include <sys/socket.h>
int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addlen); //返回本地sockaddr
int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addlen); //返回对端sockaddr
由上可以猜想:sockfd所关联的结构中,必然包括本地sockaddr和对端sockaddr。
# include <sys/socket.h>
int socket (int family, int type, int protocol);
Family:协议族(AF_INET, AF_INET6, AF_LOCAL, AF_ROUTE, AF_KEY等)当前linux可支持更多协议族。
AF_KEY:支持加密保护的数据安全
type:协议类型(SOCK_STREAM, SOCK_DGRAM, SOCK_RAW)
protocol:大多数情况下为0,除了raw sockets
AF_UNIX区别于AF_LOCAL??????
Linux支持SOCK_PACKET类型,用于操作2层数据包(datalink)。packet(7)显示,不推荐使用
AF_ 在大多数系统中等同于 PF_
# include <sys/socket.h>
int connect(int sockfd, const struct sockaddr_in *addr, socketlen_t len);
Connect 主要执行tcp的3次握手(to be established),在发出ACK报文后,connect()就返回了。在此之前,不需要客户端执行bind,内核将自动为其指定ip及port
Connect的错误返回值:
1. 服务器不响应(主叫方发出SYN后,75s内无响应)ETIMEOUT
2. 服务器返回RST(服务器的被动端口未打开hard error)ECONNREFUSED
3. 中间路由器响应目标不可达(soft error),也必须等待75s,期间内核继续发送SYN包。EHOSTUNREACH ENETUNREACH
注意:如果connect返回错误,则该sockfd将不再可用,如需再次申请建立连接,必须:close(sockfd); socket(sockfd2); connect(sockfd2);
# include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen);
绑定(bind)用于指定本地sockaddr到套接口
服务器通常需要使用bind来绑定特定的port来监听用户的连接申请。例外:
RPC通过使用端口映射(PORT MAP)来查询所用的RPC端口。
如果服务器未使用bind绑定ip,则使用客户端的目的ip填充到sockaddr。也就是说,在客户端连接到服务器之前,sockaddr的值是未确定的。客户端的本地套接口地址是由路由表决定的
port=0
htonl(INADDR_ANY)
注意:posix.1g规定,sockaddr的sin_port和sin_addr必须是以网络字节顺序表示,虽然INADDR_ANY在大多数情况下为0,但从规范上讲,应该使用上面的例子
在<netinet/in.h>中定义的常数”INADDR_xxx”都是主机顺序,应使用htonl()进行转换
在bind()执行完成后,本机的sockaddr必须通过getsockname()获得,bind()本身并不返回该值
# include <sys/socket.h>
int listen(int sockfd, int backlog);做两件事:
1. 将无连接的sockfd转为被动套接口(passive socket),内核将能接受指向sockfd的连接请求。
2. 将该套接口的连接队列置为backlog
内核为每个监听套接口维护2个队列:incomplete和complete
incomplete队列是指,没有完成3次握手的队列。
只有在accept()调用返回后,一个处于complete queue的entry才被清除。而且在此之前所获得的有效数据已保存在TCP接收缓存中
一般系统对backlog的值都有上限,backlog不要设为0(各个系统的实现不一致)。
linux的backlog可以设置为0,表示不限制连接数。但是这含有bug
Linux下的backlog默认为1024
传统上,backlog表示2个队列的和;
linux2.2以后,backlog设为完成队列的值,未完成队列的值由内核管理,通过tcp_max_sys_backlog (sysctl)指定。(这是为了放置网络上的syn攻击塞满早期的整个backlog)
# include <sys/socket.h>
int accept(int sockfd, struct sockaddr *cliaddr, socklen_t * addrlen);
返回客户端sockaddr
一般listened socket存在于整个服务器进程中,connected socket 存在于每个子进程。
只有超级用户才有权限绑定1-1024口。
fork()后子进程继承父进程sockfd,子进程exec后,如果FD_CLOEXEC为0(默认),则sockfd不会关闭。可以用fcntl()修改。
在子进程exec后,唯一获得sockaddr的函数是getsockname(), getpeername()
iterative server
concurrent server
调用close(sockfd)后: (counter == 0) ? shutdown(sockfd) : (counter-1)
常用并行服务器模型:
Socket();
Bind();
Listen();
While (1) {
Accept();
Pid=Fork();
If (pid==0) { //child process
Close(listenfd);
Do sth;
Close(connfd);
}
If (pid>0) { //parent process
Close(connfd);
}
}
注意:以上的父进程如果省略close(connfd),后果:
1. 父进程的套接口描述符将越来越大,直到OPEN_MAX
2. 子进程并没有发送FIN,连接依然存在。父进程的连接数一直增大到backlog后,不再接受任何连接
close()进行的操作:(如果counter为0)
1.关闭socket的读、写端
2.发送send buffer中的数据,并且发送FIN
3.并且不会等待对端返回ACK而直接返回。(见p188,figure7.6)
可以通过使用shutdown(),SO_LINGER参数来更改。具体区别见p191,figure7.10
# include <sys/socket.h>
int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addlen); //返回本地sockaddr
int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addlen); //返回对端sockaddr
由上可以猜想:sockfd所关联的结构中,必然包括本地sockaddr和对端sockaddr。