一、Java Socket的概述
1、Socket套接字方便了開發網絡應用程序。TCP面向連接的可靠傳輸協議、具有數據確認和數據重傳機制。保證了發送數據一定能到達通信的對方。UPD協議無連接,不可靠的傳輸協議。不具有數據確認和數據重傳機制。socket是套接字的意思,一般用來描述IP地址和端口,是一個通信鏈的句柄。應用程序通常通過套接字向網絡發出請求或者應答網絡請求。摘一段比喻,有助於理解。socket非常類似於電話插座。以一個國家級電話網為例。電話的通話雙方相當於相互通信的2個進程,區號是它的網絡地址:區內一個單位的交換機相當於一台主機,主機分配給每個用戶的局內號碼相當於socket號。任何用戶在通話之前,首先要占有一部電話機。相當於申請一個socket;同時要知道對方的號碼,相當於對方有一個固定的socket。然后向對方撥號呼叫,相當於發出連接請求。
2、 Socket是著名的網絡應用編程接口(API)之一,而Java語言是網絡編程的主要語言,提供了強大和獨特的網絡通訊支持機制和能力。Socket機制成功的解決了兩台主機不同進程之間的通信問題。Socket通信機制它采用客戶服務器模式,由服務器方先建立自己的半相關(建立Socket並將Socket聯編到某個端口上),並進入監聽狀態,同時監聽是否有與自己端口相對應的連接請求。連接是客戶方發送的。客戶方在建立自己的半相關后,向服務器發起連接(調用方法conmect(),服務器在檢測到連接后,接受連接(調用方法accept()),這樣就建立起來一個完整的連接。
二、ServerSocket類和Socket類的常用方法
Socket類
Socket(InetAddress address, int port) 創建一個流套接字並將其連接到指定IP地址的指定端口號。 Socket(String host, int port) 創建一個流套接字並將其連接到指定主機上的指定端口號。 Socket(InetAddress address, int port, InetAddress localAddr, int localPort) 創建一個套接字並將其連接到指定遠程地址上的指定遠程端口。 Socket(String host, int port, InetAddress localAddr, int localPort) 創建一個套接字並將其連接到指定遠程主機上的指定遠程端口。 close() 關閉此套接字。 connect(SocketAddress endpoint) 將此套接字連接到服務器。 connect(SocketAddress endpoint, int timeout) 將此套接字連接到服務器,並指定一個超時值。 getInetAddress() 返回套接字連接的地址。 getInputStream() 返回此套接字的輸入流。 getLocalPort() 返回此套接字綁定到的本地端口。 getOutputStream() 返回此套接字的輸出流。 getPort() 返回此套接字連接到的遠程端口。
ServerSocket類
ServerSocket(int port) 創建綁定到特定端口的服務器套接字。 accept() 偵聽並接受到此套接字的連接。 getInetAddress() 返回此服務器套接字的本地地址。
三、TCP通信
服務器端步驟:
1、創建ServerSocket對象,綁定監聽端口。
2、通過accept()方法監聽客戶端請求。
3、連接建立后,通過輸入流讀取客戶端發送的請求信息。
4、通過輸出流向客戶端發送響應信息。
5、關閉響應的資源。
客戶端步驟:
1、創建Socket對象,指明需要連接的服務器的地址和端口號。
2、連接建立后,通過輸出流向服務器發送請求信息。
3、通過輸入流獲取服務器響應的信息。
4、關閉相應資源。
四、代碼實現
服務端:
package test; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.ServerSocket; import java.net.Socket; public class Server { public static void main(String[] args) throws IOException { //建立tcp的服務端 ServerSocket serverSocket = new ServerSocket(9090); //接受客戶端的連接,產生一個Socket Socket socket = serverSocket.accept(); //獲取到Socket的輸入流對象 BufferedReader socketReader = new BufferedReader(new InputStreamReader(socket.getInputStream())); //獲取到Socket輸出流對象 OutputStreamWriter socketOut = new OutputStreamWriter(socket.getOutputStream()); //獲取鍵盤的輸入流對象 BufferedReader keyReader = new BufferedReader(new InputStreamReader(System.in)); //讀取客戶端的數據 String line = null; while((line = socketReader.readLine())!=null){ System.out.println("客戶端:"+ line); System.out.println("服務端:"); line = keyReader.readLine(); socketOut.write(line+"\r\n"); socketOut.flush(); } serverSocket.close(); } }
客戶端:
package test; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.InetAddress; import java.net.Socket; //聊天的客戶端 public class Client { public static void main(String[] args) throws IOException { //建立tcp的客戶端服務 Socket socket = new Socket(InetAddress.getLocalHost(),9090); //獲取socket的輸出流對象。 OutputStreamWriter socketOut = new OutputStreamWriter(socket.getOutputStream()); //獲取socket的輸入流對象 BufferedReader socketReader = new BufferedReader(new InputStreamReader(socket.getInputStream())); //獲取鍵盤的輸入流對象,讀取數據 BufferedReader keyReader = new BufferedReader(new InputStreamReader(System.in)); String line = null; //不斷的讀取鍵盤錄入的數據,然后把數據寫出 System.out.print("客戶端:"); while((line = keyReader.readLine())!=null){ socketOut.write(line+"\r\n"); socketOut.flush(); //讀取服務端回送的數據 line = socketReader.readLine(); System.out.println("服務端:"+line); } socket.close(); } }
五、實驗結果
六、Linux與網絡編程相關的函數
int socket(int domain, int type,int protocol) domain:說明我們網絡程序所在的主機采用的通訊協族(AF_UNIX和AF_INET等)。 AF_UNIX只能夠用於單一的Unix系統進程間通信,而AF_INET是針對Internet的,因而可以允許在遠程
主機之間通信(當我們 man socket時發現 domain可選項是 PF_*而不是AF_*,因為glibc是posix的實現 所以用PF代替了AF,不過我們都可以使用的)。 type:我們網絡程序所采用的通訊協議(SOCK_STREAM, SOCK_DGRAM等) SOCK_STREAM表明我們用的是TCP協議,這樣會提供按順序的,可靠,雙向,面向連接的比特流。
SOCK_DGRAM 表明我們用的是UDP協議,這樣只會提供定長的,不可靠,無連接的通信。 protocol:由於我們指定了type,所以這個地方我們一般只要用0來代替就可以了 socket為網絡通訊做基本的准備。成功時返回文件描述符,失敗時返回-1,看errno可知道出錯的詳細情。
int bind(int sockfd, struct sockaddr *my_addr, int addrlen) sockfd:是由socket調用返回的文件描述符。
addrlen:是sockaddr結構的長度。my_addr:是一個指向sockaddr的指針。 在其中有 sockaddr的定義: struct sockaddr{ unisgned short as_family; char sa_data[14]; };
int listen(int sockfd,int backlog) sockfd:是bind后的文件描述符。 backlog:設置請求排隊的最大長度。當有多個客戶端程序和服務端相連時,使用這個表示可以介紹的排隊長度。listen函數將bind的文件描述符變為監聽套接字。返回的情況和bind一樣。
int accept(int sockfd, struct sockaddr *addr,int *addrlen) sockfd:是listen后的文件描述符。 addr, addrlen是用來給客戶端的程序填寫的,服務器端只要傳遞指針就可以了。 bind,listen和accept是服務器端用的函數,accept調用時,服務器端的程序會一直阻塞到有一個
客戶程序發出了連接。
accept成功時返回最后的服務器端的文件描述符,這個時候服務器端可以向該描述符寫信息了。失敗時返回-1。
int connect(int sockfd, struct sockaddr * serv_addr,int addrlen) sockfd:socket返回的文件描述符。 serv_addr:儲存了服務器端的連接信息。其中sin_add是服務端的地址 addrlen:serv_addr的長度 connect函數是客戶端用來同服務端連接的。成功時返回0,sockfd是同服務端通訊的文件描述符 失敗時返回-1。