用线程解决Socket的getInputStream阻塞

  • Post author:
  • Post category:其他



1.背景:

在Socket通信中,当我们希望传输对象时,往往会用到输入/输出对象流。

ObjectInputStream in=new ObjectInputStream(socket.getInputStream());

ObjectOutputStream out=new ObjectOutputStream(socket.getOutputStream());


2.问题:

当程序调用socket.getInputStream()程序被被卡住。


3.原因:

socket.getInputStream()方法会导致程序阻塞,直到inputStream收到对方发过来的报文消息,程序才会继续往下执行。


public ObjectInputStream(InputStream in) throws IOException的官方API显示:Creates an ObjectInputStream that reads from the specified InputStream. A serialization stream header is read from the stream and verified. This constructor will block until the corresponding ObjectOutputStream has written and flushed the header. [1]


4.解决办法:

用线程的方式处理输入流。以下为示例代码:

//===============客户端代码 SocketClient.java=====================

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;


public class SocketClient {
	private Socket socket;
	private ObjectOutputStream out;
	private ObjectInputStream in;
	
	public SocketClient(){
		try {
			socket=new Socket("localhost",8081);
			out=new ObjectOutputStream(socket.getOutputStream());
			ReadThread readThread=new ReadThread();
			readThread.start();
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public void sendMessage(String msg){
		System.out.println("send message:"+msg);
		try {
			out.writeObject(msg);
			out.flush();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	class ReadThread extends Thread{
		boolean runFlag=true;
		public void run(){
			try {
				in=new ObjectInputStream(socket.getInputStream());
			} catch (IOException e1) {
				e1.printStackTrace();
			}
			while(runFlag){
				if(socket.isClosed()){
					return;
				}
				try {
					Object obj=in.readObject();
					if(obj instanceof String){
						System.out.println("Client recive:"+obj);
					}
				} 
				catch (IOException e) {
					e.printStackTrace();
				} 
				catch (ClassNotFoundException e) {
					e.printStackTrace();
				}
			}
		}
		
		public void exit(){
			runFlag=false;
		}
	}
	
	public static void main(String[] args) {
		SocketClient socketClient=new SocketClient();
		System.out.println("build socketClient");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		socketClient.sendMessage("Hello first.");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		socketClient.sendMessage("Hello second.");
	}

}

//============服务器端代码 SocketService.java===========

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.Date;

public class SocketService {
	ServerSocket serverSocket;
	
	public SocketService(){
		try {
			serverSocket=new ServerSocket(8081);
			while(true){
				Socket socket=serverSocket.accept();
				SocketServiceThread sst=new SocketServiceThread(socket);
				sst.start();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	class SocketServiceThread extends Thread{
		Socket socket;
		ObjectInputStream in;
		ObjectOutputStream out;
		boolean runFlag=true;
		public SocketServiceThread(Socket socket){
			if(null==socket){
				runFlag=false;
				return;
			}
			this.socket=socket;
			try {
				out=new ObjectOutputStream(socket.getOutputStream());
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		public void run(){
			if(null==socket){
				System.out.println("socket is null");
				return;
			}
			try {
				in=new ObjectInputStream(socket.getInputStream());
				while(runFlag){
					if(socket.isClosed()){
						System.out.println("socket is closed");
						return;
					}
					try {
						String obj=(String)in.readObject();
						if(obj instanceof String){
							System.out.println("Server recive:"+obj);
							Date date=new Date();
							out.writeObject("["+date+"]"+obj);
							out.flush();
						}
						else{
							System.out.println("Server recive:"+obj);
						}
					} 
					catch (ClassNotFoundException e) {
						e.printStackTrace();
					}
					catch (SocketException e){
						e.printStackTrace();
						return;
					}
					catch (IOException e){
						e.printStackTrace();
					}
				}
			} catch (IOException e1) {
				e1.printStackTrace();
				return;
			} catch (Exception e){
				return;
			}
		}
		
		public void exit(){
			runFlag=false;
		}
	}
	
	public static void main(String[] args) {
		System.out.println("===============start service===============");
		new SocketService();
	}

}


5.Socket通信注意事项

(1).writeXXX()方法后一般用flush()来把缓存内容发送出去。

(2).发送对象时,对象必须串行化,即该对象需要实现Serializable接口。



6.参考文献

[1]

Socket同时使用ObjectInputStream和ObjectOutputStream传输序列化对象时的顺序



版权声明:本文为wang_hua_yi原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。