1. 程式人生 > >使用socket寫一個簡單的聊天程式&碰到的問題

使用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