in_family AF_INET servaddr.sin_addr.s_addr htonlINADDR_ANY servaddr.sin_port htonsSERV_PORT首先将整个结构体清零,然后设置地址类型为 AF_INET,网络地址为 INADDR_ANY,这个宏表示本地的任意 IP 地址,因为服务器可能有多个网卡,每个网卡也可能绑定多个 IP地址,这样设置可以在所有的 IP 地址上监听,直到与某个客户端建立了连接时才确定下来到底用哪个 IP 地址,端口号为 SERV_PORT,我们定义为 8000。
当底层网络设备接受到数据包时,它必须将数据包传递到正确的 INET 和 BSD 套接字以便进行处理,因此,TCP 维护多个哈希表,用来查找传入 IP 消息的地址,并将它们定向到正确的 socket/sock 对。
TCP 并不在绑定过程中将绑定的 sock 数据结构添加到哈希表中,在这一过程中,它仅仅判断所请求的端口号当前是否正在使用。
在监听操作中,该sock 结构才被添加到 TCP 的哈希表中。
3.在 INET BSD 套接字上建立连接(connect) 创建一个套接字之后,该套接字不仅可以用于监听入站的连接请求,也可以用于建立出站的连接请求。
不论怎样都涉及到一个重要的过程:建立两个应用程序之间的虚拟电路。
出站连接只能建立在处于正确状态的 INET BSD 套接字上,因此,不能建立于已建立连接的套接字,也不能建立于用于监听入站连接的套接字。
也就是说,该 BSD socket 数据结构的状态必须为 SS_UNCONNECTED。
在建立连接过程中,双方 TCP 要进行三次“握手”,具体过程在 本章第二节——网络协议一文中有详细介绍。
如果 TCP sock 正在等待传入消息,则该 sock 结构添加到tcp_listening_hash 表中,这样,传入的 TCP 消息就可以定向到该 sock 数据结构。
由于客户端不需要固定的端口号, 因此不必调用 bind,客户端的端口号由内核自动分配。
注意,客户端不是不允许调用 bind,只是没有必要调用 bind固定一个端口号,服务器也不是必须调用 bind,但如果服务器不调用 bind,内核会自动给服务器分配监听端口,每次启动服务器时端口号都不一样,客户端要连接服务器就会遇到麻烦。
int connectint sockfd const struct sockaddr servaddr socklen_t addrlen客户端需要调用 connect连接服务器,connect 和 bind 的参数形式一致,区别在于 bind的参数是自己的地址,而 connect 的参数是对方的地址。
connect成功返回 0,出错返回-1。
4.监听(listen) INET BSD 套接字 int listenint sockfd int backlog典型的服务器程序可以同时服务于多个客户端,当有客户端发起连接时,服务器调用的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 套接字上进行套接字操作。
int acceptint sockfd struct sockaddr cliaddr socklen_t addrlen三方握手完成后,服务器调用 accept接受连接,如果服务器调用 accept时还没有客户端的连接请求,就阻塞等待直到有客户端连接上来。
cliaddr 是一个传出参数,accept返回时传出客户端的地址和端口号。
addrlen 参数是一个传入传出参数(value-resultargument),传入的是调用者提供的缓冲区 cliaddr 的长度以避免缓冲区溢出问题,传出的是客户端地址结构体的实际长度 。
(有可能没有占满调用者提供的缓冲区) 如果给 cliaddr参数传 NULL,表示不关心客户端的地址。
注意:服务器接收到传入的请求后,如果能够接受该请求,服务器必须创建一个新的套接字来接受该请求并建立通讯连接(用于监听的套接字不能用来建立通讯连接),这时,服务器和客户就可以利用建立好的通讯连接传输数据。
上一篇:
平顶山学院教案
下一篇:
加强公路工程施工技术管理工作的措施