最初的需求:
············代碼片段············
//1、創建一個服務端Socket,即ServerSocket對象,指定綁定的端口,並偵聽該端口
ServerSocket serverSocket = new ServerSocket(6666);
//2、調用accept()方法開始偵聽客戶端請求,創建Socket,等待客戶端的連接
System.out.println("===================服務器即將啓動,等待客戶端的連接===============");
Socket socket = serverSocket.accept();
System.out.println(new Date());
System.out.println("=========連接成功==========");
············代碼片段············
( 自己編寫的JAVA程序,作爲服務器端,接收客戶端發來的消息,並且實時輸出至控制檯。)
遇到的第一個問題:
我先發送了第一條消息MESSAGE1,客戶端已經發送成功,但是服務器端並沒有將接收到的字符串打印出來,於是我又接着發了兩條:MESSAGE2 MESSAGE3並且斷開了連接。
後來經過排查,出現問題的代碼在這:
String text = null;
String data = null;
while(null != (text = (data = br.readLine()))) {
System.out.println(new Date());
System.out.println("我是服務器端,客戶端說:"+ data);
}
當時翻來覆去也沒有想明白這段代碼有什麼問題,還自己寫了一個客戶端來進行檢查,結果在自己寫的客戶端上消息都是實時顯示的。但是出於需求的原因,客戶端只能用這個軟件來模擬,不能自己編寫,於是便繼續尋找解決方案。
當時以爲是緩衝區擁堵,於是改寫了代碼,結果引發了第二個問題。
遇到的第二個問題:
char[] b = new char[1024];
int len = 0;
while((len = br.read(b)) != -1) {
System.out.println(new Date());
System.out.println("我是服務器端,客戶端說:"+new String(b));
}
雖然消息能夠實時顯示了,但是出現了更嚴重的問題:
我分別發送了三次消息
MESSAGE1
HELLO
HI
可以看到,這裏產生了消息的黏連。
後來經過N久的思考,想出了這個問題的原因:
由於服務端進行socket消息的處理,客戶端負責消息的發送。所以如果客戶端處理消息“慢”,或者服務端發送消息“快”,則會導致客戶端接收消息的時候會將那些未處理的正在隊列中排隊的消息都接收過來連成一條消息,導致信息處理出錯。
這裏用的是字符數組,而read讀進來的是一個字節,所以會出現那種情形。
最終解決方法:
byte[] b = new byte[200];
int len;
while((len = is.read(b)) != -1) {
System.out.println(new Date());
String str = new String(b, 0, len);
System.out.println("我是服務器端,客戶端說:"+ str);
}
總結:
readLine()這個方法本身有一個注意點,它讀取的是一整行,遇見‘\n’或 ‘ \r’ 代表讀取結束。因爲用的是網絡助手作爲客戶端 ,每次發送消息時並不會添加‘\n’,而用控制檯作爲客戶端進行發送時,按下回車的時候就已經添加了‘\n’,所以服務器端進行接收的時候就是實時顯示的。