socket函数:
#include<sys/socket.h>
int socket(int family,int type,int protocol);
返回值:若成功则返回非负描述符,若失败则返回-1
family:协议族IPV4、IPV6等
type:字节流、数据包等
protocol:TCP、UDP等
connect函数:
#include<sys/socket.h>
int connect(int sockfd,const struct sockaddr *servaddr,socklen_t addrlen)
若成功则返回0,失败返回-1
第二个函数参数,套接字地址结构含有服务器的IP地址和端口号
客户在调用connect之前,不必非得调用bind函数,若有需要的话内核会确定源IP地址并选择一个临时端口作为源端口号
如果是TCP套接字,调用connect函数将激发TCP的三次握手过程,而且仅在建立成功或出错时返回
出错返回的三种情况:
1.未收到SYN的响应,重传超时后仍未收到,返回ETIMEDOUT
2.收到RST响应,服务器主机上,指定端口上没有进程在等待或运行(硬错误),返回ECONNREFUSED
3.客户发出的SYN在中间的某个路由器上引发了一个“目的地不可达”的ICMP错误,客户机内核记录重传,超时后返回EHOSTUNREACH/ENETUNREACH错误(按照本地系统的转发表,根本没有到达远程系统的路径或connect调用根本不等待就返回)
connect将socket由closed状态转变为syn_sent状态,若成功进入ESTABLISHED状态,若失败,则该套接字不可用,必须关闭
bind函数:
将本地协议地址,赋给套接字,包含IP地址与端口号
#inlude<sys/socket.h>
int bind(int sockfd,const struct sockaddr *myadddr,socklen_t addrlen);
成功返回0,出错返回-1
调用bind,可以指定IP地址或端口,可以两者都指定,也可以都不指定(通配地址、本地IP地址,0,非0)
若让内核来选定一个临时端口号,bind函数并不返回,需要调用getsockname函数来返回协议地址
网络字节序(大端,高位字节在低地址)、主机字节序(小端,地位字节在低地址)
htons转16位,htonl转32位
常见错误:地址已使用
bzero:地址置0
setscockopt:消除端口复用
listen函数:
主动套接字->被动套接字
backlog参数规定了内核应该为相应套接字排队的最大连接数
在socket、bind之后,在accept之前调用
内核为一个给定的监听套接字维护了两个队列 队列之和不超过backlog
1.未完成连接队列(SYN_RCVD)
2.已完成连接队列.(ESTABLISHED)
不要将backlog设置为0,若不希望让任何客户连接至监听套接字,那么就关掉
三次握手正常完成的情况下,未完成连接队列中的任何一项在其中的存留时间就是一个RTT
当一个客户SYN到达后,若队列是满的,TCP就忽略该分节,不发送RST
accept函数:
由TCP服务器调用,用于从已完成连接队列头返回下一个已完成连接,如果已完成连接队列为空,那么进程被投入睡眠(套接字为默认阻塞方式时),若成功则为描述符,失败为-1.
#include<sys/socket.h>
int accept(int sockfd,struct sockaddr *cliaddr,socklen_t *addrlen)
监听套接字与已连接套接字
一个问题:非阻塞accept、connect
引用回答:
1.非阻塞accept:
我们一般使用IO复用来实现并发模型,如果我们默认监听套接字为阻塞模式,假设一种场景如下:
客户通过connect向TCP服务器发起三次握手
三次握手完成后,触发TCP服务器监听套接字的可读事件,IO复用返回(select、poll、epoll_wait)
客户通过RST报文取消连接
TCP服务器调用accept接受连接,此时发现内核已连接队列为空(因为唯一的连接已被客户端取消)
程序阻塞在accept调用,无法响应其它已连接套接字的事件
为了防止出现上面的场景,我们需要把监听套接字设置为非阻塞
2.由于程序用select等待连接完成,可以设置一个select等待时间限制,从而缩短connect超时时间。
多数实现中,connect的超时时间在75秒到几分钟之间。有时程序希望在等待一定时间内结束,使用非阻塞connect可以防止阻塞75秒,在多线程网络编程中,尤其必要。