卷前:
- 首先得有IO的基本知识,然后了解下BIO、NIO、AIO;
- socket虽然用处很片面,不过它的TCP、UDP拓展思想还是值得去学习;
- 重点在于阻塞
问题:初学者在学习socket时,会发现serversocket.accept返回socket后(即客户端与服务端建立了连接),这里称为一个session,在每次收发数据时由于socket是bio模式,即阻塞型的流传输。
情境①:客户端socket.getOutputStream之后write和flush刷新发送流到服务端,服务端相应地serversocket.accept返回socket后socket.getInputStream后持续接收流,服务端想socket.getOutputStream发送流给客户端,客户端socket.getInputStream接收流。(基于TCP)
问题出现:服务端会由于接收流一直在进行,而导致之后的步骤是全部失效的!
解决办法:
- 使用多线程,接收与发送用不同的线程;
- 使用包装流,如printWriter中的println方法一次发送“一行”流,再bufferedReader中readline方法一次读取“一行”流。
思考:如解决方法2所述,似乎是带入了文件末尾流概念,即流有结束标志?
答案如下,的确InputStream中read方法也有说明在读取到文件流末尾会返回-1
-
- Returns:the next byte of data, or
-1
- if the end of the stream is reached.
但是问题在于socket阻塞的深度意思是这个-1根本不存在,于是也合理的解释了
而printWriter中的println方法
Terminates the current line by writing the line separator string. The line separator string is defined by the system property
line.separator
, and is not necessarily a single newline character ('\n'
).
bufferedReader中readline方法
Reads a line of text. A line is considered to be terminated by any one of a line feed (‘\n’), a carriage return (‘\r’), or a carriage return followed immediately by a linefeed.
- Returns:
- A String containing the contents of the line, not including any line-termination characters, or null if the end of the stream has been reached
由于存在换行符,流管道一直存在,但是双方默契地使用\n来作为阻塞打破。
情境②:客户端单线程or多线程方式发送流,服务端多线程方式接收流,服务端相应流并返回流,客户端接收到服务端响应流。(基于TCP)
问题:多线程的开启与处理方式!
服务端:
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; /** * 大二16年下半年写的套接字程序,基于TCP协议的socket通信 * 修改2018-4-9 * 修改2018-6- * 多线程 * @author pcshao */ public class serverT { private static ArrayList<Socket> sockets = new ArrayList<Socket>(); private static ServerSocket cServer = null; private static int ADDRESS_PORT = 61234; private static void transmitMessage(Socket socket){ new Thread(new Thread()){ public void run(){ try { while(true) { BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); String text = br.readLine(); System.out.print("接收成功,准备向客户端发送\n"); System.out.println(text); // socket.shutdownInput(); //多客户端同步消息 for(Socket s:sockets) { PrintWriter pw = new PrintWriter(s.getOutputStream()); pw.println(s.getInetAddress()+":"+text); pw.flush(); } } } catch (IOException e) { System.out.println("客户端发送丢失"); } }}.start(); } public static void main(String[] args){ try { cServer = new ServerSocket(ADDRESS_PORT); } catch (IOException e) { System.out.println("聊天服务开启失败"); } System.out.println("服务器开启.."); while(true){ //accept侦听,socket实例 Socket socket = null; try { socket = cServer.accept(); } catch (IOException e) { e.printStackTrace(); } //输出客户端IP System.out.println(socket.getInetAddress()+"\t已连接"); //接收到的每一个客户端socket存到链表里 sockets.add(socket); //转换信息 transmitMessage(socket); } } }
客户端:
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.Socket; import java.util.Scanner; public class Client_dual { public static Socket socket; private static String ADDRESS_URL = "127.0.0.1"; private static int ADDRESS_PORT = 61234; //socket创建 private static boolean createSocket() { try { socket = new Socket(ADDRESS_URL,ADDRESS_PORT); return true; } catch (IOException e) { System.out.println("连接失败,请检查服务端"); return false; } } //接收线程 public void receive(Socket socket) { new Thread(new Thread()) { public void run() { while(true) { try { BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); String text = ""; if((text = br.readLine())!="") System.out.println(text); } catch (IOException e) { e.printStackTrace(); } } } }.start(); } //发送线程 public void send(Socket socket) { new Thread(new Thread()) { public void run() { while(true) { try { PrintWriter pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream())); String text = new Scanner(System.in).nextLine(); pw.println(text); pw.flush(); }catch(IOException e) { e.printStackTrace(); } } } }.start(); } public static void main(String[] args) { Client_dual c = new Client_dual(); if(!createSocket()) System.out.println("创建失败"); c.receive(socket); c.send(socket); } }
卷后:
- 更新;
未经授权,禁止转载。