java socket tcp重连等 处理粘包、沾包

本文分享了一段关于德州扑克棋牌游戏的Socket编程代码实现。主要内容包括客户端连接服务器的流程、重连机制及消息收发处理等关键环节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这个程序是前段时间写的德州扑克棋牌手程序,现在把以前写的东西整理出来。

下面是socket部分,还是很有帮助的:

private void start(String severIP, int serverPort) {
		// TODO Auto-generated method stub
		try {


			s = new Socket();
			s.setReuseAddress(true);
			remoteAddr = new InetSocketAddress(severIP,
					serverPort);
			int playport = Integer.parseInt(playPort);


			SocketAddress localAddr = new InetSocketAddress(playIp, playport);
			s.bind(localAddr);
			s.connect(remoteAddr);
			while(!s.isConnected()){
				try {
					Thread.sleep(100);
					s.connect(remoteAddr);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}			
            //register
			registerMsg(playId, playName);
			//receive Msg
			while (true) {
				while(!s.isConnected()){
					try {
						Thread.sleep(100);
						s.connect(remoteAddr);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}		
				receiveMsg();		
                if (stopexe) {
					break;
				}
			}
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
	
详细代码:https://github.com/gocaption/caption/blob/master/game

<think>我们被要求用中文回答,并且遵循特定的格式要求(如行内数学表达式用$...$,独立公式用$$...$$等)。但当前问题涉及TCP和拆,属于网络编程领域,不太涉及数学表达式,因此我们主要关注文本描述和代码示例。 根据用户的问题:“请问Java TCP连接如何解决和拆问题”,以及提供的引用,我们可以总结如下: 引用[2]提到了和拆的产生原因,引用[3]和[4]则提供了一些解决方案,特别是Netty框架中的解决方法。 解决方案通常括: 1. 消息长度固定:每个消息都是固定长度,不足则补0。这样接收方每次读取固定长度的数据即可。 2. 使用特殊分隔符:例如换行符作为消息结束标志,这样接收方按行读取。 3. 在消息头中定义长度字段:在消息前面加上消息的长度,这样接收方先读取长度,再根据长度读取内容。 在Java中,我们可以使用以下方法处理: - 对于原生Socket编程,我们需要在应用层设计协议,比如在数据前面加上长度字段,然后读取时先读长度,再读内容。 - 使用Netty等框架,它们提供了内置的编解码器来处理和拆问题,如LineBasedFrameDecoder(基于换行符)、DelimiterBasedFrameDecoder(基于分隔符)、LengthFieldBasedFrameDecoder(基于长度字段)等。 下面我们给出一个使用原生Socket解决的示例(使用长度字段)和一个使用Netty的示例。 ### 使用原生Socket解决(长度字段法) 在发送数据时,先发送数据的长度(固定字节,比如4个字节),再发送数据本身。接收方先读取4个字节得到长度,再读取指定长度的数据。 发送方示例: ```java // 发送数据 OutputStream out = socket.getOutputStream(); byte[] data = ...; // 要发送的数据 int length = data.length; // 先发送长度(4个字节) out.write(intToBytes(length)); out.write(data); out.flush(); // 将int转换为字节数组(大端序) private static byte[] intToBytes(int value) { return new byte[] { (byte)(value >> 24), (byte)(value >> 16), (byte)(value >> 8), (byte)value }; } ``` 接收方示例: ```java // 接收数据 InputStream in = socket.getInputStream(); // 读取长度(4个字节) byte[] lenBytes = new byte[4]; readFully(in, lenBytes); // 确保读取4个字节 int length = bytesToInt(lenBytes); // 读取数据 byte[] data = new byte[length]; readFully(in, data); // 将字节数组转换为int(大端序) private static int bytesToInt(byte[] bytes) { return (bytes[0] << 24) | ((bytes[1] & 0xFF) << 16) | ((bytes[2] & 0xFF) << 8) | (bytes[3] & 0xFF); } // 确保读取指定长度的数据 private static void readFully(InputStream in, byte[] buffer) throws IOException { int bytesRead = 0; while (bytesRead < buffer.length) { int count = in.read(buffer, bytesRead, buffer.length - bytesRead); if (count == -1) { throw new EOFException("Unexpected end of stream"); } bytesRead += count; } } ``` ### 使用Netty解决 Netty提供了多种解码器来处理和拆问题。这里以LengthFieldBasedFrameDecoder为例,它通过在消息头中定义长度字段来标识消息的总长度。 服务端示例: ```java EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) { ch.pipeline().addLast( // 最大帧长度、长度字段偏移量、长度字段长度 new LengthFieldBasedFrameDecoder(1024 * 1024, 0, 4, 0, 4), new LengthFieldPrepender(4), // 在发送的消息前加上4字节的长度字段 new StringDecoder(CharsetUtil.UTF_8), new StringEncoder(CharsetUtil.UTF_8), new YourBusinessHandler() // 你的业务处理器 ); } }); ChannelFuture f = b.bind(8080).sync(); f.channel().closeFuture().sync(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } ``` 其中,LengthFieldBasedFrameDecoder用于解码,它会根据长度字段的值来拆分接收到的ByteBuf。LengthFieldPrepender用于编码,它在发送的消息前面加上4个字节的长度字段。 ### 总结 在Java处理TCP和拆问题,主要通过在应用层设计协议来解决。常见的方法有: 1. 固定消息长度:简单但不够灵活。 2. 使用分隔符:例如换行符,适合文本协议。 3. 使用长度字段:在消息头中指定消息长度,这是最常用的方法。 对于原生Socket编程,需要手动处理长度字段的读写。而使用Netty框架,则可以利用其内置的编解码器简化开发[^3][^4]。 §§相关问题§§ 1. 除了长度字段法,还有哪些常见的解决的方法? 2. Netty中还有哪些处理的编解码器? 3. 在UDP协议中是否存在和拆问题?</think>### Java TCP连接中和拆问题的解决方案 在Java TCP通信中,(多个数据被合并接收)和拆(单个数据被拆分成多次接收)是常见问题。以下是核心解决方案: #### 1. **固定消息长度法** - 发送方固定每个消息长度(不足补0),接收方按固定长度读取 ```java // 发送端 byte[] data = "Hello".getBytes(); byte[] fixedData = Arrays.copyOf(data, FIXED_LENGTH); // 补0至固定长度 out.write(fixedData); // 接收端 byte[] buffer = new byte[FIXED_LENGTH]; in.read(buffer); String msg = new String(buffer).trim(); // 去除补位0 ``` #### 2. **特殊分隔符法** - 使用特定字符(如换行符`\n`)作为消息边界 ```java // 发送端 out.write("Message1\n".getBytes()); out.write("Message2\n".getBytes()); // 接收端 (使用BufferedReader) BufferedReader reader = new BufferedReader(new InputStreamReader(in)); String msg = reader.readLine(); // 按行读取 ``` #### 3. **长度字段法(推荐)** - 在消息头添加长度字段,明确标识消息体长度 ```java // 发送端 byte[] data = "Hello".getBytes(); out.write(intToBytes(data.length)); // 写入4字节长度头 out.write(data); // 写入实际数据 // 接收端 byte[] lenBytes = new byte[4]; in.read(lenBytes); int length = bytesToInt(lenBytes); // 解析长度头 byte[] body = new byte[length]; in.read(body); // 按长度读取 ``` #### 4. **使用Netty框架的编解码器** Netty提供内置解决方案: - **`LineBasedFrameDecoder`**:换行符分隔 ```java pipeline.addLast(new LineBasedFrameDecoder(1024)); pipeline.addLast(new StringDecoder()); ``` - **`DelimiterBasedFrameDecoder`**:自定义分隔符 ```java ByteBuf delimiter = Unpooled.copiedBuffer("|".getBytes()); pipeline.addLast(new DelimiterBasedFrameDecoder(1024, delimiter)); ``` - **`LengthFieldBasedFrameDecoder`**:长度字段解码 ```java // 参数:最大长度、长度字段偏移量、长度字段长度 pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 0, 4)); pipeline.addLast(new LengthFieldPrepender(4)); // 编码器 ``` #### 关键原因与选择建议 | 方案 | 适用场景 | 优缺点 | |--------------------|---------------------------------|--------------------------| | 固定长度 | 消息长度固定的简单场景 | 实现简单,但灵活性差 | | 特殊分隔符 | 文本协议(如HTTP、FTP) | 易实现,需转义分隔符 | | 长度字段 | 二进制协议(主流方案) | 高效可靠,需额外头开销 | | Netty内置解码器 | 高性能网络应用 | 开发效率高,需引入框架 | > 选择建议: > - 简单场景:分隔符法(如`BufferedReader.readLine()`) > - 复杂系统:长度字段法 + Netty框架(解决边界问题)[^3][^4] > - 避免误区:调整TCP缓冲区大小无法根本解决问题,需应用层协议设计[^2]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值