回顾Java多线程在Socket通信中的使用
一、Java中socket编程
- socket = IP + 端口号,构成了在网络上唯一能被识别的标识符套接字。
- 通信的client和server双方都要获取对方的socket才能进行通信,socket之间的通信就是网络通信。
- socket之间就是普通的IO流传输。
- 这篇主要回顾TCP通信,也就是基于字节流的网络传输。
二、一般的单线程socket通信
建立连接的几个步骤:
- server 端声明 ServerSocket ,然后调用accept() 方法阻塞获得client的socket。
- 通过获得的socket调用 getInputStream()或者getOutputStream()获得输入输出流,进行数据传输。
- client 声明 Socket ,获得输入输出流,进行数据传输。
- 双方关闭socket
建立示例:
//服务器端 public class Server { public static void main(String[] args) { ServerSocket serverSocket; { try { serverSocket = new ServerSocket(8888); Socket socket = serverSocket.accept(); InputStream inputStream = socket.getInputStream(); OutputStream outputStream = socket.getOutputStream(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); String str; str = bufferedReader.readLine(); System.out.println(str); BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream)); bufferedWriter.write("Message Received"); bufferedWriter.flush(); socket.shutdownOutput(); } catch (IOException e) { e.printStackTrace(); } } } }public class Client { public static void main(String[] args) { Socket socket; { try { socket = new Socket("127.0.0.1",8888); InputStream inputStream = socket.getInputStream(); OutputStream outputStream = socket.getOutputStream(); BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream)); bufferedWriter.write("First message"); bufferedWriter.flush(); socket.shutdownOutput();//需要告诉server端client这边输出已经结束了,那边readline可以停止阻塞了 BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); System.out.println(bufferedReader.readLine()); } catch (IOException e) { e.printStackTrace(); } } } }运行结果:
注:
- socket中有两处是阻塞的
- server的accept(),调用这个方法之后,server在连接建立之前会一直阻塞等待
- IO中的read()方法,没有获得client端的消息发送结束标识,会一直阻塞等待,所以通过socket.shutdownOutput()方法结束client的socket,这样如果server端发送消息,client还是能收到的;如果直接close就不能收到了消息了。
- 只要有结束标识都会正常接收,另一个例子如下,通过newline增加分割
同步信息发送
public class Server { public static void main(String[] args) { ServerSocket serverSocket; { try { serverSocket = new ServerSocket(8888); Socket socket = serverSocket.accept(); InputStream inputStream = socket.getInputStream(); OutputStream outputStream = socket.getOutputStream(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); String str; while((str = bufferedReader.readLine()) != null){ System.out.println(str); } } catch (IOException e) { e.printStackTrace(); } } } }public class Client { public static void main(String[] args) { Socket socket; { try { socket = new Socket("127.0.0.1",8888); InputStream inputStream = socket.getInputStream(); OutputStream outputStream = socket.getOutputStream(); BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream)); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in,"UTF-8")); while(true){ bufferedWriter.write(bufferedReader.readLine()); bufferedWriter.newLine(); bufferedWriter.flush(); } } catch (IOException e) { e.printStackTrace(); } } } }主要问题:
只能是一对一通信,如果有A、B、C三者,A和B进行通信时,C对B发送消息B是收不到的,因为B会阻塞在A上,只有A关闭之后B才能和连接到它的client进行通信。
三、多线程socket通信
每个client通过server的一个线程进行通信。
使用线程池减少资源消耗,提高运行速度。
public class ThreadServer { public static void main(String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket(8899); ExecutorService executorService = Executors.newFixedThreadPool(10); while(true){ Socket socket = serverSocket.accept(); executorService.submit(new Runnable() { @Override public void run() { try { BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream())); String str; while((str = bufferedReader.readLine()) != null){ System.out.println(str); } } catch (IOException e) { e.printStackTrace(); } } }); } } }多个client进程都可以通过一个server线程进行通信
四、总结
- socket通信关键点为:socket的获取,IO流的应用,交互关系的明确,体会双向通信的时序关系。
- 明确怎么标识消息的间隔,使用shutdownOutput() 还是 close();bufferedStream的flush()问题,可能出现不显示的情况。
- 多线程中线程池的使用提高效率,减少资源的消耗。
版权声明:本文为PanicJaw原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。