TCP套接字函数
- start:
- int socket(int family,int type,int protocol);
- int connect(int sockfd,const struct sockaddr *servaddr,socklen_t addrlen);
- int bind(int sockfd,const struct sockaddr *myaddr,socklen_t addrlen);
- int listen(int sockfd, int backlog);
- int accept(int sockfd,struct sockaddr *cliaddr,socklen_t *addrlen);
- int close(int sockfd);
- int getsockname(int sockfd,struct sockaddr *localaddr,socklen_t *addrlen);
- int getpeername(int sockfd,struct sockaddr *peeraddr,socklen_t *addrlen);
start:
#include <sys/socket.h>
int socket(int family,int type,int protocol);
family:协议类型
type:套接字类型
protocol
family | 说明 |
---|---|
AF_INET | IPv4 |
AF_INET6 | IPv6 |
AF_LOCAL | Unix域套接字 |
AF_ROUTE | 路由套接字 |
AF_KEY | 密钥套接字 |
type | 说明 |
---|---|
SOCK_STREAM | 字节流套接字 |
SOCK_DGRAM | 数据报套接字 |
SOCK_SEQPACKET | 有序分组套接字 |
SOCK_RAM | 原始套接字 |
protocol | 说明 |
---|---|
IPPROTO_TCP | TCP传输协议 |
IPPROTO_UDP | UDP传输协议 |
IPPROTO_SCTP | SCTP传输协议 |
并非所有的family和type组合都是有效的:
type\family | AF_INET | AF_INET6 | AF_LOCAL | AF_ROUTE | AF_KEY |
---|---|---|---|---|---|
SOCK_STREAM | TCP,SCTP | TCP,SCTP | 有 | ||
SOCK_DGRAM | UDP | UDP | 有 | ||
SOCK_SEQPACKET | SCTP | SCTP | 有 | ||
SOCK_RAW | IPv4 | IPv6 | 有 | 有 |
int connect(int sockfd,const struct sockaddr *servaddr,socklen_t addrlen);
作用:建立与TCP服务器的连接
sockfd:socket()函数返回的套接字描述符
servaddr:指向要连接的服务器 的 套接字地址结构 的 指针
addrlen:套接字地址结构的大小,sizeof(struct sockaddr)
connect激发三次握手过程。
通常情况下,我们定义sockaddr_in数据类型进行赋值操作,函数引用的时候,使用强制类型,转换为sockaddr数据类型。
套接字地址结构(常用):
IPv4套接字地址结构:
struct sockaddr_in{ //16 bytes
uint8_t sin_len; //1 byte
sa_family_t sin_family; //1 byte
in_port_t sin_port; //2 bytes
struct in_addr sin_addr; //4 bytes
char sin_zero[8]; //unused 8 bytes
}
struct in_addr{ //为什么一个元素还用结构体包裹?
in_addr_t s_addr;
}
通用套接字地址结构
struct sockaddr{ //16bytes
uint8_t sa_len; //1 byte
sa_family_t sa_family; 1 byte
char sa_data[14]; //14 bytes
}
定义报文结构体时,方便定义的一些数据类型:
数据类型 | 说明 | 头文件 |
---|---|---|
int8_t / uint8_t | 有/无 符号的8位整数 | <sys/types.h> |
int16_t / uint16_t | 有/无 符号的16位整数 | <sys/types.h> |
int32_t / uint32_t | 有/无 符号的32位整数 | <sys/types.h> |
sa_family_t | 套接字地址结构的地址族 | <sys/socket.h> |
socklen_t | 套接字地址结构的长度,一般uint32_t | <sys/socket.h> |
in_addr_t | IPv4地址,一般uint32_t | <netinet/in.h> |
in_port_t | TCP,UDP端口,一般uint16_t | <netinet/in.h> |
int bind(int sockfd,const struct sockaddr *myaddr,socklen_t addrlen);
把一个本地协议地址赋给一个套接字。
bind可以指定IP地址或端口、可以两者都指定,也可以都不指定:
IP地址 | 端口 | 结果 |
---|---|---|
通配地址 | 0 | 内核选择IP地址和端口 |
通配地址 | 非0 | 内核选择IP地址,进程指定端口 |
本地IP地址 | 0 | 进程指定IP地址,内核选择端口 |
本地IP地址 | 非0 | 进程指定IP地址和端口 |
通配地址,常由INADDR_ANY指定,值一般为0。
int listen(int sockfd, int backlog);
listen()函数把一个未连接的套接字转换成一个被动套接字,指示内核应接受指向该套接字的连接请求。
backlog规定了内核应该为相应套接字排队的最大连接个数。
内核为任何一个给定的监听套接字维护两个队列:
未完成连接队列:收到客户SYN分节,但尚未完成三次握手
已完成连接队列:完成三次握手
两个队列之和不大于backlog。
进程调用accept()时,已完成连接队列中的队头项返回给进程,如果该队列为空,进程就等到队列里面被放进来一项为止。
int accept(int sockfd,struct sockaddr *cliaddr,socklen_t *addrlen);
sockfd:监听套接字。
参数cliaddr 和 addrlen用来返回已连接的对端进程的协议地址。
如果accept成功,返回值是由内核自动生成的一个全新的描述符,代表服务器和客户端之间的TCP连接,也被成为已连接套接字
int close(int sockfd);
#include <unistd.h>
close一个TCP套接字的默认行为是把该套接字标记成已关闭,然后立即返回到调用进程,该套接字描述符不能再由调用进程使用。
并发服务器中父进程关闭已连接套接字只是导致相应描述符的引用计数减一,除非刚好减一为零,否则close调用并不能引发TCP四分组连接终止序列。这是并发服务器所希望的。
若确实希望直接进行“四次挥手”,可以将close改为shutdown函数。
int getsockname(int sockfd,struct sockaddr *localaddr,socklen_t *addrlen);
得到与某个套接字关联的本地协议地址
int getpeername(int sockfd,struct sockaddr *peeraddr,socklen_t *addrlen);
得到与某个套接字关联的外地协议地址