socket通信过程及流程

上传人:宝路 文档编号:2759451 上传时间:2017-07-27 格式:DOCX 页数:9 大小:185.64KB
返回 下载 相关 举报
socket通信过程及流程_第1页
第1页 / 共9页
socket通信过程及流程_第2页
第2页 / 共9页
socket通信过程及流程_第3页
第3页 / 共9页
socket通信过程及流程_第4页
第4页 / 共9页
socket通信过程及流程_第5页
第5页 / 共9页
点击查看更多>>
资源描述

《socket通信过程及流程》由会员分享,可在线阅读,更多相关《socket通信过程及流程(9页珍藏版)》请在金锄头文库上搜索。

1、socket 通信过程及流程下图是基于 TCP 协议的客户端/服务器程序的一般流程:服务器调用 socket()、bind()、listen()完成初始化后,调用 accept()阻塞等待,处于监听端口的状态,客户端调用 socket()初始化后,调用 connect()发出 SYN 段并阻塞等待服务器应答,服务器应答一个 SYN-ACK 段,客户端收到后从 connect()返回,同时应答一个 ACK 段,服务器收到后从 accept()返回。数据传输的过程:建立连接后,TCP 协议提供全双工的通信服务,但是一般的客户端/服务器程序的流程是由客户端主动发起请求,服务器被动处理请求,一问一答的

2、方式。因此,服务器从 accept()返回后立刻调用read(),读 socket 就像读管道一样,如果没有数据到达就阻塞等待,这时客户端调用 write()发送请求给服务器,服务器收到后从 read()返回,对客户端的请求进行处理,在此期间客户端调用 read()阻塞等待服务器的应答,服务器调用 write()将处理结果发回给客户端,再次调用 read()阻塞等待下一条请求,客户端收到后从 read()返回,发送下一条请求,如此循环下去。如果客户端没有更多的请求了,就调用 close()关闭连接,就像写端关闭的管道一样,服务器的read()返回 0,这样服务器就知道客户端关闭了连接,也调用

3、close()关闭连接。注意,任何一方调用 close()后,连接的两个传输方向都关闭,不能再发送数据了。如果一方调用 shutdown()则连接处于半关闭状态,仍可接收对方发来的数据。在学习 socket API 时要注意应用程序和 TCP 协议层是如何交互的: *应用程序调用某个 socket 函数时 TCP 协议层完成什么动作,比如调用 connect()会发出 SYN 段 *应用程序如何知道 TCP 协议层的状态变化,比如从某个阻塞的 socket 函数返回就表明 TCP 协议收到了某些段,再比如 read()返回 0 就表明收到了 FIN 段看图所示的socket 通信过程图 12.

4、9 socket 的通信过程1.建立套接字在 sys/socket.h 中。int socket(int family, int type, int protocol);socket()打开一个网络通讯端口,如果成功的话,就像 open()一样返回一个文件描述符,应用程序可以像读写文件一样用 read/write 在网络上收发数据,如果 socket()调用出错则返回-1 。对于IPv4,family 参数指定为 AF_INET。对于 TCP 协议,type 参数指定为 SOCK_STREAM,表示面向流的传输协议。如果是 UDP 协议,则 type 参数指定为 SOCK_DGRAM,表示面向

5、数据报的传输协议。protocol 参数的介绍从略,指定为 0 即可。Linux 在利用 socket()系统调用建立新的套接字时,需要传递套接字的地址族标识符、套接字类型以及协议,其函数定义于 net/socket.c 中:asmlinkagelong sys_socket(int family, int type, int protocol)int retval;struct socket *sock;retval = sock_create(family, type, protocol,&sock);if (retval 0)goto out;retval = sock_map_fd(s

6、ock);if (retval 0)goto out_release;out:/* It may be already another descriptor 8) Not kernel problem. */ return retval;out_release:sock_release(sock);return retval;实际上,套接字对于用户程序而言就是特殊的已打开的文件。内核中为套接字定义了一种特殊的文件类型,形成一种特殊的文件系统 sockfs,其定义于 net/socket.c:static struct vfsmount *sock_mnt;static DECLARE_FSTY

7、PE(sock_fs_type, sockfs,sockfs_read_super, FS_NOMOUNT);在系统初始化时,要通过 kern_mount()安装这个文件系统。安装时有个作为连接件的 vfsmount数据结构,这个结构的地址就保存在一个全局的指针 sock_mnt 中。所谓创建一个套接字,就是在sockfs 文件系统中创建一个特殊文件,或者说一个节点,并建立起为实现套接字功能所需的一整套数据结构。所以,函数 sock_create()首先是建立一个 socket 数据结构,然后将其“映射”到一个已打开的文件中,进行 socket 结构和 sock 结构的分配和初始化。新创建的

8、BSD socket 数据结构包含有指向地址族专有的套接字例程的指针,这一指针实际就是proto_ops 数据结构的地址。BSD 套接字的套接字类型设置为所请求的 SOCK_STREAM 或 SOCK_DGRAM 等。然后,内核利用 proto_ops 数据结构中的信息调用地址族专有的创建例程。之后,内核从当前进程的 fd 向量中分配空闲的文件描述符,该描述符指向的 file 数据结构被初始化。初始化过程包括将文件操作集指针指向由 BSD 套接字接口支持的 BSD 文件操作集。所有随后的套接字(文件)操作都将定向到该套接字接口,而套接字接口则会进一步调用地址族的操作例程,从而将操作传递到底层地

9、址族,如图 12.10 所示。实际上,socket 结构与 sock 结构是同一事物的两个方面。如果说 socket 结构是面向进程和系统调用界面的,那么 sock 结构就是面向底层驱动程序的。可是,为什么不把这两个数据结构合并成一个呢?我们说套接字是一种特殊的文件系统,因此,inode 结构内部的 union 的一个成分就用作 socket 结构,其定义如下:struct inode union struct socket socket_i;由于套接字操作的特殊性,这个结构中需要大量的结构成分。可是,如果把这些结构成分全都放在socket 结构中,则 inode 结构中的这个 union 就

10、会变得很大,从而 inode 结构也会变得很大,而对于其他文件系统,这个 union 成分并不需要那么庞大。因此,就把套接字所需的这些结构成分拆成两部分,把与文件系统关系比较密切的那一部分放在 socket 结构中,把与通信关系比较密切的那一部分则单独组成一个数据结构,即 sock 结构。由于这两部分数据在逻辑上本来就是一体的,所以要通过指针互相指向对方,形成一对一的关系。2在 INET BSD 套接字上绑定(bind)地址为了监听传入的 Internet 连接请求,每个服务器都需要建立一个 INET BSD 套接字,并且将自己的地址绑定到该套接字。绑定操作主要在 INET 套接字层中进行,还

11、需要底层 TCP 层和 IP 层的某些支持。将地址绑定到某个套接字上之后,该套接字就不能用来进行任何其他的通讯,因此,该 socket 数据结构的状态必须为 TCP_CLOSE。传递到绑定操作的 sockaddr 数据结构中包含要绑定的 IP 地址,以及一个可选的端口地址。通常而言,要绑定的地址应该是赋予某个网络设备的 IP 地址,而该网络设备应该支持 INET 地址族,并且该设备是可用的。利用 ifconfig 命令可查看当前活动的网络接口。被绑定的 IP 地址保存在 sock 数据结构的 rcv_saddr 和 saddr 域中,这两个域分别用于哈希查找和发送用的 IP 地址。端口地址是可

12、选的,如果没有指定,底层的支持网络会选择一个空闲的端口。int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen);服务器程序所监听的网络地址和端口号通常是固定不变的,客户端程序得知服务器程序的地址和端口号后就可以向服务器发起连接,因此服务器需要调用 bind 绑定一个固定的网络地址和端口号。bind()成功返回 0,失败返回-1。bind()的作用是将参数 sockfd 和 myaddr 绑定在一起,使 sockfd 这个用于网络通讯的文件描述符监听 myaddr 所描述的地址和端口号。前面讲过,struct

13、sockaddr *是一个通用指针类型,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,网络地址为 IN

14、ADDR_ANY,这个宏表示本地的任意 IP 地址,因为服务器可能有多个网卡,每个网卡也可能绑定多个 IP 地址,这样设置可以在所有的 IP 地址上监听,直到与某个客户端建立了连接时才确定下来到底用哪个 IP 地址,端口号为 SERV_PORT,我们定义为 8000。当底层网络设备接受到数据包时,它必须将数据包传递到正确的 INET 和 BSD 套接字以便进行处理,因此,TCP 维护多个哈希表,用来查找传入 IP 消息的地址,并将它们定向到正确的socket/sock 对。TCP 并不在绑定过程中将绑定的 sock 数据结构添加到哈希表中,在这一过程中,它仅仅判断所请求的端口号当前是否正在使用

15、。在监听操作中,该 sock 结构才被添加到 TCP 的哈希表中。3在 INET BSD 套接字上建立连接(connect)创建一个套接字之后,该套接字不仅可以用于监听入站的连接请求,也可以用于建立出站的连接请求。不论怎样都涉及到一个重要的过程:建立两个应用程序之间的虚拟电路。出站连接只能建立在处于正确状态的 INET BSD 套接字上,因此,不能建立于已建立连接的套接字,也不能建立于用于监听入站连接的套接字。也就是说,该 BSD socket 数据结构的状态必须为 SS_UNCONNECTED。在建立连接过程中,双方 TCP 要进行三次“ 握手”,具体过程在 本章第二节网络协议一文中有详细介

16、绍。如果 TCP sock 正在等待传入消息,则该 sock 结构添加到 tcp_listening_hash 表中,这样,传入的 TCP 消息就可以定向到该 sock 数据结构。由于客户端不需要固定的端口号,因此不必调用 bind(),客户端的端口号由内核自动分配。注意,客户端不是不允许调用 bind(),只是没有必要调用 bind()固定一个端口号,服务器也不是必须调用bind(),但如果服务器不调用 bind(),内核会自动给服务器分配监听端口,每次启动服务器时端口号都不一样,客户端要连接服务器就会遇到麻烦。int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen);客户端需要调用 connect()连接服务器,connect 和 bind 的参数形式一致,区别在于 bind 的参数是自己的地址,而 connect 的参数是对方的地址。 con

展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 中学教育 > 试题/考题

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号