socket函数
int socket(int family, int type, int protocol);
为了执行网络IO,一个进程第一步就是执行socket函数
family常用值:
AF_LOCAL Local communication unix(7)
AF_INET IPv4 Internet protocols ip(7)
AF_INET6 IPv6 Internet protocols ipv6(7)
AF_ROUTE 路由套接字
AF_KEY 秘钥套接字
type常用值:
SOCK_STREAM 字节流套接字
SOCK_DGRAM 数据报套接字
SOCK_SEQPACKET 有序分组套接字
SOCK_RAW 原始套接字
protocal常用值:
IPPROTO_TCP:tcp传输协议
IPPROTO_UDP:udp传输协议
IPPROTO_SCTP: sctp传输协议
connect函数
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
sockfd:socket函数成功返回的socket fd
addr是指向一个套接字地址结构的指针,addrlen是该结构的大小
如果是TCP套接字,调用connect函数会激发TCP的三次握手
1.若TCP客户端没有收到SYN的响应,则返回ETIMEDOUT,表示服务端无响应,内核会加长时间间隔发送SYN,几次发送后均无返回时,则函数返回调用端
2.若对客户端的SYN返回RST,则表示服务器主机在我们指定的端口上没有进程在等待连接
返回ECONNREFUSED
3.若客户端发送SYN在中间某个路由器上引发目标不可达的错误(ICMP错误),则认为是一个软错误。客户端可以按一定时间间隔再反复发送该数据包
出现这种错误的原因可能有1,按照本地系统的转发表,没有到达远端的路径。2,connect调用没有等待就直接返回
bind函数
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
将一个本地地址赋予一个套接字
1.服务器在启动时捆绑它们的众所周知的端口,如果一个TCP客户端或者服务端,未曾bind绑定一个端口,当调用connect或者listen,内核就会为相应的套接字选择一个临时端口。
2.进程可以把一个特定的IP地址绑定到它的套接字上,不过这个IP地址必须属于其所在主机的网络接口之一。
struct sockaddr {
sa_family_t sa_family;
char sa_data[14];
}
对于ipv4来说,通配地址由常量值INADDR_ANY来指定
如果让内核来选择一个临时端口号,那么bind函数并不返回所选择的值,为了得到内核选定的端口号,必须调用getsockname来返回协议地址。
listen函数
int listen(int sockfd, int backlog);
listen函数仅由TCP服务端调用,该函数做了两件事
1.当socket函数创建一个套接字时,它被假设成一个主动套接字,它是一个将要调用connect发起连接的客户端socket。listen函数是把一个未连接的套接字,转换成一个被动套接字,指示内核应接受指向该套接字的连接请求。根据状态图将一个socket由CLOSED状态转换成LISTEN状态
2.本函数第二个参数,规定了内核为相应套接字排队的最大连接个数。
accept函数
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
由TCP服务端调用,用于从已完成队列头,返回下一个已完成链接。如果已完成队列为空,那么进程进入休眠。(假定套接字是默认的阻塞方式)
addr和addrlen返回的是已连接对端的协议地址。