1. 程式人生 > >Java中的TCP、UDP通訊

Java中的TCP、UDP通訊

文章目錄

TCP

TCP 協議提供面向連線的服務,通過它建立的是可靠地連線。Java 為 TCP 協議提供了兩個類:Socke 類和 ServerSocket 類。一個 Socket 例項代表了 TCP 連線的一個客戶端,而一個 ServerSocket 例項代表了 TCP 連線的一個伺服器端,一般在 TCP Socket 程式設計中,客戶端有多個,而伺服器端只有一個,客戶端 TCP 向伺服器端 TCP 傳送連線請求,伺服器端的 ServerSocket 例項則監聽來自客戶端的 TCP 連線請求,併為每個請求建立新的 Socket 例項,由於服務端在呼叫 accept()等待客戶端的連線請求時會阻塞,直到收到客戶端傳送的連線請求才會繼續往下執行程式碼,因此要為每個 Socket 連線開啟一個執行緒。伺服器端要同時處理 ServerSocket 例項和 Socket 例項,而客戶端只需要使用 Socket 例項。另外,每個 Socket 例項會關聯一個 InputStream 和 OutputStream 物件,我們通過將位元組寫入套接字的 OutputStream 來發送資料,並通過從 InputStream 來接收資料。

From:極客wiki

建立伺服器

  1. 通過ServerSocket監聽本地埠(以22333埠為例)
//:TCPServer.java

ServerSocket serverSocket = new ServerSocket(22333);
while(true){
	Socket conn = serverSocket.accept();
	onClientArrived(conn);
}
//deal with the request connection
public void abstract onClientArrived(Socket conn);

在新的連線到達前ServerSocket#accept()

將處於阻塞狀態。當每個連線到達時accept函式都將返回一個Socket例項; 2) 在onClientArrived中處理新連線

@Override
public void onConnArrived(Socket conn) {
	try {
		BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
		String tmpLine = null;
		while ((tmpLine = reader.readLine()) != null){
				System.
out.println(tmpLine); } reader.close(); conn.close(); } catch (IOException e) { e.printStackTrace(); } }

Reader#readLine()為一個阻塞讀取過程,因此需要在客戶端使用(啟用auto flush)PrintWriterprintln等含有自動newLine的 函式與之配合使用。這裡為了簡便在讀取完成後,直接關閉連線,如果想進行持續會話,則可根據conn的內容來控制時候結束會話,並且需要保證客戶端並沒有主動關閉連線。另外,通常當ServerSocket#accept()返回新Socket物件後,應該為該物件建立一個任務,並將該任務交由執行緒池處理。

建立客戶端

  1. 客戶端套接字 連線本地22333埠

Socket client = new Socket("127.0.0.1", 22333);
  1. 向套接字中寫入資料
//使用PrintWriter向套接字的OutputStream中寫入資料
PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(client.getOutputStream()), true);
printWriter.println("hello ! this msg is send by  client");
printWriter.close();
client.close();

此處在寫入完成直接關閉連線。若希望持續會話,則可在服務端的連線沒有關閉的情況下,此處可以通過對Socket輸入流的read操作和輸出流的write操作實現持續會話 3) 完整程式碼

//:TCPTest.java
public class TCPTest {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                TCPServer tcpServer = new TCPServer() {
                    @Override
                    public void onConnArrived(Socket conn) {
                        try {
                            BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
                            String tmpLine = null;
                            while ((tmpLine = reader.readLine()) != null){
                                System.out.println("receive msg from client" + tmpLine);
                            }
                            reader.close();
                            conn.close();
                            System.out.println("conn shutdown");
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                };
                tcpServer.startServer();
            }
        }).start();

        try {
            Socket socket = new Socket("127.0.0.1",TCPServer.PORT);
            PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true);
            printWriter.println("hello ! this msg is send by client");
            printWriter.close();
            socket.close();
            System.out.println("client close");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
//:TCPServer.java

public abstract class TCPServer {
    public static final int PORT = 22333;
    private static ServerSocket serverSocket;
    private volatile boolean ifServerIsRunning = false;
    public TCPServer(){
        synchronized (TCPServer.class){
            if (serverSocket == null){
                try {
                    serverSocket = new ServerSocket(PORT);
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
        }
    }
    public void startServer(){
        if (!ifServerIsRunning){
            ifServerIsRunning = true;
            while (true){
                try {
                    Socket conn = serverSocket.accept();
                    onConnArrived(conn);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public abstract void  onConnArrived(Socket conn);
}

由以上的程式可以看出,Socket就是程序與程序之間的一種通訊方式,一個位於服務端,一個位於客戶端。Socket並不代表運輸層,它只是一個網路程式設計API,當選用Socket時,代表我們希望使用TCP協議通訊,當選用DatagramSocket時代表我們希望使用UDP協議進行通訊。

UDP

Java 通過 DatagramPacket 類和 DatagramSocket 類來使用 UDP 套接字,客戶端和伺服器端都通過DatagramSocket 的 send()方法和 receive()方法來發送和接收資料,用 DatagramPacket 來包裝需要傳送或者接收到的資料。傳送資訊時,Java 建立一個包含待發送資訊的 DatagramPacket 例項,並將其作為引數傳遞給DatagramSocket例項的send()方法;接收資訊時,Java 程式首先建立一個 DatagramPacket 例項,該例項預先分配了一些空間,並將接收到的資訊存放在該空間中,然後把該例項作為引數傳遞給 DatagramSocket 例項的 receive()方法。在建立 DatagramPacket 例項時,要注意:如果該例項用來包裝待接收的資料,則不指定資料來源的遠端主機和埠,只需指定一個快取資料的 byte 陣列即可(在呼叫 receive()方法接收到資料後,源地址和埠等資訊會自動包含在 DatagramPacket 例項中),而如果該例項用來包裝待發送的資料,則要指定要傳送到的目的主機和埠。 From:極客wiki

小結

  • 在讀取未關閉的socket的過程中,任何的read()readline()操作都是一個阻塞過程直到讀取到內容或者socket關閉,可對比new Scanner(System.in)的類next操作。
  • ServerSocketaccept()操作是同樣是一個阻塞過程,直到收到一個連線請求,每次客戶端成功發起請求後,accept()都返回一個Socket物件,這個Socket物件將負責與發起請求的本次客戶端進行通訊。
  • ReaderreadLine()需要使用'\n'作為此次讀取結束標記,否則將一直阻塞在讀取狀態。
  • “一切皆檔案”