socket通信进程及流程.docx
- 文档编号:28412984
- 上传时间:2023-07-13
- 格式:DOCX
- 页数:10
- 大小:206.62KB
socket通信进程及流程.docx
《socket通信进程及流程.docx》由会员分享,可在线阅读,更多相关《socket通信进程及流程.docx(10页珍藏版)》请在冰豆网上搜索。
socket通信进程及流程
socket通信进程及流程
以下图是基于TCP协议的客户端/效劳器程序的一样流程:
效劳器挪用socket()、bind()、listen()完成初始化后,挪用accept()阻塞等待,处于监听端口的状态,客户端挪用socket()初始化后,挪用connect()发出SYN段并阻塞等待效劳器应答,效劳器应答一个SYN-ACK段,客户端收到后从connect()返回,同时应答一个ACK段,效劳器收到后从accept()返回。
数据传输的进程:
成立连接后,TCP协议提供全双工的通信效劳,可是一样的客户端/效劳器程序的流程是由客户端主动发起请求,效劳器被动处置请求,一问一答的方式。
因此,效劳器从accept()返回后立刻挪用read(),读socket就像读管道一样,若是没有数据抵达就阻塞等待,这时客户端挪用write()发送请求给效劳器,效劳器收到后从read()返回,对客户端的请求进行处置,在此期间客户端挪用read()阻塞等待服务器的应答,效劳器挪用write()将处置结果发还给客户端,再次挪用read()阻塞等待下一条请求,客户端收到后从read()返回,发送下一条请求,如此循环下去。
若是客户端没有更多的请求了,就挪用close()关闭连接,就像写端关闭的管道一样,效劳器的read()返回0,如此效劳器就明白客户端关闭了连接,也挪用close()关闭连接。
注意,任何一方挪用close()后,连接的两个传输方向都关闭,不能再发送数据了。
若是一方挪用shutdown()那么连接处于半关闭状态,仍可接收对方发来的数据。
在学习socketAPI时要注意应用程序和TCP协议层是如何交互的:
*应用程序挪用某个socket函数时TCP协议层完成什么动作,比如挪用connect()会发出SYN段*应用程序如何明白TCP协议层的状态转变,比如从某个阻塞的socket函数返回就说明TCP协议收到了某些段,再比如read()返回0就说明收到了FIN段
看图所示的socket 通信进程
图12.9socket的通信进程
1.成立套接字
在中。
intsocket(intfamily,inttype,intprotocol);
socket()打开一个网络通信端口,若是成功的话,就像open()一样返回一个文件描述符,应用程序能够像读写文件一样用read/write在网络上收发数据,若是socket()挪用犯错那么返回-1。
关于IPv4,family参数指定为AF_INET。
关于TCP协议,type参数指定为SOCK_STREAM,表示面向流的传输协议。
若是是UDP协议,那么type参数指定为SOCK_DGRAM,表示面向数据报的传输协议。
protocol参数的介绍从略,指定为0即可。
Linux在利用socket()系统挪用成立新的套接字时,需要传递套接字的地址族标识符、套接字类型和协议,其函数概念于中:
asmlinkagelongsys_socket(intfamily,inttype,intprotocol)
{
intretval;
structsocket*sock;
retval=sock_create(family,type,protocol,&sock);
if(retval<0)
gotoout;
retval=sock_map_fd(sock);
if(retval<0)
gotoout_release;
out:
/*Itmaybealreadyanotherdescriptor8)Notkernelproblem.*/
returnretval;
out_release:
sock_release(sock);
returnretval;
}
事实上,套接字关于用户程序而言确实是特殊的已打开的文件。
内核中为套接字概念了一种特殊的文件类型,形成一种特殊的文件系统sockfs,其概念于:
staticstructvfsmount*sock_mnt;
staticDECLARE_FSTYPE(sock_fs_type,"sockfs",sockfs_read_super,FS_NOMOUNT);
在系统初始化时,要通过kern_mount()安装那个文件系统。
安装时有个作为连接件的vfsmount数据结构,那个结构的地址就保留在一个全局的指针sock_mnt中。
所谓创建一个套接字,确实是在sockfs文件系统中创建一个特殊文件,或说一个节点,并成立起为实现套接字功能所需的一整套数据结构。
因此,函数sock_create()第一是成立一个socket数据结构,然后将其“映射”到一个已打开的文件中,进行socket结构和sock结构的分派和初始化。
新创建的BSDsocket数据结构包括有指向地址族专有的套接字例程的指针,这一指针实际确实是proto_ops数据结构的地址。
BSD套接字的套接字类型设置为所请求的SOCK_STREAM或SOCK_DGRAM等。
然后,内核利用proto_ops数据结构中的信息挪用地址族专有的创建例程。
以后,内核从当前进程的fd向量中分派空闲的文件描述符,该描述符指向的file数据结构被初始化。
初始化进程包括将文件操作集指针指向由BSD套接字接口支持的BSD文件操作集。
所有随后的套接字(文件)操作都将定向到该套接字接口,而套接字接口那么会进一步伐用地址族的操作例程,从而将操作传递到底层地址族,如图所示。
事实上,socket结构与sock结构是同一事物的两个方面。
若是说socket结构是面向进程和系统挪用界面的,那么sock结构确实是面向底层驱动程序的。
可是,什么缘故不把这两个数据结构归并成一个呢?
咱们说套接字是一种特殊的文件系统,因此,inode结构内部的union的一个成份就用作socket结构,其概念如下:
structinode{
…
union{
…
structsocket socket_i;
}
}
由于套接字操作的特殊性,那个结构中需要大量的结组成份。
可是,若是把这些结组成份全都放在socket结构中,那么inode结构中的那个union就会变得专门大,从而inode结构也会变得专门大,而关于其他文件系统,那个union成份并非需要那么庞大。
因此,就把套接字所需的这些结组成份拆成两部份,把与文件系统关系比较紧密的那一部份放在socket结构中,把与通信关系比较紧密的那一部份那么单独组成一个数据结构,即sock结构。
由于这两部份数据在逻辑上本来确实是一体的,因此要通过指针相互指向对方,形成一对一的关系。
2.在INETBSD套接字上绑定(bind)地址
为了监听传入的Internet连接请求,每一个效劳器都需要成立一个INETBSD套接字,而且将自己的地址绑定到该套接字。
绑定操作要紧在INET套接字层中进行,还需要底层TCP层和IP层的某些支持。
将地址绑定到某个套接字上以后,该套接字就不能用来进行任何其他的通信,因此,该socket数据结构的状态必需为TCP_CLOSE。
传递到绑定操作的sockaddr数据结构中包括要绑定的IP地址,和一个可选的端口地址。
通常而言,要绑定的地址应该是给予某个网络设备的IP地址,而该网络设备应该支持INET地址族,而且该设备是可用的。
利用ifconfig命令可查看当前活动的网络接口。
被绑定的IP地址保留在sock数据结构的rcv_saddr和saddr域中,这两个域别离用于哈希查找和发送用的IP地址。
端口地址是可选的,若是没有指定,底层的支持网络会选择一个空闲的端口。
intbind(intsockfd,conststructsockaddr*myaddr,socklen_taddrlen);
效劳器程序所监听的网络地址和端口号一般是固定不变的,客户端程序得知效劳器程序的地址和端口号后就能够够向效劳器发起连接,因此效劳器需要挪用bind绑定一个固定的网络地址和端口号。
bind()成功返回0,失败返回-1。
bind()的作用是将参数sockfd和myaddr绑定在一路,使sockfd那个用于网络通信的文件描述符监听myaddr所描述的地址和端口号。
前面讲过,structsockaddr*是一个通用指针类型,myaddr参数事实上能够同意多种协议的sockaddr结构体,而它们的长度各不相同,因此需要第三个参数addrlen指定结构体的长度。
咱们的程序中对myaddr参数是如此初始化的:
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
servaddr.sin_port=htons(SERV_PORT);
第一将整个结构体清零,然后设置地址类型为AF_INET,网络地址为INADDR_ANY,那个宏表示本地的任意IP地址,因为效劳器可能有多个网卡,每一个网卡也可能绑定多个IP地址,如此设置能够在所有的IP地址上监听,直到与某个客户端成立了连接时才确信下来到底用哪个IP地址,端口号为SERV_PORT,咱们概念为8000。
当底层网络设备同意到数据包时,它必需将数据包传递到正确的INET和BSD套接字以便进行处置,因此,TCP保护多个哈希表,用来查找传入IP消息的地址,并将它们定向到正确的socket/sock对。
TCP并非在绑定进程中将绑定的sock数据结构添加到哈希表中,在这一进程中,它仅仅判定所请求的端口号当前是不是正在利用。
在监听操作中,该sock结构才被添加到TCP的哈希表中。
3.在INETBSD套接字上成立连接(connect)
创建一个套接字以后,该套接字不仅能够用于监听入站的连接请求,也能够用于成立出站的连接请求。
不论如何都涉及到一个重要的进程:
成立两个应用程序之间的虚拟电路。
出站连接只能成立在处于正确状态的INETBSD套接字上,因此,不能成立于已成立连接的套接字,也不能成立于用于监听入站连接的套接字。
也确实是说,该BSDsocket数据结构的状态必需为SS_UNCONNECTED。
在成立连接进程中,两边TCP要进行三次“握手”,具体进程在本章第二节——网络协议一文中有详细介绍。
若是TCPsock正在等待传入消息,那么该sock结构添加到tcp_listening_hash表中,如此,传入的TCP消息就能够够定向到该sock数据结构。
由于客户端不需要固定的端口号,因此没必要挪用bind(),客户端的端口号由内核自动分派。
注意,客户端不是不许诺挪用bind(),只是没有必要挪用bind()固定一个端口号,效劳器也不是必需挪用bind(),但如果是效劳器不挪用bind(),内核会自动给效劳器分派监听端口,每次启动效劳器时端口号都不一样,客户端要连接效劳器就会碰到麻烦。
intconnect(intsockfd,conststructsockaddr*servaddr,socklen_taddrlen);
客户端需要挪用connect()连接效劳器,connect和bind的参数形式一致,区别在于bind的参数是自己的地址,而connect的参数是对方的地址。
connect()成功返回0,犯错返回-1。
4.监听(listen)INETBSD套接字
intlisten(intsockfd,intbacklog);
典型的效劳器程序能够同时效劳于多个客户端,当有客户端发起连接时,效劳器挪用的accept()返回并同意那个连接,若是有大量的客户端发起连接而效劳器来不及处置,尚未accept的客户端就处于连接等待状态,listen()声明sockfd处于监听状态,而且最多许诺有backlog个客户端处于连接待状态,若是接收到更多的连接请求就忽略。
listen()成功返回0,失败返回-1。
当某个套接字被绑定了地址以后,该套接字就能够够用来监听专属于该绑定地址的传入连接。
网络应用程序也能够在未绑定地址之前监听套接字,这时,INET套接字层将利用空闲的端口编号并自动绑定到该套接字。
套接字的监听函数将socket的状态改变成TCP_LISTEN。
当接收到某个传入的TCP连接请求时,TCP成立一个新的sock数据结构来描述该连接。
当该连接最终被同意时,新的sock数据结构将变成该TCP连接的内核bottom_half部份,这时,它要克隆包括连接请求的传入sk_buff中的信息,并在监听sock数据结构的receive_queue队列中将克隆的信息排队。
克隆的sk_buff中包括有指向新sock数据结构的指针。
5.同意连接请求(accept)
同意操作在监听套接字上进行,从监听socket中克隆一个新的socket数据结构。
其进程如下:
同意操作第一传递到支持协议层,即INET中,以便同意任何传入的连接请求。
相反,同意操作进一步传递到实际的协议,例如TCP上。
同意操作能够是阻塞的,也能够是非阻塞的。
同意操作为非阻塞的情形下,若是没有可同意的传入连接,那么同意操作将失败,而新成立的socket数据结构被抛弃。
同意操作为阻塞的情形下,执行阻塞操作的网络应用程序将添加到等待队列中,并维持挂起直到接收到一个TCP连接请求为至。
当连接请求抵达以后,包括连接请求的sk_buff被抛弃,而由TCP成立的新sock数据结构返回到INET套接字层,在那个地址,sock数据结构和先前成立的新socket数据结组成立链接。
而新socket的文件描述符(fd)被返回到网络应用程序,尔后,应用程序就能够够利用该文件描述符在新成立的INETBSD套接字上进行套接字操作。
intaccept(intsockfd,structsockaddr*cliaddr,socklen_t*addrlen);
三方握手完成后,效劳器挪用accept()同意连接,若是效劳器挪用accept()时尚未客户端的连接请求,就阻塞等待直到有客户端连接上来。
cliaddr是一个传出参数,accept()返回时传出客户端的地址和端口号。
addrlen参数是一个传入传出参数(value-resultargument),传入的是挪用者提供的缓冲区cliaddr的长度以幸免缓冲区溢出问题,传出的是客户端地址结构体的实际长度(有可能没有占满挪用者提供的缓冲区)。
若是给cliaddr参数传NULL,表示不关切客户端的地址。
注意:
效劳器接收到传入的请求后,若是能够同意该请求,效劳器必需创建一个新的套接字来同意该请求并成立通信连接(用于监听的套接字不能用来成立通信连接),这时,效劳器和客户就能够够利用成立好的通信连接传输数据。
转载地址:
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- socket 通信 进程 流程