用于指定通讯的协议类型,它的返回值为socket descriptorbash
函数定义为 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_TCP表示TCP协议,IPROTO_UDP表示UDP协议
客户端用来创建与TCP服务器的链接,它的调用将激发TCP的三路握手,即会使当前套接字从CLOSED状态转移到SYN_SENT状态,若成功再转移到ESTABLISHED状态。只有链接创建或者出错才会返回。并发
connect失败则该套接字不可再用,必须关闭,想要重链接必须再调用socketsocket
对于4.4BSD内核发送SYN,没有响应再等6s发送,无响应等24s,若是总共等了75s仍然没有就返回ETIMEDOUT错误函数
这是种硬错误。收到RST多是:没有服务器监听链接的端口;TCP想取消链接;TCP收到一个根本不存在的链接上的分节ui
这是种软错误spa
将本地协议地址赋予一个套接字。操作系统
本地协议地址:好比 IPv4或IPv6地址与端口的组合3d
调用bind的端口和地址能够都指定或者都不指定,或者只指定一个。若是端口号不指定,内核会在bind被调用时选择一个临时的端口。指针
函数定义为 int bind(int sockfd,const struct *myaddr,socklen_t addrlen);第一个参数就是就是socket返回的套接字描述符,第二个参数是指向特定于协议的地址结构的指针,第三个是该地址结构的长度。因为地址结构是个常量,因此若是是内核指定端口,没法返回,因此要获取内核指定的临时端口,必须调用
getsockname
返回协议地址
作两件事
socket建立的套接字默认是用来主动发起请求的,即用来调用connect函数,listen则是将这个套接字变成被动套接字,用来接收请求
backlog的同一个取值根据操做系统不一样,实际的数目会有差异
三次握手正常完成的这项会从未完成链接对列移到已完成队列的队尾。当进程调用accept时,已完成队列的头部将返回给进程,若是已完成队列为空,进程将被投入睡眠,睡眠针对的是默认的阻塞模式,直到TCP在该队列中放入一项才唤醒。
当客户SYN到达时,若是队列是满的,TCP会忽略这个包,使得客户端会重传
用于从已完成链接队列队头返回下一个已完成链接。若是accept成功,返回值是有内核自动生成的一个全新的描述符,表明与客户端创建的TCP链接。
一个服务器一般只建立一个监听套接字,他在这个服务的声明周期内一直存在。可是会为每一个客户端的链接创建一个以链接套接字,对客户端的服务完成时,就关闭这个链接套接字
首先处于监听状态的服务器监听客户端发来的链接请求
父进程中调用fork以前所打开的全部描述符在fork返回以后与子进程共享。
并发服务器
并发服务器的存在是不但愿一个服务一个客户端过长时间,而致使整个服务器被单个客户端长期占用,Unix中编写并发服务器最简单的办法就是 fork一个子进程来服务每一个客户,通常实现以下:
for(;;){ connfd=Accept(listenfd,..) // fork调用一次会返回两次。在子进程中返回值一次,返回值为0;在调用进程,即父进程,中返回一次,返回值为新建的子进程的进程ID; if((pid=Fork())==0){ Close(listenfd); //子进程不监听,直接关闭 doSomething(connfd); //处理客户端请求 Close(connfd); //处理客户端请求完毕,关闭链接 exit(0); } Close(connfd) //由子进程处理,父进程就能够断开链接 } 复制代码
父进程中关闭了新创建的链接,为何子进程还能处理链接请求?
每一个文件或套接字都有一个引用计数。在文件表中维护,它表示的是当前打开着的引用该文件或者套接字的描述符的个数。socket返回后与listenfd关联的文件表项的引用计数值为1,accept返回的connfd也是如此。fork以后,两个文件描述符在父子进程之间共享,所以引用计数均变成2,这样当父进程关闭connfd的时候,只是引用计数从2变成了1,而真正的资源清理和释放只有在变为0才发生。
用来关闭套接字,若是文件的引用计数此时刚好为0,就会发送FIN包,终止TCP链接。
若是想直接终止能够用shutdown