使用socket寫一個簡單的聊天程式&碰到的問題
程式分成2個部分:
1,服務端,用來接受客戶端發來的資訊
2,客戶端,用來向服務端發信息。
一、服務端如下:
SocketServerThread.java用來處理客戶端傳送的資訊
package com.thread.socket.service; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintWriter; import java.net.Socket; public class SocketServerThread extends Thread { // 和本執行緒相關的Socket Socket socket = null; public SocketServerThread(Socket socket) { this.socket = socket; } @Override public void run() { InputStream is = null; InputStreamReader isr = null; BufferedReader br = null; OutputStream os = null; PrintWriter pw = null; //獲取輸入流,並讀取客戶端資訊 try { is = socket.getInputStream(); isr = new InputStreamReader(is); br = new BufferedReader(isr); String info = null; while((info=br.readLine()) != null) { //迴圈讀取客戶端的資訊 System.out.println(info); } socket.shutdownInput(); //關閉輸入流 // 獲取輸出流,響應客戶端的請求 os = socket.getOutputStream(); pw = new PrintWriter(os); } catch (IOException e) { e.printStackTrace(); } finally { //關閉資源 try { if(pw!=null) pw.close(); if(os!=null) os.close(); if(br!=null) br.close(); if(isr!=null) isr.close(); if(is!=null) is.close(); if(socket!=null) socket.close(); } catch (IOException e) { e.printStackTrace(); } } } }
ServerThread.java用來監聽客戶端請求,接受請求後開啟執行緒排程SocketServerThread.java
package com.thread.socket.service; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; /** * 用來接受客戶端傳送的資訊 * */ public class ServerThread extends Thread { @SuppressWarnings("resource") @Override public void run() { // 1.建立一個伺服器端Socket,即ServerSocket,指定繫結的埠,並監聽此埠 try { ServerSocket serverSocket = new ServerSocket(8888); Socket socket = null; System.out.println("***伺服器即將啟動,等待客戶端的連線***"); // 迴圈監聽等待客戶端的連線 while(true) { // 呼叫accept()方法開始監聽,等待客戶端的連線 socket = serverSocket.accept(); // 建立一個新的執行緒,用來處理請求 SocketServerThread serverThread = new SocketServerThread(socket); // 啟動執行緒 serverThread.start(); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
二、客戶端如下:
Client.java
package com.thread.socket.client; import java.io.IOException; import java.io.OutputStream; import java.io.PrintWriter; import java.net.Socket; import java.net.UnknownHostException; public class Client { /** * 向服務端傳送資訊 * * @param str */ public static void output(String str) { try { // 1.建立客戶端Socket,指定伺服器地址和埠 Socket socket = new Socket("localhost", 8008); // TODO 這裡用的是本地的IP // 2.獲取輸出流,向伺服器端傳送資訊 OutputStream os = socket.getOutputStream(); PrintWriter pw = new PrintWriter(os); pw.write(str); pw.flush(); socket.shutdownOutput();//關閉輸出流 //4.關閉資源 pw.close(); os.close(); socket.close(); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
三、啟動服務端
package com.thread.socket.main;
import java.util.Scanner;
import com.thread.socket.client.Client;
import com.thread.socket.service.ServerThread;
public class Chat {
@SuppressWarnings("resource")
public static void main(String[] args) {
// 啟動伺服器
ServerThread s = new ServerThread();
s.start();
// 向伺服器傳送資訊
while (true) {
Scanner sb = new Scanner(System.in);
System.out.print("輸入語句:");
String str = sb.nextLine();
Client.output(str);
}
}
}
注意:上面幾個檔案放在一起算一個程式。
將上述檔案複製一份,做成另一個程式,開啟這兩個程式,就可以相互聊天了。
如果想在本地讓這兩程式相互發送訊息的話,其中的一個程式的服務端要需要修改監聽埠號,
另外一個程式的客戶端要更改傳送的埠號,這樣兩個程式才能在本地相互發送訊息;
否則啟動好一個程式後,再啟動另一個程式,就會報該埠號被佔用的錯誤。
---------------------------下面是本人搞事時,發現的bug-----------------------------
另外本人嘗試連續不斷的向服務端傳送10000的請求,發現報錯了
注意:設定成10000時,有時候會報錯,不一定是每次都報錯,如果想每次報錯把這個10000改成100w試試。
報錯資訊如下:java.net.BindException: Address already in use: connect
大致原因是埠號來不及釋放,導致錯誤。
------------------------------------------------------------------------------------------
我多次測試之後,感覺應該是服務端接受請求上限導致的。
測試結果:服務端同時能接受的請求的上限是50條,
ServerSocket類部分原始碼定義如下(大概在229行左右):
public ServerSocket(int port) throws IOException {
this(port, 50, null);
}
public ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException {
setImpl();
if (port < 0 || port > 0xFFFF)
throw new IllegalArgumentException(
"Port value out of range: " + port);
if (backlog < 1)
backlog = 50;
try {
bind(new InetSocketAddress(bindAddr, port), backlog);
} catch(SecurityException e) {
close();
throw e;
} catch(IOException e) {
close();
throw e;
}
}
backlog這個引數解釋是:請求連結佇列的最大長度。
當你直接 new ServerSocket(port)時,預設是50。
從上面構造可以看出請求連結最大不能超過0xFFFF=2^16-1=65535