网络编程
什么是网络编程
网络编程就是在操作系统封装的TCP/IP协议栈的基础上,使用系统内核提供的socket api 进行应用开发.
TCP/IP四层协议栈 |
---|
应用层 |
传输层 |
网络层 |
链路层 |
网络编程的交互过程
TCP server | 说明 |
---|---|
socket() | 创建基于ipv4 or ipv6 的TCP socket数据结构,并和一个文件相绑定,同时给我们返回一个socketfd ,供后续函数使用 |
bind() | 将具体的IP PORT 绑定到该socket数据结构上 |
listen() | 将socket转为已监听套接字 。此时服务器可被动连接了 |
accept() | 等待全连接队列 里有socket,若在全连接队列里发现了socket,会为它创建一个 新文件 并分配新的acceptfd ,这个文件被称为已连接socket 此时,服务器与客户端就可以收发数据了 |
read() | 读取acceptfd 相关的socket接收缓冲区 ,如果socket接收缓冲区有数据,read() 返回,否则read() 会阻塞,直到socket接收缓冲区中有了数据。这个接收数据的过程是由内核TCP/IP协议栈 实现的。 |
write() | 写入socket发送缓冲区 ,如果此时发送缓冲区是满的,write () 则会被阻塞。写入socket发送缓冲区的数据,由内核TCP/IP协议 执行发往对端 |
close() | 完成四次挥手的第二阶段 |
TCP client | 说明 |
---|---|
socket() | 创建基于ipv4 or ipv6 的TCP socket数据结构,并和一个文件相绑定,同时给我们返回一个socketfd ,供后续函数使用 |
connect() | 发起主动连接。 客户端内核协议栈向服务器发送三次握手的第一步 SYN ;当服务器收到这个 SYN ,会把该socket放到半连接队列,并向客户端发送ACK,SYN 。当客户端接受到这个 ACK,SYN ,会向服务器发送ACK 。此时客户端connect() 函数返回,客户端认为三次握手完成。当服务器收到客户端传来的 ACK ,则将内核中的SOCKET放入全连接队列 ,服务器端accept() 函数返回 |
write() | 写入socket发送缓冲区 ,如果此时发送缓冲区是满的,write ()则会被阻塞。写入socket发送缓冲区的数据,由内核TCP/IP协议 执行发往对端 |
read() | 读取sokcetfd 相关的socket接收缓冲区 ,如果socket接收缓冲区有数据,read()返回,否则read()会阻塞,直到socket接收缓冲区中有了数据。这个接收数据的过程是由内核TCP/IP协议栈 实现的。 |
close() | 完成四次挥手的第一阶段。 内核协议协议栈会给服务器发送 FIN ,告知本端已经关闭;服务端收到客户端的 FIN ,内核协议栈会给客户端回复ACK ,同时read() 调用会返回0 ,这样服务器就知首客户端关闭连接了。这个时候,服务器会调用 close() ,开启四次挥手的第二个阶段,向客户端发送一个FIN 。客户端收到该 FIN 后,内核协议栈回复ACK ,并进入TIME_WAIT 状态,等待2MSL ,也就是60秒 |
read()
返回值:
大于0,读取成功;
=0,表示对端关闭连接,此时也要调用close()关闭连接;
<0,表示操作产生错误
write()
返回值:
大于等于0,表示写了多少字节的数据,0表示啥也没写
<0,表示操作产生错误
常见问题
-
客户端调用close()一定会发送FIN吗?
不一定。这要看该socketfd的引用计数器
有没有达到0
。如有多个进程共享这个socket文件,其中一个进程close()
,并不会向对端发送FIN
。
-
假如一方断网了,不能进行四次挥手,服务器会怎么处理?
- 未断开的一端,会保持
established
状态 - 未断开一端
开启keepalive
,则会定时向对方发送TCP保活segment
,对方协议栈回复RST
。这样未端开端就会知道对方的状态,若对方已离线,就关闭本端连接。 - 未断开一端
未开启keepalive
,只有在向对方发送数据时,会得到错误提示Connection reset by peer
,再次向对对方写数据,则会产生EPIPE
错误。 - 所以为了应对这种情况,服务器会在
应用层做保活心跳
,对于断开的client 做定时踢掉处理
。
-
客户端发送FIN之后,会发生什么?
- TCP是全双工通信,客断端发送
FIN
,就是告诉服务器它不再发送数据了,也不要在acceptefd上读数据了,若SERVER再次进行读取,只会得到0,也就是EOF.(四次挥手的第一阶段
) - 若SERVER还想给CLIENT发送数据,就继续给CLIENT发送就可以了。这也是常说的
TCP半关闭状态
。 - 如果SERVER也不需要再向CLIENT发送数据了,也需要调用close,向CLIENT发送FIN。(
四次挥手的第二阶段
)