Java 面试题 —— TCP 粘包、拆包问题

Java 面试题 —— TCP 粘包、拆包问题

1、粘包、拆包问题概况

正常情况:

​  服务端一共接收到客户端的两个数据包,两个数据包各自包含完整的消息。

在这里插入图片描述

粘包问题:

​  服务端一共接收到客户端的一个数据包,这个数据包共包含两条消息。

在这里插入图片描述

拆包问题:

​  服务端一共接收到客户端的两个数据包,第一个数据包只包含第一条消息的部分,第二个数据包共包含第一条消息的剩余部分和第二条消息。

在这里插入图片描述


2、产生原因
  • 应用程序写入的数据大于套接字缓冲区大小,这将会发生拆包问题;
  • 应用程序写入的数据小于套接字缓冲区大小,网卡将应用多次写入的数据发送到网络上,这将会发生粘包问题;
  • 进行 MSS(最大报文长度)大小的 TCP 分段,当 TCP 报文长度 - TCP 头部长度 > MSS 的时候,将发生拆包问题;
  • 接收方法不及时读取套接字缓冲区中的数据,这将发生粘包问题。

补充:

  • 滑动窗口 限制
  • MTU/MSS 限制
  • Nagle 算法


3、解决方案
  • 使用带消息头的协议

    ​  消息头存储消息开始标识及消息长度信息,接收端获取消息头的时候解析出消息长度,然后向后读取该长度的内容,位数不够补0。

  • 设置定长消息

    ​  接收端每次读取既定长度的内容作为一条完整消息,不足的空位补全。

  • 设置消息边界

    ​  可在报文末尾增加换行符表明一条完整的消息,这样接收端可以根据这个换行符来判断消息是否完整。


4、Netty 框架对 TCP 粘包拆包问题的解决工具
  • LineBasedFrameDecoder(基于换行符)
  • DelimiterBasedFrameDecoder(基于分隔符)
  • FixedLengthFrameDecoder(基于定长)

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 我可以为你提供一个简单的python代码示例,用来编写不粘包tcp服务端程序:import socket# 创建socket对象 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 绑定本机的IP和端口号 s.bind(('localhost', 8080))# 启动监听 s.listen(5)# 接受客户端连接 client, addr = s.accept()# 不断接收客户端发送的消息 while True: # 接受客户端发送的消息 data = client.recv(1024) # 如果客户端发送消息为空,则断开连接 if not data: break # 处理消息 print(data.decode('utf-8')) # 向客户端发送消息 client.send(b'Received your message!')# 关闭连接 client.close() s.close() ### 回答2: 编写不粘包TCP服务器程序,可以采用如下步骤: 1. 导入相关模块,如socket、struct和threading。 2. 创建一个socket对象,指定网络协议为TCP。 3. 绑定监听的主机地址和端口号。 4. 开始监听客户端的连接请求。 5. 定义一个处理客户端请求的线程函数,接收客户端发送的数据。 6. 使用struct模块来处理数据的字节序,将接收到的数据长度信息和实际数据分开。 7. 判断客户端是否关闭连接,如果关闭则退出线程。 8. 继续接收数据,直到接收完整所有数据。 9. 处理接收到的数据,可根据实际需求进行逻辑处理。 10. 向客户端发送响应数据。 11. 关闭与客户端的连接。 12. 循环等待其他客户端的连接请求。 以下是一个简单示例代码: ```python import socket import struct import threading def handle_client(client_socket): while True: try: # 接收4字节的数据,表示后续数据的长度 data_len = client_socket.recv(4) if not data_len: # 客户端关闭连接 break # 将数据长度转换为整数 data_len = struct.unpack('i', data_len)[0] # 继续接收数据,直到接收完整所有数据 data = b'' while len(data) < data_len: data += client_socket.recv(data_len - len(data)) # 处理接收到的数据 # ... # 响应数据 response = 'Server received: {}'.format(data.decode()) client_socket.send(response.encode()) except: break # 关闭与客户端的连接 client_socket.close() def main(): server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.bind(('0.0.0.0', 8888)) server_socket.listen(5) while True: client_socket, client_address = server_socket.accept() # 创建新的线程处理客户端请求 client_thread = threading.Thread(target=handle_client, args=(client_socket,)) client_thread.start() if __name__ == '__main__': main() ``` 该代码使用了多线程的方式来处理每个客户端的连接请求,确保可以同时处理多个客户端的请求,并且利用struct模块处理数据的字节序,避免粘包问题的发生。 ### 回答3: 要编写一个不粘包TCP服务端程序,可以使用Python的`socket`模块来实现。以下是一个示例代码: ```python import socket def handle_client(conn): while True: data = conn.recv(1024) # 接收数据,缓冲区大小为1024 if not data: break # 如果接收到空数据,跳出循环 message = data.decode().strip() print("Received message:", message) response = "Server has received your message: {}".format(message) conn.sendall(response.encode()) # 发送数据回客户端 conn.close() # 关闭连接 def start_server(host, port): with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.bind((host, port)) # 绑定主机和端口号 s.listen(5) # 监听客户端连接 print("Server listening on {}:{}".format(host, port)) while True: conn, addr = s.accept() # 接受客户端连接 print("Connected to client:", addr) handle_client(conn) if __name__ == '__main__': host = "localhost" port = 8080 start_server(host, port) ``` 以上代码实现了一个简单的TCP服务端程序。主要步骤包括:创建一个socket对象,绑定主机和端口号,监听客户端连接。接收到客户端连接后,使用一个循环来接收和发送数据。通过调用`recv`方法接收数据,并使用`decode`方法将接收到的字节数据转换为字符串。然后在控制台打印接收到的消息,并使用`sendall`方法将回复消息发送回客户端。如果接收到空数据,则跳出循环。最后,关闭连接。 这个程序可以确保在接收和发送数据时不会出现粘包问题。每次接收数据后,都会及时处理并发送回复,确保客户端和服务端之间的通信是一对一的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值