Socket通信异常关闭

项目场景:

自学javaSE过程中,发现尚硅谷第十六章网络聊天室没有关闭各种流,想自己手动添加代码关闭,结果出现异常


问题描述

例如:数据传输过程中数据不时出现丢失的情况,偶尔会丢失一部分数据
APP 中接收数据代码:
在这里插入图片描述

代码如下

package com.atguigu02.tcpudp.chat;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;

public class ChatServerTest {
	//这个集合用来存储所有在线的客户端
	static ArrayList<Socket> online = new  ArrayList<Socket>();
	
	public static void main(String[] args)throws Exception {
		//1、启动服务器,绑定端口号
		ServerSocket server = new ServerSocket(8989);
		
		//2、接收n多的客户端同时连接
		while(true){
			Socket socket = server.accept();
			
			online.add(socket);//把新连接的客户端添加到online列表中
			
			MessageHandler mh = new MessageHandler(socket);
			mh.start();//这个线程专门处理这个socket的信息
		}
	}
	
	static class MessageHandler extends Thread{
		private Socket socket;
		private String ip;
		
		public MessageHandler(Socket socket) {
			super();
			this.socket = socket;
		}

		public void run(){
			BufferedReader br = null;
			try {
				ip = socket.getInetAddress().getHostAddress();
				
				//插入:给其他客户端转发“我上线了”
				sendToOther(ip+"上线了");
				
				//(1)接收该客户端的发送的消息
				InputStream input = socket.getInputStream();
				InputStreamReader reader = new InputStreamReader(input);
				br = new BufferedReader(reader);
				
				String str;
				while((str = br.readLine())!=null){
					//(2)给其他在线客户端转发
					sendToOther(ip+":"+str);
				}
				
				sendToOther(ip+"下线了");
			} catch (IOException e) {
				try {
					sendToOther(ip+"掉线了");
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}finally{
				//从在线人员中移除我
				online.remove(socket);
				try {
					br.close();
				} catch (IOException e) {
					throw new RuntimeException(e);
				}
			}
		}
		
		//封装一个方法:给其他客户端转发xxx消息
		public void sendToOther(String message) throws IOException{
			//遍历所有的在线客户端,一一转发
			for (Socket on : online) {
				OutputStream every = on.getOutputStream();
				//为什么用PrintStream?目的用它的println方法,按行打印
				PrintStream ps = new PrintStream(every);
				
				ps.println(message);
				ps.close();
			}
		}
	}
}


public class ChatClientTest {
	public static void main(String[] args)throws Exception {
		//1、连接服务器
		Socket socket = new Socket("127.0.0.1",8989);
		
		//2、开启两个线程
		//(1)一个线程负责看别人聊,即接收服务器转发的消息
		Receive receive = new Receive(socket);
		receive.start();
		
		//(2)一个线程负责发送自己的话
		Send send = new Send(socket);
		send.start();
		
		send.join();//等我发送线程结束了,才结束整个程序
		
		socket.close();
	}
}
class Send extends Thread{
	private Socket socket;
	
	public Send(Socket socket) {
		super();
		this.socket = socket;
	}

	public void run(){
		try {
			Scanner input = new Scanner(System.in);

			OutputStream outputStream = socket.getOutputStream();
			//按行打印
			PrintStream ps = new PrintStream(outputStream);
			
			//从键盘不断的输入自己的话,给服务器发送,由服务器给其他人转发
			while(true){
				System.out.print("自己的话:");
				String str = input.nextLine(); //阻塞式的方法
				if("bye".equals(str)){
					break;
				}
				ps.println(str);//打印到socket.getOutputStream(),即往服务器端打印
			}
			
			input.close();
			ps.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
}
class Receive extends Thread{
	private Socket socket;
	
	public Receive(Socket socket) {
		super();
		this.socket = socket;
	}
	
	public void run(){
		try {
			InputStream inputStream = socket.getInputStream();
			Scanner input = new Scanner(inputStream);
			
			while(input.hasNextLine()){
				String line = input.nextLine();
				System.out.println(line);
			}
			input.close();
			inputStream.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

原因分析:

发现是sendToOther方法中关闭了printStream导致出现异常,尝试debug分析

在这里插入图片描述
运行服务器程序,发现从断点处直接执行到catch,说明此时已经断开了socket连接。

在这里插入图片描述
因为ps是处理流,关闭ps就会直接关闭every——Socket的输出流,我之前错误的理解了这样并不会影响Socket的输入流。
查阅资料发现Java的socket是一个全双工套接字,任何的输入流或输出流的close()都会造成Socket关闭。


解决方案:

使用socket.shutdownOutput()方法关闭套接字的输出流,使服务器知道输出流关闭,可以得到流末尾标志(-1)。
在这里插入图片描述
但是这样又会出现新的问题,关闭了输出流,那这个服务器就从以下断点直接跳出循环了,不会再等待读取数据了,客户端直接下线了

此时调用sendToOther因为输出流已关闭,直接就报错了。
在这里插入图片描述

所以还是不要乱改代码了,还是不在sendToOther中关闭这些流了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Rookie_gzzz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值