Java Socket通訊簡介

服務端

客戶端

 

Java中Socket通訊是通過java.net.ServerSocketjava.net.Socket實現的,ServerSocket用於服務端偵聽,Socket用於真實的連接。

服務端

服務端所有操作是通過ServerSocket完成。

偵聽

服務端需要先綁定要偵聽的端口,然後通過accept等待客戶端連接:

  • new時傳入端口:在所有本機地址上偵聽;

  • 通過bind綁定:在指定的本機地址上偵聽;

以偵聽本地迴環地址爲例(只能通過127或localhost進行連接),因accept會阻塞,一般需要以線程方式運行:

class AcceptThread extends Thread {
    @Override
    public void run() {
        try (ServerSocket srvSock = new ServerSocket()) {
            InetAddress inAddr = Inet4Address.getLoopbackAddress();
            InetSocketAddress sockAddr = new InetSocketAddress(inAddr, 8900);
            srvSock.bind(sockAddr);
            System.out.println("Listen at: " + srvSock.getLocalSocketAddress());

            while (true) {
                Socket sockClient = srvSock.accept();

                System.out.println("Accept: " + sockClient.getRemoteSocketAddress());
                ClientThread client = new ClientThread(sockClient);
                client.start();
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

socket連接

當客戶端有連接到來時,就會自動產生一個新的Socket,通過此Socket即可與客戶端進行交互。
Java中Socket提供了輸入與輸出流,因此可以使用各種流操作類(如緩衝讀​)方便地進行操作。在使用Java流進行寫時,一定要注意及時刷新(創建時設定autoFlush,或寫完成時調用flush接口)。

如下,等待客戶端的輸入(每次一行),並在接收到後以Echo方式返回:

class ClientThread extends Thread {
    private Socket _sockClient;

    public ClientThread(Socket sock) {
        _sockClient = sock;
    }

    @Override
    public void run() {
        try (// in stream
                InputStream inStream = _sockClient.getInputStream();
                InputStreamReader inReader = new InputStreamReader(inStream, StandardCharsets.UTF_8);
                BufferedReader buffReader = new BufferedReader(inReader);
                // out stream
                OutputStream outStream = _sockClient.getOutputStream();
                OutputStreamWriter outWriter = new OutputStreamWriter(outStream, StandardCharsets.UTF_8);
                PrintWriter printer = new PrintWriter(outWriter, true);) {
            String strReceive;
            System.out.println("Wait for client msg");
            while ((strReceive = buffReader.readLine()) != null) {
                System.out.println(strReceive);

                if (strReceive.equals("bye")) {
                    printer.println("bye");
                    break;
                }

                printer.println("Echo: " + strReceive);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            try {
                if (!_sockClient.isClosed())
                    _sockClient.close();
            } catch (Exception ex) {
            }
        }

        System.out.println("Socket closed");
    }
}

客戶端

客戶端只需要使用Socket直接連接即可,在創建時傳遞服務端的地址與端口號,就會自動連接。

如下實現了一個可在命令行中輸入發送消息的示例,爲了能接收命令行消息,並通過socket發送與接收,創建了三個流:讀標準輸入的流,socket寫流與socket讀流。
此處PrintWriter在創建時沒有設定autoFlush,因此在數據寫入完成後一定要調用flush,否則很可能是緩衝在本地沒有發出去,然後後面的read就一直獲取不到服務端的數據(因服務端是在收到客戶端數據後再應答的)。

try (Socket sock = new Socket("127.0.0.1", 8900)) {
    System.out.println("Connected: " + sock.getRemoteSocketAddress());

    try (// Input
            BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
            // Socket out
            OutputStreamWriter outStream = new OutputStreamWriter(sock.getOutputStream(),
                    StandardCharsets.UTF_8);
            PrintWriter sockWriter = new PrintWriter(outStream);
            // Socket in
            InputStreamReader inStream = new InputStreamReader(sock.getInputStream(), StandardCharsets.UTF_8);
            BufferedReader sockReader = new BufferedReader(inStream);) {
        String strLine;
        while (!(strLine = input.readLine()).equals("end")) {
            sockWriter.println(strLine);
            sockWriter.flush();

            String strReceive;
            if ((strReceive = sockReader.readLine()) == null)
                break;
            System.out.println(strReceive);
            if(strReceive.equals("bye"))break;
        }
    }
} catch (Exception ex) {
    ex.printStackTrace();
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章