Posts match “ Socket ” tag:

Socket是??

  • Socket最早緣起於Unix,而在Unix的檔案可以用開啟open -> 讀寫write/read -> 關閉close的模式來操作,所以Socket在Unix當中也遵循這樣的操作模式,算是一種文件,Socket所提供的API就是可以進行的操作(開啟/關閉、讀寫)。
  • 使用TCP/IP協定的程式都是採用Socket來進行連接傳輸的介面,最常見的例子就是網頁瀏覽器,底層都是用Socket去做連接通信的介面。
  • Socket架構圖解


Java Socket API

簡單了解Socket為何後,接著就要來看在Java當中程式怎麼寫了。Socket Programming採用Server/Client(以下簡稱S/C架構)的架構來實作,所以下面列出幾個簡單觀念先了解後會更快上手:

  • S/C架構下的程式進行通訊,Server端需要提供一個固定位置(一個IP:Port或Domain Name),Client要連接就要先知道這位置。
  • 在Java當中S/C架構就是對應到ServerSocketSocket兩個物件,Server端用ServerSocket.listen()來監聽Client端,Client端用Socket和Server端做連接,Server端用ServerSocket.accept()來取得和Client端連線的Socket物件。
  • 當Server和Client連接後,就可以用socket.getInputStream()socket.getOutputStream()來做資料的讀寫傳輸。
  • 最後資料傳遞完後,記得要呼叫socket.close()來關閉所有使用到的Sockets。

程式碼

Server端

public class Server
{
    public static final int LISTEN_PORT = 5987;
    
    public void listenRequest()
    {
        ServerSocket serverSocket = null;
        ExecutorService threadExecutor = Executors.newCachedThreadPool();
        try
        {
            serverSocket = new ServerSocket( LISTEN_PORT );
            System.out.println("Server listening requests...");
            while ( true )
            {
                Socket socket = serverSocket.accept();
                threadExecutor.execute( new RequestThread( socket ) );
            }
        }
        catch ( IOException e )
        {
            e.printStackTrace();
        }
        finally
        {
            if ( threadExecutor != null )
                threadExecutor.shutdown();
            if ( serverSocket != null )
                try
                {
                    serverSocket.close();
                }
                catch ( IOException e )
                {
                    e.printStackTrace();
                }
        }
    }
    
    /**
     * @param args
     */
    public static void main( String[] args )
    {
        Server server = new Server();
        server.listenRequest();
    }
    
    /**
     * 處理Client端的Request執行續。
     *
     * @version
     */
    class RequestThread implements Runnable
    {
        private Socket clientSocket;
        
        public RequestThread( Socket clientSocket )
        {
            this.clientSocket = clientSocket;
        }
        
        /* (non-Javadoc)
         * @see java.lang.Runnable#run()
         */
        @Override
        public void run()
        {
            System.out.printf("有%s連線進來!\n", clientSocket.getRemoteSocketAddress() );
            DataInputStream input = null;
            DataOutputStream output = null;
            
            try
            {
                input = new DataInputStream( this.clientSocket.getInputStream() );
                output = new DataOutputStream( this.clientSocket.getOutputStream() );
                while ( true )
                {
                    output.writeUTF( String.format("Hi, %s!\n", clientSocket.getRemoteSocketAddress() ) );
                    output.flush();
                    // TODO 處理IO,這邊定義protocol協定!!

                    break;
                }
            }
            catch ( IOException e )
            {
                e.printStackTrace();
            }
            finally 
            {
                try
                {
                    if ( input != null )
                        input.close();
                    if ( output != null )
                        output.close();
                    if ( this.clientSocket != null && !this.clientSocket.isClosed() )
                        this.clientSocket.close();
                }
                catch ( IOException e )
                {
                    e.printStackTrace();
                }
            }
        }
    }
}

Client端

public class Client
{
    public static void main( String[] args ) throws IOException
    {
        String host = "";
        int port = 5987;
        Socket socket = null;
        Scanner consoleInput = new Scanner( System.in );
        System.out.println("請輸入Server端位址");
        host = consoleInput.nextLine();
        try
        {
            socket = new Socket( host, port );
            DataInputStream input = null;
            DataOutputStream output = null;
            
            try
            {
                input = new DataInputStream( socket.getInputStream() );
                output = new DataOutputStream( socket.getOutputStream() );
                while ( true )
                {
                    System.out.println( input.readUTF() );
                    break;
                }
            }
            catch ( IOException e )
            {
            }
            finally 
            {
                if ( input != null )
                    input.close();
                if ( output != null )
                    output.close();
            }
        }
        catch ( IOException e )
        {
            e.printStackTrace();
        }
        finally
        {
            if ( socket != null )
                socket.close();
            if ( consoleInput != null )
                consoleInput.close();
        }
    }
}

程式解說

  • ServerSocket serverSocket = new ServerSocket( port )當中的port使用上必須是唯一的,因為port是用來辨識電腦上每個獨立的服務,每個不同的服務所使用的port就會不同,可用的port範圍從0~65536,前1024個port是TCP/IP欲留著給一些特定協定使用(例如:HTTP=80, FTP=21),所以程式的port只能使用大於1024之後的整數。
  • Server端採用允許多個Client端連接的簡單多執行緒連線架構,其思路為每一個Client連線就用一個執行緒來處理,在ServerSocket.accept()呼叫後會回傳一個和目前Client連線的Socket,我們將此Socket傳遞給執行緒做後續的處理,而ServerSocket可以繼續listen()來達到多個Client連接的架構。
  • 程式當中是使用DataInput/OutputStream來處理IO資料傳遞,當然你可以照你的需求來使用不同的IO Stream。
  • 取得IO Stream後就可以依照你所定義的protocol來做資料的傳遞,程式當中是Server端回傳一個訊息給Client端,然後Client端取得後印出。