天天看點

Java網絡程式設計-Socket程式設計初涉一(簡易用戶端-伺服器)

推薦:​​Java網絡程式設計彙總​​

Java網絡程式設計-Socket程式設計初涉一(簡易用戶端-伺服器)

伺服器

伺服器的socket程式有以下幾個任務:

  • 建立ServerSocket。
  • 綁定端口。
  • 監聽端口。
  • 阻塞,等待用戶端連接配接。
  • 與用戶端連接配接成功後,可以資料互動。

​​Java網絡程式設計-Socket簡介​​ 伺服器socket程式:

package socket;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
    public static void main(String[] args) {

        final String QUIT = "QUIT";
        final int DEFAULT_PORT = 8888;
        ServerSocket serverSocket = null;

        try {
            // 綁定監聽端口
            serverSocket = new ServerSocket(DEFAULT_PORT);
            System.out.println("啟動伺服器,監聽端口"+DEFAULT_PORT);
            while(true){
                //等待用戶端連接配接
                Socket socket = serverSocket.accept();
                System.out.println("用戶端["+socket.getPort()+"]已連接配接");
                BufferedReader reader = new BufferedReader(
                        new InputStreamReader(socket.getInputStream()));

                BufferedWriter writer = new BufferedWriter(
                        new OutputStreamWriter(socket.getOutputStream()));

                String msg = null;
                //讀取用戶端發送的消息,并且進行回複
                while ((msg = reader.readLine()) != null){
                    System.out.println("用戶端["+socket.getPort()+"]:"+msg);

                    //回複用戶端
                    writer.write("伺服器:已收到-"+msg+"\n");
                    writer.flush();

                    // 檢視用戶端是否退出
                    if(QUIT.equalsIgnoreCase(msg)){
                        System.out.println("用戶端["+socket.getPort()+"]已退出");
                        break;
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(serverSocket != null){
                try {
                    serverSocket.close();
                    System.out.println("關閉serverSocket");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}      

建立socket、綁定端口、監聽端口

建立ServerSocket、綁定端口、監聽端口其實在我們的程式中,一行語句就實作了。

serverSocket = new ServerSocket(DEFAULT_PORT);      

按住CTRL,進入該構造器。

源碼如下:

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;
        }
    }      

進入​

​bind方法​

​​。

源碼如下:

public void bind(SocketAddress endpoint, int backlog) throws IOException {
        if (isClosed())
            throw new SocketException("Socket is closed");
        if (!oldImpl && isBound())
            throw new SocketException("Already bound");
        if (endpoint == null)
            endpoint = new InetSocketAddress(0);
        if (!(endpoint instanceof InetSocketAddress))
            throw new IllegalArgumentException("Unsupported address type");
        InetSocketAddress epoint = (InetSocketAddress) endpoint;
        if (epoint.isUnresolved())
            throw new SocketException("Unresolved address");
        if (backlog < 1)
          backlog = 50;
        try {
            SecurityManager security = System.getSecurityManager();
            if (security != null)
                security.checkListen(epoint.getPort());
            getImpl().bind(epoint.getAddress(), epoint.getPort());
            getImpl().listen(backlog);
            bound = true;
        } catch(SecurityException e) {
            bound = false;
            throw e;
        } catch(IOException e) {
            bound = false;
            throw e;
        }
    }      

其中有兩行語句如下:

getImpl().bind(epoint.getAddress(), epoint.getPort());
getImpl().listen(backlog);      

很清楚了吧,程式調用了ServerSocket的構造器,建立了ServerSocket。

而該構造器間接實作了綁定端口、監聽端口(上面兩行語句)。

阻塞,等待用戶端連接配接

Socket socket = serverSocket.accept();      

可以自己去看看源碼。

有用戶端連接配接成功後,會生成一個socket。

BufferedReader reader = new BufferedReader(
                        new InputStreamReader(socket.getInputStream()));

BufferedWriter writer = new BufferedWriter(
                        new OutputStreamWriter(socket.getOutputStream()));      

擷取向用戶端讀、寫的字元流。

​Java IO體系的學習總結​​

String msg = null;
                //讀取用戶端發送的消息,并且進行回複
                while ((msg = reader.readLine()) != null){
                    System.out.println("用戶端["+socket.getPort()+"]:"+msg);

                    //回複用戶端
                    writer.write("伺服器:已收到-"+msg+"\n");
                    writer.flush();

                    // 檢視用戶端是否退出
                    if(QUIT.equalsIgnoreCase(msg)){
                        System.out.println("用戶端["+socket.getPort()+"]已退出");
                        break;
                    }
                }      

上面是伺服器讀取用戶端發送的消息的代碼,一行一行的讀取消息,并且隻有當用戶端發送​

​“quit”​

​​給伺服器時,才表示此用戶端要退出,并且在這個過程中其他用戶端是不能與伺服器進行連接配接的,因為伺服器一直在​

​while​

​裡面讀取此用戶端發送的資料,不過,這隻是一個體驗版,以後會一步一步進行更新的,畢竟學習也是一步一步學出來的。

關閉資源

先不用糾結關閉資源的正确姿勢,這是課程中講師關閉資源的方法,等基礎比較好以後,我會自己去實踐一下,再進行總結,之後也會去閱讀源碼,寫相關部落格,現在的任務就是把整個流程搞明白即可,不去糾結細節,一層一層來揭開Java網絡程式設計的面紗。

serverSocket.close();      

用戶端

用戶端的socket程式有以下幾個任務:

  • 建立Socket。
  • 連接配接伺服器。
  • 與伺服器連接配接成功後,可以資料互動。

用戶端socket程式:

package socket;

import java.io.*;
import java.net.Socket;

public class Client {
    public static void main(String[] args){

        final String QUIT = "QUIT";
        final String DEFAULT_SERVER_HOST = "127.0.0.1";
        final int DEFAULT_PORT = 8888;
        Socket socket = null;
        BufferedWriter writer = null;

        try {
            //建立socket
            socket = new Socket(DEFAULT_SERVER_HOST , DEFAULT_PORT);

            //建立IO流
            BufferedReader reader = new BufferedReader(
                    new InputStreamReader(socket.getInputStream()));

            writer = new BufferedWriter(
                    new OutputStreamWriter(socket.getOutputStream()));

            //等待使用者輸入資訊
            BufferedReader consoleReader = new BufferedReader(
                    new InputStreamReader(System.in));

            while (true){
                String input = consoleReader.readLine();

                //發送消息給伺服器
                writer.write(input+"\n");
                writer.flush();

                //讀取伺服器傳回的消息
                String msg = reader.readLine();
                System.out.println(msg);

                // 檢視使用者是否退出
                if (QUIT.equalsIgnoreCase(input)){
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(writer != null){
                try {
                    writer.close(); // 會自動flush()
                    System.out.println("關閉socket");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}      

建立Socket、連接配接伺服器

socket = new Socket(DEFAULT_SERVER_HOST , DEFAULT_PORT);      

進入構造器。

源碼如下:

/**
     * Creates a stream socket and connects it to the specified port
     * number on the named host.
     * <p>
     * If the specified host is {@code null} it is the equivalent of
     * specifying the address as
     * {@link java.net.InetAddress#getByName InetAddress.getByName}{@code (null)}.
     * In other words, it is equivalent to specifying an address of the
     * loopback interface. </p>
     * <p>
     * If the application has specified a server socket factory, that
     * factory's {@code createSocketImpl} method is called to create
     * the actual socket implementation. Otherwise a "plain" socket is created.
     * <p>
     * If there is a security manager, its
     * {@code checkConnect} method is called
     * with the host address and {@code port}
     * as its arguments. This could result in a SecurityException.
     *
     * @param      host   the host name, or {@code null} for the loopback address.
     * @param      port   the port number.
     *
     * @exception  UnknownHostException if the IP address of
     * the host could not be determined.
     *
     * @exception  IOException  if an I/O error occurs when creating the socket.
     * @exception  SecurityException  if a security manager exists and its
     *             {@code checkConnect} method doesn't allow the operation.
     * @exception  IllegalArgumentException if the port parameter is outside
     *             the specified range of valid port values, which is between
     *             0 and 65535, inclusive.
     * @see        java.net.Socket#setSocketImplFactory(java.net.SocketImplFactory)
     * @see        java.net.SocketImpl
     * @see        java.net.SocketImplFactory#createSocketImpl()
     * @see        SecurityManager#checkConnect
     */
    public Socket(String host, int port)
        throws UnknownHostException, IOException
    {
        this(host != null ? new InetSocketAddress(host, port) :
             new InetSocketAddress(InetAddress.getByName(null), port),
             (SocketAddress) null, true);
    }      

先不去看細節,先看注釋​

​Creates a stream socket and connects it to the specified port number on the named host.​

​。建立流套接字并将其連接配接到命名主機上的指定端口号。很顯然,用戶端調用Socket構造器,建立了Socket,并且連接配接了伺服器(伺服器已經運作的情況下)。

BufferedReader reader = new BufferedReader(
                    new InputStreamReader(socket.getInputStream()));

writer = new BufferedWriter(
                    new OutputStreamWriter(socket.getOutputStream()));      

擷取向伺服器讀、寫的字元流。

//等待使用者輸入資訊
BufferedReader consoleReader = new BufferedReader(
                    new InputStreamReader(System.in));      

等待使用者輸入資訊,并且是控制台的輸入​

​System.in​

​。

while (true){
                String input = consoleReader.readLine();

                //發送消息給伺服器
                writer.write(input+"\n");
                writer.flush();

                //讀取伺服器傳回的消息
                String msg = reader.readLine();
                System.out.println(msg);

                // 檢視使用者是否退出
                if (QUIT.equalsIgnoreCase(input)){
                    break;
                }
            }      

向伺服器發送消息,并且接收伺服器的回複,也是一行一行的讀取。

關閉資源

writer.close();      
Java網絡程式設計-Socket程式設計初涉一(簡易用戶端-伺服器)
Java網絡程式設計-Socket程式設計初涉一(簡易用戶端-伺服器)

到這裡,我們便實作了一個簡易用戶端-伺服器編寫。