基本TCP套接字操作
include <sys/types.h> /* See NOTES */
include <sys/socket.h>
socket
创建一个套接字,指定通信协议类型
// 成功:返回文件描述符 失败:返回-1,设置errno
int socket(int domain, int type, int protocol);
-
domain
:协议族AF_INET
、AF_INET6
、AF_LOCAL、AF_ROUTE、AF_KEYAF_XXX
指地址族,PF_XXX
指协议族。但是在实际中,支持多个地址族的协议族从来没有实现过,且<sys/socket.h>中 PF_ 值总是与对应的 AF_ 值相等,因此,两者值相等,现在可看成是一个意思。 -
type
:套接字类型SOCK_STREAM
、SOCK_DGRAM
、SOCK_SEQPACKET、SOCK_RAW -
protocol
:某协议类型的常值,通常为0
bind
把一个本地协议地址赋予一个套接字。
协议地址:ip地址 + port
// 成功:返回0 失败:返回-1,设置errno(常见:EACCES、EADDRINUSE)
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
struct sockaddr_in {
sa_family_t sin_family; /* address family: AF_INET */
in_port_t sin_port; /* port in network byte order */
struct in_addr sin_addr; /* internet address */
};
/* Internet address. */
struct in_addr {
uint32_t s_addr; /* address in network byte order */
};
sockfd
:文件描述符(套接字)addr
:指向特定协议的地址结构的指针addrlen
:地址结构长度
listen
做两件事:
- 把一个未连接的主动套接字转换成被动套接字,指示内核接收指向该套接字的连接请求,CLOSED状态 --> LISTEN状态
- backlog规定已完成连接队列的最大连接个数
// 成功:返回0 失败:返回-1,设置errno
int listen(int sockfd, int backlog);
内核为任何一个给定的监听套接字维护两个队列:
- 未完成连接队列:正在等待完成TCP三次握手,处于 SYN_RCVD 状态
- 已完成连接队列:已完成TCP三次握手,等待通过
accept
从队列中取出已连接套接字,处于 ESTABLISED 状态
Linux 2.2 以后,backlog 表示已完成连接队列的 socket 上限。实际测试中,队列最大长度为 backlog + 1。
测试代码及数据:listen()函数backlog参数测试
connect
TCP客户端使用,用于建立与TCP服务器的连接
// 成功:返回0 失败:返回-1,设置errno
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
accept
有TCP服务器调用,从已完成连接队列队头返回一个描述符。若队列为空,进程睡眠(默认阻塞方式)
// 成功:返回非负描述符 失败:返回-1,设置errno
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
若对返回客户端协议地址不感兴趣,可把 addr、addrlen 均值为空指针。
close
关闭套接字,该套接字不能再被调用进程使用。并发服务器中,close调用实际是相应套接字引用计数减1
// 成功:返回0 失败:返回-1,设置errno
int close(int sockfd);
当close关闭套接字时,TCP将尝试发送已排队等待发送到对端的任何数据,发送完毕后的是正常的TCP连接终止序列(四次握手)。