总结一下套接字进行通信的过程:
1.使用socket()函数创建套接字:
socket() 函数用来创建套接字,确定套接字的各种属性, 即socket()函数的三个参数 域(domain),类型(type)和协议(protocol)
(1)domain:即协议域,又称为协议族(family),域 指定套接字通信中使用的网络介质(即 决定了socket的地址类型,在通信中必须采用对应的地址),最常见的套接字域是AF_INET,它指的是Internet网络;
常用的协议族有,AF_INET、AF_INET6、AF_LOCAL;如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址。
(2)type:指定socket类型(其实就是通信方式)。常用的socket类型有,SOCK_STREAM、SOCK_DGRAM;
1) SOCK_STREAM 提供有序的、可靠的、双向的和基于连接的字节流,使用带外数据传送机制,为Internet地址族使用TCP。
2)SOCK_DGRAM 支持无连接的、不可靠的和使用固定大小(通常很小)缓冲区的数据报服务,为Internet地址族使用UDP。
(3)protocol:故名思意,就是指定协议(通常使用默认就可以了(也就是最后一个参数填0))。 常用的协议有,IPPROTO_TCP、 IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等
2.使用bind()将套接字与特定的IP地址和端口绑定起来:
服务器端要用 bind() 函数将套接字与特定的IP地址和端口绑定起来,只有这样,流经该IP地址和端口的数据才能交给套接字处理;即 bind()函数把一个地址族中的特定地址赋给socket
(1)sockfd:即socket描述字,它是通过socket()函数创建的,唯一标识一个socket。bind()函数就是将给这个描述字绑定一个名字。
(2)addr:一个const struct sockaddr *指针,指向要绑定给sockfd的协议地址。这个地址结构根据地址创建socket时的地址协议族的不同而不同
(3)addrlen:对应的是地址的长度。
3.使用connect()函数用来建立连接:
客户端要用 connect() 函数建立连接。
connect函数的第一个参数sockfd, 即为客户端的socket描述字,第二参数 addr 为服务器的socket地址,第三个参数addrlen, 为socket地址的长度。客户端通过调用connect函数来建立与TCP服务器的连接。
4.使用listen()函数让套接字进入被动监听状态 :
通过 listen() 函数可以让套接字进入被动监听状态,listen() 只是让套接字进入监听状态,并没有真正接收客户端请求,listen() 后面的代码会继续执行,直到遇到 accept()。accept() 会阻塞程序执行(后面代码不能被执行),直到有新的请求到来。
listen函数的第一个参数sockfd, 即为要监听的socket描述字,第二个参数backlog, 为相应socket可以排队的最大连接个数。socket()函数创建的socket默认是一个主动类型的,listen函数将socket变为被动类型的,等待客户的连接请求。
5.使用accept()函数随时响应客户端的请求:
所谓被动监听,是指当没有客户端请求时,套接字处于“睡眠”状态,只有当接收到客户端请求时,套接字才会被“唤醒”来响应请求。
accept函数的第一个参数为服务器的socket描述字,第二个参数为指向struct sockaddr *的指针,用于返回客户端的协议地址,第三个参数为协议地址的长度。如果accpet成功,那么其返回值是由内核自动生成的一个全新的描述字,代表与返回客户的TCP连接。
6.recvmsg()/sendmsg()调用网络I/O进行读写操作,实现了网咯中不同进程之间的通信!
网络I/O操作有下面几组:
read()/write()
recv()/send()
readv()/writev()
recvmsg()/sendmsg()
recvfrom()/sendto()
推荐使用 recvmsg()/sendmsg()函数,这两个函数是最通用的I/O函数,实际上可以把上面的其它函数都替换成这两个函数
7.close()函数
在服务器与客户端建立连接之后,会进行一些读写操作,完成了读写操作就要关闭相应的socket描述字,好比操作完打开的文件要调用close( ) 函数关闭打开的文件。
close一个TCP socket的缺省行为时把该socket标记为以关闭,然后立即返回到调用进程。该描述字不能再由调用进程使用,也就是说不能再作为read或write的第一个参数。
注意:close操作只是使相应socket描述字的引用计数-1,只有当引用计数为0的时候,才会触发TCP客户端向服务器发送终止连接请求。
三次握手和四次挥手中对应的套接字
三次握手对应的套接字:
当客户端调用connect时,触发了连接请求,向服务器发送了SYN J包,这时connect进入阻塞状态;服务器监听到连接请求,即收到SYN J包,调用accept函数接收请求向客户端发送SYN K ,ACK J+1,这时accept进入阻塞状态;客户端收到服务器的SYN K ,ACK J+1之后,这时connect返回,并对SYN K进行确认;服务器收到ACK K+1时,accept返回,至此三次握手完毕,连接建立。
总结:客户端的connect在三次握手的第二个次返回,而服务器端的accept在三次握手的第三次返回。
四次挥手对应的套接字:
某个应用进程首先调用close主动关闭连接,这时TCP发送一个FIN M;
另一端接收到FIN M之后,执行被动关闭,对这个FIN进行确认。它的接收也作为文件结束符传递给应用进程,因为FIN的接收意味着应用进程在相应的连接上再也接收不到额外数据;
一段时间之后,接收到文件结束符的应用进程调用close关闭它的socket。这导致它的TCP也发送一个FIN N;
接收到这个FIN的源发送端TCP对它进行确认。