用于指定通信的
函数定义为 int socket(int family,int type,int protocol),在 sys/socket.h中定义。
family:指定协议族,比如 AF_INET表示IPv4协议,AF_INET6表示IPv6协议
type:表明套接字类型,比如 SCOK_STREAM 表示字节流套接字,SCOK_DGRAM表示
protocol:表示某个协议类型的常量值,一般为0,表示对所有family和type的用系统默认值。IPROTO_
connect
客户端用来建立与TCP
connect失败则该套接字不可再用,必须关闭,想要重连接必须再调用socket
connect在那些情况下会出错?
客户端没有收到SYN的响应,返回ETIMEDOUT错误。
对于4.4BSD内核发送SYN,没有响应再等6s发送,无响应等24s,如果总共等了75s仍然没有就返回ETIMEDOUT错误
客户端收到SYN响应为RST,返回ECONNREFUESED错误。
这是种硬错误。收到RST可能是:没有服务器监听连接的
路由器引发了‘destination unreachable’ ICMP错误。
这是种软错误
bind
将本地协议地址赋予一个套接字。
本地协议地址:比如 IPv4或IPv6地址与端口的组合
调用bind的端口和地址可以都指定或者都不指定,或者只指定一个。如果端口号不指定,内核会在bind被调用时选择一个临时的端口。
函数定义为 int bind(int sockfd,const getsockname 返回协议地址
做两件事
指示内核应该接受指向此套接字的连接请求,对应TCP状态转移为套接字从CLOSED状态变成LISTEN状态
规定内核应该为相应套接字排队的最大连接个数
socket创建的套接字默认是用来主动发起请求的,即用来调用connect函数,listen则是将这个套接字变成被动套接字,用来接收请求
内核维护的监听套接字队列
backlog的同一个取值根据
未完成队列:由某个客户端发出的SYN包到达了服务器,而服务器正在等待完成相应的TCP三次握手的过程;
已完成的队列:每个已完成TCP三次握手的客户端对应的其中一项
三次握手正常完成的这项会从未完成连接对列移到已完成队列的队尾。当
当客户SYN到达时,如果队列是满的,TCP会忽略这个包,使得客户端会重传
accept
用于从已完成连接队列队头返回下一个已完成连接。如果accept成功,返回值是有内核
一个服务器通常只创建一个监听套接字,他在这个服务的声明周期内一直存在。但是会为每个客户端的连接建立一个以连接套接字,对客户端的服务完成时,就关闭这个连接套接字
accept生成新的描述符处理已连接的请求过程
首先处于监听状态的服务器监听客户端发来的连接请求
第二步accept返回结果,连接被内核接受,新的套接字(connfd)创建
第三步
最后父进程关闭已连接套接字,子进程关闭监听套接字,由子进程处理与客户端的连接,父进程则继续监听下一个客户端连接请求
父进程中调用fork之前所打开的所有描述符在fork返回之后与子进程共享。
并发服务器
并发服务器的存在是不
for(;;){
connfd=Accept(listenfd,..)
// fork调用一次会返回两次。在子进程中返回值一次,返回值为0;在调用进程,即父进程,中返回一次,返回值为新建的子进程的进程ID;
if((p
父进程中关闭了新建立的连接,为什么子进程还能处理连接请求?
每个文件或套接字都有一个引用计数。在文件表中维护,它表示的是当前打开着的引用该文件或者套接字的描述符的个数。socket返回后与listenfd关联的文件表项的引用计数值为1,accept返回的connfd也是如此。fork之后,两个文件描述符在父子进程之间共享,因此引用计数均变成2,这样当父进程关闭connfd的时候,只是引用计数从2变成了1,而真正的资源清理和释放只有在变为0才发生。
close
用来关闭套接字,如果文件的引用计数此时恰好为0,就会发送FIN包,终止TCP连接。
如果想直接终止可以用shutdown