Java的Socket通訊基礎編程完全指南_java

來源:互聯網
上載者:User

什麼是Socket
網路上的兩個程式通過一個雙向的通訊串連實現資料的交換,這個雙向鏈路的一端稱為一個Socket。Socket通常用來實現客戶方和服務方的串連。Socket是TCP/IP協議的一個十分流行的編程介面,一個Socket由一個IP地址和一個連接埠號碼唯一確定。
但是,Socket所支援的協議種類也不光TCP/IP一種,因此兩者之間是沒有必然聯絡的。在Java環境下,Socket編程主要是指基於TCP/IP協議的網路編程。

Socket通訊的過程
Server端Listen(監聽)某個連接埠是否有串連請求,Client端向Server 端發出Connect(串連)請求,Server端向Client端發回Accept(接受)訊息。一個串連就建立起來了。Server端和Client 端都可以通過Send,Write等方法與對方通訊。
對於一個功能齊全的Socket,都要包含以下基本結構,其工作過程包含以下四個基本的步驟:
  (1) 建立Socket;
  (2) 開啟串連到Socket的輸入/出流;
  (3) 按照一定的協議對Socket進行讀/寫操作;
  (4) 關閉Socket.(在實際應用中,並未使用到顯示的close,雖然很多文章都推薦如此,不過在我的程式中,可能因為程式本身比較簡單,要求不高,所以並未造成什麼影響。)


建立Socket
java在包java.net中提供了兩個類Socket和ServerSocket,分別用來表示雙向串連的用戶端和服務端。這是兩個封裝得非常好的類,使用很方便。其構造方法如下:

  Socket(InetAddress address, int port);  Socket(InetAddress address, int port, boolean stream);  Socket(String host, int prot);  Socket(String host, int prot, boolean stream);  Socket(SocketImpl impl)  Socket(String host, int port, InetAddress localAddr, int localPort)  Socket(InetAddress address, int port, InetAddress localAddr, int localPort)  ServerSocket(int port);  ServerSocket(int port, int backlog);  ServerSocket(int port, int backlog, InetAddress bindAddr)

  其中address、host和port分別是雙向串連中另一方的IP地址、主機名稱和端 口號,stream指明socket是流socket還是資料報socket,localPort表示本地主機的連接埠號碼,localAddr和 bindAddr是本地機器的地址(ServerSocket的主機地址),impl是socket的父類,既可以用來建立serverSocket又可 以用來建立Socket。count則表示服務端所能支援的最大串連數。例如:學習視頻網 http://www.xxspw.com

  Socket client = new Socket("127.0.01.", 80);  ServerSocket server = new ServerSocket(80);

  注意,在選擇連接埠時,必須小心。每一個連接埠提供一種特定的服務,只有給出正確的連接埠,才 能獲得相應的服務。0~1023的連接埠號碼為系統所保留,例如http服務的連接埠號碼為80,telnet服務的連接埠號碼為21,ftp服務的連接埠號碼為23, 所以我們在選擇連接埠號碼時,最好選擇一個大於1023的數以防止發生衝突。
  在建立socket時如果發生錯誤,將產生IOException,在程式中必須對之作出處理。所以在建立Socket或ServerSocket是必須捕獲或拋出例外。

代碼

server

   

 package socket;      import java.io.*;   import java.net.*;      public class TcpServer {     public static void main(String[] args) throws Exception {       ServerSocket server = new ServerSocket(9091);       try {         Socket client = server.accept();         try {           BufferedReader input =               new BufferedReader(new InputStreamReader(client.getInputStream()));           boolean flag = true;           int count = 1;              while (flag) {             System.out.println("用戶端要開始發騷了,這是第" + count + "次!");             count++;                          String line = input.readLine();             System.out.println("用戶端說:" + line);                          if (line.equals("exit")) {               flag = false;               System.out.println("用戶端不想玩了!");             } else {               System.out.println("用戶端說: " + line);             }              }         } finally {           client.close();         }                } finally {         server.close();       }     }   } 


client

  package socket;      import java.io.*;   import java.net.*;   import java.util.Scanner;      public class TcpClient {     public static void main(String[] args) throws Exception {       Socket client = new Socket("127.0.0.1", 9091);       try {         PrintWriter output =             new PrintWriter(client.getOutputStream(), true);         Scanner cin = new Scanner(System.in);         String words;            while (cin.hasNext()) {           words = cin.nextLine();              output.println(words);              System.out.println("寫出了資料: " + words);         }            cin.close();       } finally {         client.close();       }     }   } 

Server綁定ip

用c寫socket的時候,struct sockaddr_in 結構體是可以指定sin_addr.s_addr的,也就是可以指定ip地址,為什麼會有這種需求呢,例如我的網路連結是這樣的:

我可能只想綁定eth0這個網卡的ip地址,因為我的lo和wlan0都可能在用一連接埠做了nginx的虛擬機器主機,因此在伺服器端開啟ServerSocket的時候,有指定ip的需求

方案
ServerSocket的一個建構函式如下:

public ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException

參數:

    port - 本地 TCP 通訊埠
    backlog - 偵聽 backlog
    bindAddr - 要將伺服器綁定到的 InetAddress


因為InetAddress無建構函式,我在這裡糾結了好一段時間,查看stackoverflow上,可以使用InetAddress的getByName方法

範例程式碼

  InetAddress bindip = InetAddress.getByName("192.168.1.168");      ServerSocket server = new ServerSocket(9091, 0, bindip); 

並發訪問
伺服器端通過增加多線程來同時處理多個用戶端的請求,其實實現還是很水的,畢竟java對多線程封裝也足夠好了,我是在Server伺服器端用一個內部類實現了Runnable介面,在run方法裡處理用戶端的請求,將資料列印出來

server代碼

   

package capitalsocket;      import java.io.BufferedReader;   import java.io.IOException;   import java.io.InputStreamReader;   import java.net.InetAddress;   import java.net.ServerSocket;   import java.net.Socket;      public class CapitalizeServer {     private static int clientNum = 0;        public static void main(String args[]) throws Exception {       ServerSocket listener = new ServerSocket(9898, 0, InetAddress.getByName("192.168.1.168"));       try {         while (true) {           Capitalizer multip = new Capitalizer(listener.accept(), CapitalizeServer.clientNum ++);           Thread t = new Thread(multip);           t.start();         }       } finally {         listener.close();       }     }        private static class Capitalizer implements Runnable {       private Socket client;       private int id;          public Capitalizer(Socket s, int id) {         this.client = s;         this.id = id;       }          public void run() {         try {           BufferedReader input =               new BufferedReader(new InputStreamReader(this.client.getInputStream()));                      while (true) {             String data = input.readLine();                          if (data.equals("bye")) {               System.out.println("當前第" + this.id + "個用戶端度不想玩了!");               break;             } else {               System.out.println("當前第" + this.id + "個用戶端說:" + data);             }           }            } catch (IOException e) {           e.printStackTrace();         } finally {           try {             this.client.close();           } catch (IOException e) {             e.printStackTrace();           }         }       }     }      } 


client代碼
用戶端代碼基本沒變,增加了一個退出操作

   

package capitalsocket;      import java.io.PrintWriter;   import java.net.Socket;   import java.util.Scanner;      public class CapitalizeClient {     public static void main(String[] args) throws Exception {       Socket client = new Socket("192.168.1.168", 9898);       try {         PrintWriter output = new PrintWriter(client.getOutputStream(), true);         Scanner cin = new Scanner(System.in);         String words;            while (cin.hasNext()) {           words = cin.nextLine();           output.println(words);                      if (words.equals("bye")) {             break;           }                      // 每寫一次資料需要sleep一會           Thread.sleep(3000);         }            cin.close();       } finally {         client.close();       }     }   } 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.