文章目录
- 网络通信概述
- ip地址
- ip的作用
- ip地址的分类
- 私有ip
- 掩码和广播地址
- linux 命令(ping ifconfig)
- 查看或配置网卡信息:ifconfig(widows 用ipconfig)
- 测试远程主机连通性:ping
- 路由查看
- 端口
- 端口是怎样分配的
- 知名端口
- 动态端口
- 查看端口及谁使用了端口
- 查询端口状态netstat / 一般搭配grep一起使用
- 查看具体的端口由什么进程在使用lsof
- 路由追踪(traceroute)
- 总结
- socket简介
- TCP和UDP的使用场景
- 不同电脑上的进程之间如何通信
- 什么是 socket
- 创建 socket
- python3编码转换
- udp
- udp基本网络框架
- udp服务端代码
- udp客户端代码
- 小tips
- 阿里云服务器udp演示
- 补充(kill)
- tcp
- tcp的特点
- 面向连接
- 可靠传输
- TCP 与 UDP 的不同点
- TCP的基本网络框架
- TCP服务端代码
- 关于listen
- TCP用户端代码
- tcp示例样例--文件下载器雏形
- 补充--高级传参
- 代码
- tcp的三次握手
- tcp的四次挥手
- tcp的长连接和短连接
- 通信的全程图
- 短连接和长连接的区别
- 短链接的操作步骤:
- 长连接的操作步骤:
- TCP 长/短连接的优点和缺点
- TCP 长/短连接的应用场景
网络通信概述
WAN:
一种跨越大的、地域性的计算机网络的集合。通常跨越省、市,甚至一个国家。广域网包括大大小小不同的子网,子网可以是局域网,也可以是小型的广域网
LAN:
顾名思义,LAN是指在某一区域内由多台计算机互联成的计算机组。一般是方圆几千米以内。局域网可以实现文件管理、应用软件共享、打印机共享、工作组内的日程安排、电子邮件和传真通信服务等功能。局域网是封闭型的,可以由办公室内的两台计算机组成,也可以由一个公司内的上千台计算机组成。
ip地址
ip的作用
IP地址的作用是唯一标识网络中的设备,使得设备能够在网络中进行通信。它类似于门牌号码,用于确定设备的位置和身份。IP地址可以用于寻找和连接其他设备,发送和接收数据包,并在Internet上进行通信。IP地址的作用还包括路由选择、网络管理和安全控制等方面。
一句话的话ip 地址就是用来在网络中标记一台电脑,比如 192.168.1.1在本地局域网上是唯一的。
ip地址的分类
每一个 IP 地址包括两部分:网络地址和主机地址
私有ip
在这么多网络 IP 中,国际规定有一部分 IP 地址是用于我们的局域网使用,也就是属于私网 IP,不在公网中使用的,它们的范围是:
10.0.0.0~10.255.255.255
172.16.0.0~172.31.255.255
192.168.0.0~192.168.255.255
值得关注的是:
IP 地址 127.0.0.1~127.255.255.255 用于回路测试,
如:127.0.0.1 可以代表本机 IP 地址,用 http://127.0.0.1 就可以测试本机中配
置的 Web 服务器
掩码和广播地址
子网掩码:互联网是由许多小型网络构成的,每个网络上都有许多主机,这样便构成了一个有层次的结构。IP地址在设计时就考虑到地址分配的层次特点,将每个IP地址都分割成网络号和主机号两部分,以便于IP地址的寻址操作。
子网掩码有两个功能:
- 将一个IP地址划分为网络位和主机位
- 判断两个设备是否在同一个子网
广播地址是专门用于同时向网络中所有工作站进行发送的一个地址。
在使用TCP/IP 协议的网络中,主机标识段host ID 为全1 的IP 地址为广播地址,广播的分组传送给host ID段所涉及的所有计算机。例如,对于10.1.1.0 (255.255.255.0 )网段,其广播地址为10.1.1.255 (255 即为2 进制的11111111 ),当发出一个目的地址为10.1.1.255 的分组(封包)时,它将被分发给该网段上的所有计算机。
linux 命令(ping ifconfig)
查看或配置网卡信息:ifconfig(widows 用ipconfig)
测试远程主机连通性:ping
通常用 ping 来检测网络是否正常,直接ping (网址/ip)即可
路由查看
route -n
内核 IP 路由表
目标 | 网关 | 子网掩码 |
---|---|---|
0.0.0.0 | 192.168.19.2 | 0.0.0.0 |
169.254.0.0 | 0.0.0.0 | 255.255.0.0 |
192.168.19.0 | 0.0.0.0 | 255.255.255.0 |
0.0.0.0 代表任意目的地
网关就是转发数据的设备
路由表是从上往下匹配的
端口
在unix/linux的使用中,实际上网络沟通是进程与进程之间进行的沟通,举个例子就是,我电脑上的QQ进行和别人电脑上的QQ进程进行沟通,而端口负责的就是在同一ip下的,关于具体到进程这一方面的交互。
端口是通过端口号来标记的,端口号只有整数,范围是从 0 到 65535
注意:端口数不一样的 unix/linix 系统不一样,还可以手动修改
端口是怎样分配的
端口号不是随意使用的,而是按照一定的规定进行分配。端口的分类标准有好几种,我们这里不做详细讲解,只介绍一下知名端口和动态端口
知名端口
知名端口是众所周知的端口号,范围从 0 到 1023
端口号 | 说明 |
---|---|
80 | 端口分配给 HTTP 服务 |
21 | 端口分配给 FTP 服务 |
22 | 端口分配给 ssh |
443 | 端口分配给https |
一般情况下,如果一个程序需要使用知名端口的需要有 root 权限
动态端口
动态端口的范围是从 1024 到 65535。之所以称为动态端口,是因为它一般不固定分配某种服务,而是动态分配。
动态分配是指当一个系统程序或应用程序程序需要网络通信时,它向主机申请一个端口,主机从可用的端口号中分配一个供它使用。当这个程序关闭时,同时也就释放了所占用的端口号。
查看端口及谁使用了端口
查询端口状态netstat / 一般搭配grep一起使用
netstat [-an]
查看端口状态
例子:
此时就可以简单看出在我们本地的widows是59173端口和服务器的22号端口进行相连。
查看具体的端口由什么进程在使用lsof
sudo lsof -i [tcp/udp]:2425 必须是 root 才能查看
sudo lsof -i tcp:22 查看哪一个进程用了这个端口
ps -elf |grep udp_server 查看某个进程是否还在
参考链接:
lsof
ps
路由追踪(traceroute)
使用方式:
# window下
tracert ip地址
# linux下
traceroute ip地址
总结
端口有什么用呢 ? 我们知道,一台拥有 IP 地址的主机可以提供许多服务,比如HTTP(万维网服务)、FTP(文件传输)、SMTP(电子邮件)等,这些服务完全可以通过 1 个 IP 地址来实现。那么,主机是怎样区分不同的网络服务呢?显然不能只靠 IP 地址,因为 IP 地址与网络服务的关系是一对多的关系。实际上是通过“IP 地址+端口号”来区分不同的服务的。 需要注意的是,端口并不是一一对应的。比如你的电脑作为客户机访问一台 WWW 服务器时,WWW 服务器使用“80”端口与你的电脑通信,但你的电脑则可能使用“3457”这样的端口。
socket简介
先简单介绍一下tcp和udp,后面部分会再进行详细的介绍:
TCP是面向连接的协议,在收发数据前必须和对方建立可靠的连接,建立连接的3次握手、断开连接的4次挥手,为数据传输打下可靠基础;UDP是一个面向无连接的协议,数据传输前,源端和终端不建立连接,发送端尽可能快的将数据扔到网络上,接收端从消息队列中读取消息段。
由此可以看出:UDP 服务器端不需要调用监听(listen)和接收(accept)客户端连接,而客户端也不需要连接服务器端(connect)。UDP协议中,任何一方建立socket后,都可以用sendto发送数据、用recvfrom接收数据,不必关心对方是否存在,是否发送了数据。
TCP和UDP的使用场景
为了实现TCP网络通信的可靠性,增加校验和、序号标识、滑动窗口、确认应答、拥塞控制等复杂的机制,建立了繁琐的握手过程,增加了TCP对系统资源的消耗;TCP的重传机制、顺序控制机制等对数据传输有一定延时影响,降低了传输效率。TCP适合对传输效率要求低,但准确率要求高的应用场景,比如万维网(HTTP)、文件传输(FTP)、电子邮件(SMTP)等。
UDP是无连接的,不可靠传输,尽最大努力交付数据,协议简单、资源要求少、传输速度快、实时性高的特点,适用于对传输效率要求高,但准确率要求低的应用场景,比如域名转换(DNS)、远程文件服务器(NFS)等。
不同电脑上的进程之间如何通信
首要解决的问题是如何唯一标识一个进程,否则通信无从谈起!在 1 台电脑上可以通过进程号(PID)来唯一标识一个进程,但是在网络中这是行不通的。其实 TCP/IP 协议族已经帮我们解决了这个问题,网络层的“ip 地址”可以唯一标识网络中的主机,而传输层的“协议+端口”可以唯一标识主机中的应用进程(进程)。这样利用协议,ip 地址,端口就可以标识网络的进程了,网络中的进程通信就可以利用这个标志与其它进程进行交互。
什么是 socket
socket(简称 套接字) 是进程间通信的一种方式,它与其他进程间通信的一个主要不同是:它能实现不同主机间的进程间通信,我们网络上各种各样的服务大多都是基于Socket 来完成通信的:
例如我们每天浏览网页、QQ 聊天、收发 email 等等
创建 socket
在 Python 中 使用 socket 模块的函数 socket 就可以完成:
import socket
socket.socket(AddressFamily, Type)
说明:
函数 socket.socket 创建一个 socket,该函数带有两个参数:
- Address Family:可以选择 AF_INET(用于 Internet 进程间通信) 或者AF_UNIX(用于同一台机器进程间通信),实际工作中常用 AF_INET
- Type:套接字类型,可以是 SOCK_STREAM(流式套接字,主要用于 TCP 协议)或者 SOCK_DGRAM(数据报套接字,主要用于 UDP 协议)
创建一个 tcp socket(tcp 套接字)
import socket
# 创建 tcp 的套接字
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# ...这里是使用套接字的功能(省略)...
# 不用的时候,关闭套接字
s.close()
创建一个 udp socket(udp 套接字)
import socket
# 创建 udp 的套接字
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# ...这里是使用套接字的功能(省略)...
# 不用的时候,关闭套接字
s.close()
由此可以看出一个比较不一样的地方就是使用的常数是很不一样的。socket.SOCK_STREAM和socket.SOCK_DGRAM
python3编码转换
str->bytes:encode 编码
bytes->str:decode 解码
udp
udp基本网络框架
udp服务端代码
根据上文的流程图可以写出下文的代码
import socket
# 1.创建一个udp的服务器名为server
server=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
# 2.服务器绑定ip地址与端口
addr=('本地的地址',2000) #写1024以上端口
server.bind(addr) #失败直接抛异常
# 3.阻塞接受客户端的消息
# recvfrom函数返回的list中 list[0]返回的是客户端传回的数据,list[1]传回的是客户端的ip地址端口的消息
data,client_addr=s.recvfrom(5) # 5代表接的字节大小
print(data.decode('utf8'))
print(client_addr)
# 4.根据返回的消息处理请求。略
# 5.返回消息给客户端
server.sendto('很牛'.encode('utf8'),client_addr)
# 6.关闭服务器
server.close()
小补充:
实际上当你运行服务端的时候,你去自己电脑命令窗口下netstat -an实际上就会发现你的对应的端口就会打开:
udp客户端代码
根据上文的流程图可以写出下文的代码
import socket
# 1.创建一个udp客户端命名为client
client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
# 2.发送消息给服务端
dest_addr=('192.168.0.100',2000)
# client.sendto(b'hello',dest_addr)
client.sendto('howare'.encode('utf8'),dest_addr) #放汉字还是不放都行
# 3.阻塞等待服务端发消息给客户端
data,_=client.recvfrom(100)
print(data.decode('utf8'))
# 4.后续处理,略
# 5.关闭客户端
client.close() #关闭时端口会释放
小tips
- 值得关注的是服务器端的端口需要写死端口号,这样子才可以让客户端有一个固定的访问地址。而客户端的端口号是随机生成的,所以一般来说,客户端都需要通过recvfrom中的list[1]中的元素进行知道是哪个客户端发送的消息,然后对应相对应的处理。
- 当 UDP recvfrom 内部要接的大小,少于发送的字节数,windows 报错,Linux 是不会报错,没有拿的数据直接丢弃。
- 如果采用阿里云或者腾讯云的,我们需要将服务器的那个文件放到服务器上运行,然后==服务器的bind绑定的是ifconfig中内网的值,而我们客户端发送数据sendto的时候的地址是服务器的公网的值。==下面是简单的步骤演示:
阿里云服务器udp演示
首先先配置安全组:
然后将服务器文件拖动到服务器上,并修改服务器的那个地方对应的内网ip:
先输入ifconfig:
然后修改ip:
运行服务端:
修改客户端ip:
不暴露信息,自己取修改吧,从阿里云服务器上了解其公网ip,然后在客户端上进行修改即可
运行客户端:
补充(kill)
端口占用怎么办?
ps -elf 查看进程
kill -9 进程 i
给个样例:
ps -elf|grep 2000 #查看2000号端口的使用者是谁
kill -9 对应的PID
tcp
TCP 协议,传输控制协议是一种面向连接的、可靠的、基于字节流的传输层通信协议
TCP 通信模型中,在通信开始之前,一定要先建立相关的链接,才能发送数据,类似于生活中,“打电话”“,而udp 通信模型中,在通信开始之前,不需要建立相关的链接,只需要发送数据即可,类似于生活中,“写信””
tcp的特点
面向连接
通信双方必须先建立连接才能进行数据的传输,双方都必须为该连接分配必要的系统内核资源,以管理连接的状态和连接上的传输。双方间的数据传输都可以通过这一个连接进行。完成数据交换后,双方必须断开此连接,以释放系统资源。
这种连接是一对一的,因此 TCP 不适用于广播的应用程序,基于广播的应用程序请使用 UDP 协议。
可靠传输
- TCP 采用发送应答机制
TCP 发送的每个报文段都必须得到接收方的应答才认为这个 TCP 报文段传输成功,每一个报文都有一个序列号。 - 超时重传
发送端发出一个报文段之后就启动定时器,如果在定时时间内没有收到应答就重新发送这个报文段。TCP 为了保证不发生丢包,就给每个包一个序列号,同时序列号也保证了传送到接收端实体的包的按序接收。然后接收端实体对已成功收到的包发回一个相应的确认(ACK);如果发送端实体在合理的往返时延(RTT)内未收到确认,那么对应的数据包就被假设为已丢失将会被进行重传。 - 错误校验
TCP 用一个校验和函数来检验数据是否有错误;在发送和接收时都要计算校验和。 - 流量控制和阻塞管理
滑动窗口:控制双方的发送接收速度
流量控制用来避免主机发送得过快而使接收方来不及完全收下。
TCP 与 UDP 的不同点
• 面向连接(确认有创建三方交握,连接已创建才作传输。)
• 有序数据传输
• 重发丢失的数据包
• 舍弃重复的数据包
• 无差错的数据传输
• 阻塞/流量控
TCP的基本网络框架
TCP服务端代码
根据上文的流程图可得下面的代码:
import socket
def tcp_server():
# 1.创建tcp的socket服务端
# SOCK_STREAM代表tcp的socket
server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 2.绑定地址,注意和udp不同的是,此时我们去查看端口,此端口是没有激活的
addr=('192.168.5.7',2000)
server.bind(addr) #绑定,端口并没有激活
# 3.监听端口
server.listen(128) #listen时,端口才激活
# 4.接受沟通,创建与相对应客户端相连接的套接字
new_client,client_addr=server.accept()
print(client_addr)
# 5.截下来就是相对应的别的操作了可以send也可以recv,不过需要和客户端相对应。
# 接下来就可以进行send,recv操作
new_client.send('helloworld'.encode('utf8'))
data=new_client.recv(100)
print(data.decode('utf8'))
# 6.关闭服务器,注意要先关闭client再关闭server
new_client.close()
server.close()
if __name__ == '__main__':
tcp_server()
关于listen
在socket中,listen函数的参数数字表示等待连接队列的最大长度。具体来说,它指定了服务器端能够同时处理的最大连接数。
当一个服务器端的socket调用listen函数时,它会开始监听指定的端口号,并等待客户端的连接请求。listen函数的参数指定了服务器端能够接受的最大连接数。如果连接请求的数量超过了这个限制,那些超出的连接请求将被服务器端忽略或拒绝。
例如,如果listen函数的参数设置为5,那么服务器端最多能够同时处理5个连接请求。如果有第6个连接请求到达,它将被服务器端忽略或拒绝。
需要注意的是,listen函数的参数值并不代表服务器端能够处理的并发连接数。实际上,服务器端能够处理的并发连接数取决于多个因素,如服务器的硬件性能、操作系统的限制等。listen函数的参数值只是指定了服务器端能够接受的最大连接数,而并不代表服务器端能够同时处理这么多连接。
TCP用户端代码
根据上文的流程图可得下面的代码:
import socket
def tcp_client():
# 1.创建socket对象--客户端
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2.将客户端和服务端连接,与udp不同的是,udp不需要连接,udp使用sendto加上地址进行传送数据
dest_addr = ('192.168.5.7', 2000)
client.connect(dest_addr)
# 3.后续的recv和send操作,与服务器对应
data=client.recv(5)
print(data.decode('utf8'))
client.send('我是男生abc123'.encode('utf8'))
# 4.关闭客户端
client.close()
if __name__ == '__main__':
tcp_client()
tcp示例样例–文件下载器雏形
补充–高级传参
补充知识:
import sys
for i in sys.argv:
print(i)
此时转到终端输入:
python 这个文件
得到的输出是:
这个文件
也就是说,我们可以通过后续多加几个str类型数据,从而得到相对应的配置属性输入:
python 这个文件 ip port
输出:
这个文件 ip port
代码
服务端:
from socket import *
import sys
def get_file_content(file_name):
"""获取文件的内容"""
try:
with open(file_name, "rb") as f:
content = f.read()
return content
except:
print("没有下载的文件:%s" % file_name)
def main():
if len(sys.argv) != 2:
print("请按照如下方式运行:python3 xxx.py 7890")
return
else:
# 运行方式为python3 xxx.py 2000
port = int(sys.argv[1])
# 创建socket
tcp_server_socket = socket(AF_INET, SOCK_STREAM)
# 本地信息
address = ('', port)
# 绑定本地信息
tcp_server_socket.bind(address)
# 将主动套接字变为被动套接字
tcp_server_socket.listen(128)
# 值得关注的是1->多体现在这里
while True:
# 等待客户端的链接,即为这个客户端发送文件
client_socket, clientAddr = tcp_server_socket.accept()
# 接收对方发送过来的数据
recv_data = client_socket.recv(1024) # 接收1024个字节
file_name = recv_data.decode("utf-8")
print("对方请求下载的文件名为:%s" % file_name)
file_content = get_file_content(file_name)
# 发送文件的数据给客户端
# 因为获取打开文件时是以rb方式打开,所以file_content中的数据已经是二进制的格式,因此不需要encode编码
if file_content:
client_socket.send(file_content)
# 关闭这个套接字
client_socket.close()
# 关闭监听套接字
tcp_server_socket.close()
if __name__ == "__main__":
main()
客户端:
from socket import *
def main():
# 创建socket
tcp_client_socket = socket(AF_INET, SOCK_STREAM)
# 目的信息
server_ip = '192.168.5.7'
server_port = 2000
# 链接服务器
tcp_client_socket.connect((server_ip, server_port))
# 输入需要下载的文件名
file_name = input("请输入要下载的文件名:")
# 发送文件下载请求
tcp_client_socket.send(file_name.encode("utf-8"))
# 接收对方发送过来的数据,最大接收1024个字节(1K)
recv_data = tcp_client_socket.recv(1024)
# print('接收到的数据为:', recv_data.decode('utf-8'))
# 如果接收到数据再创建文件,否则不创建
if recv_data:
with open("[接收]"+file_name, "wb") as f:
f.write(recv_data)
# 关闭套接字
tcp_client_socket.close()
if __name__ == "__main__":
main()
tcp的三次握手
三次握手是TCP协议中建立可靠连接的过程。在客户端和服务器之间建立连接之前,必须进行三次握手来确保双方都能够正常通信。下面是三次握手的详细步骤:
第一次握手(SYN):客户端发送一个SYN(同步)包给服务器端,请求建立连接。这个包包含一个随机生成的初始序列号(ISN)用于标识数据流的起始位置。
第二次握手(SYN-ACK):服务器接收到客户端的SYN包后,会发送一个SYN-ACK(同步-确认)包给客户端。这个包中会确认客户端的SYN,并且也会包含一个随机生成的ISN作为回应。
第三次握手(ACK):客户端接收到服务器的SYN-ACK包后,会发送一个ACK(确认)包给服务器。这个包会确认服务器的SYN,并且也会包含服务器发送的ISN加1作为回应。
完成了这三次握手之后,双方就建立了可靠的连接,可以开始进行数据传输。此时,双方都可以相互发送数据包,并且可以保证数据的可靠传输。
三次握手的目的是确保双方都能够正常收发数据,并且能够彼此接收到对方发送的数据。通过三次握手,双方可以建立起一个可靠的连接,以便进行可靠的数据传输。同时,三次握手也可以防止已失效的连接请求被服务器端误认为是新的连接请求,从而提高了连接的可靠性和安全性。
如果只存在两次握手,会出现两种可能导致死锁的情况:
死锁情况一(客户端发起连接失败):客户端发送SYN包给服务器端,但由于网络延迟或其他原因,服务器端没有收到该包。此时,客户端认为连接已经建立,进入等待状态,而服务器端并不知道客户端的存在。由于缺乏第三次握手的确认,服务器端无法确定客户端是否成功接收到SYN-ACK包,并且无法发送ACK包。因此,客户端和服务器端都处于等待状态,形成了死锁。
死锁情况二(服务器端发起连接失败):服务器端发送SYN-ACK包给客户端,但由于网络延迟或其他原因,客户端没有收到该包。此时,服务器端认为连接已经建立,进入等待状态,而客户端并不知道服务器端的存在。由于缺乏第三次握手的ACK确认,客户端无法确定服务器端是否成功接收到ACK包,并且无法发送数据。因此,客户端和服务器端都处于等待状态,形成了死锁。
在这两种情况下,由于缺乏第三次握手的确认,双方无法确定连接是否成功建立,进入了死锁状态。为了避免这种情况,TCP协议采用了三次握手的机制,确保双方都能够正常通信并建立可靠的连接。
tcp的四次挥手
四次挥手是TCP协议中关闭连接的过程。在双方通信结束后,需要通过四次挥手来正常关闭连接,确保双方都能够完成数据的传输并释放相关资源。下面是四次挥手的详细步骤:
-
第一次挥手(FIN):当一个端口(通常是客户端)确定不再发送数据时,它会发送一个FIN(结束)包给另一个端口(通常是服务器端)。这个FIN包表示它已经完成了数据的发送。
-
第二次挥手(ACK):接收到FIN包的端口(通常是服务器端)会发送一个ACK(确认)包给对方,表示已经收到了FIN包。此时,服务器端进入半关闭状态,即不再接收来自客户端的数据,但仍可以向客户端发送数据。
-
第三次挥手(FIN):当另一个端口(通常是服务器端)也确定不再发送数据时,它会发送一个FIN包给另一个端口(通常是客户端)。这个FIN包表示它已经完成了数据的发送。
-
第四次挥手(ACK):接收到FIN包的端口(通常是客户端)会发送一个ACK包给对方,表示已经收到了FIN包。此时,客户端进入TIME_WAIT状态,等待一段时间后关闭连接,确保服务器端收到了ACK包。
完成了这四次挥手之后,双方都完成了数据的传输并释放了相关资源,连接被正常关闭。
四次挥手的目的是确保双方都能够完成数据的传输并释放相关资源。通过四次挥手,双方可以协商关闭连接,并确保对方收到了关闭连接的请求。这样可以避免数据的丢失或不完整,并且保证连接的可靠关闭。
补充:
TIME_WAIT状态的主要目的是确保在网络中所有的数据包都已经被接收和处理完毕,以避免数据的重复传输或混乱。在TIME_WAIT状态下,端口会保持开放并等待一段时间(通常是2倍的最大报文段生存时间(MSL)),以确保网络中已经没有挂起的数据包。
tcp的长连接和短连接
TCP 在真正的读写操作之前,server 与 client 之间必须建立一个连接,
当读写操作完成后,双方不再需要这个连接时它们可以释放这个连接,
连接的建立通过三次握手,释放则需要四次握手,
所以说每个连接的建立都是需要资源消耗和时间消耗的。
通信的全程图
短连接和长连接的区别
短链接实际上就是在通信的过程中只进行一次send和recv,而长连接进行多次
短链接的操作步骤:
建立连接——数据传输——关闭连接…建立连接——数据传输——关闭连接
长连接的操作步骤:
建立连接——数据传输…(保持连接)…数据传输——关闭连接
TCP 长/短连接的优点和缺点
- 长连接可以省去较多的 TCP 建立和关闭的操作,减少浪费,节约时间。对于频繁请求资源的客户来说,较适用长连接。
- client 与 server 之间的连接如果一直不关闭的话,会存在一个问题,随着客户端连接越来越多,server 早晚有扛不住的时候,这时候 server 端需要采取一些策略,如关闭一些长时间没有读写事件发生的连接,这样可以避免一些恶意连接导致server 端服务受损;如果条件再允许就可以以客户端机器为颗粒度,限制每个客户端的最大长连接数,这样可以完全避免某个蛋疼的客户端连累后端服务。
- 短连接对于服务器来说管理较为简单,存在的连接都是有用的连接,不需要额外的控制手段。但如果客户请求频繁,将在 TCP 的建立和关闭操作上浪费时间和带宽。
TCP 长/短连接的应用场景
长连接多用于操作频繁,点对点的通讯,而且连接数不能太多情况。每个 TCP 连接都需要三次握手,这需要时间,如果每个操作都是先连接,再操作的话那么处理速度会降低很多,所以每个操作完后都不断开,再次处理时直接发送数据包就 OK 了,不用建立 TCP 连接。例如:数据库的连接用长连接,如果用短连接频繁的通信会造成 socket 错误,而且频繁的 socket 创建也是对资源的浪费。
而像 WEB 网站的 http 服务一般都用短链接,因为长连接对于服务端来说会耗费一定的资源,而像 WEB 网站这么频繁的成千上万甚至上亿客户端的连接用短连接会更省一些资源,如果用长连接,而且同时有成千上万的用户,如果每个用户都占用一个连接的话,那可想而知吧。所以并发量大,但每个用户无需频繁操作情况下需用短连好。