使用java的Socket實現客戶端和服務器端之間的鏈接,實現客戶端發送數據到服務器端,同時客戶端能夠接收到服務器端發來的數據。java
一、socket定義
網絡上的兩個程序經過一個雙向的通訊鏈接實現數據的交換,這個鏈接的一端稱爲一個socket。創建網絡通訊鏈接至少要一對端口號(socket)。socket本質是編程接口(API),對TCP/IP的封裝,TCP/IP也要提供可供程序員作網絡開發所用的接口,這就是Socket編程接口;HTTP是轎車,提供了封裝或者顯示數據的具體形式;Socket是發動機,提供了網絡通訊的能力。 Socket的英文原義是"孔"或"插座"。做爲BSD UNIX的進程通訊機制,取後一種意思。一般也稱做"套接字",用於描述IP地址和端口,是一個通訊鏈的句柄,能夠用來實現不一樣虛擬機或不一樣計算機之間的通訊。在Internet上的主機通常運行了多個服務軟件,同時提供幾種服務。每種服務都打開一個Socket,並綁定到一個端口上,不一樣的端口對應於不一樣的服務 簡單來講,咱們網絡上的那些數據能夠當作自來水管裏的那些水,那如今服務器就是 自來水廠 而客戶端就是你本身的家,那架設在外面的那些水管就是光纖,電話線等等傳播網絡信號的介質。爲了更好的知足用戶,這個時候水廠想把水給你送過去就會作個閥門而後經過這個閥門控制水的流量,同時你本身家也一樣裝一個閥門來控制。程序員
二、java Socket編程通常步驟:
- 服務器實例化一個 ServerSocket 對象,表示經過服務器上的端口通訊。
- 服務器調用 ServerSocket 類的 accept() 方法,該方法將一直等待,直到客戶端鏈接到服務器上給定的端口。
- 服務器正在等待時,一個客戶端實例化一個 Socket 對象,指定服務器名稱和端口號來請求鏈接。
- Socket 類的構造函數試圖將客戶端鏈接到指定的服務器和端口號。若是通訊被創建,則在客戶端建立一個 Socket 對象可以與服務器進行通訊。
- 在服務器端,accept() 方法返回服務器上一個新的 socket 引用,該 socket 鏈接到客戶端的 socket。 鏈接創建後,經過使用 I/O 流在進行通訊,每個socket都有一個輸出流和一個輸入流,客戶端的輸出流鏈接到服務器端的輸入流,而客戶端的輸入流鏈接到服務器端的輸出流。
TCP 是一個雙向的通訊協議,所以數據能夠經過兩個數據流在同一時間發送.如下是一些類提供的一套完整的有用的方法來實現 socket。編程
三、代碼實現
客戶端
public class ClientDemo { public static void main(String[] args) throws IOException { //建立發送端的Socket對象 Socket s = new Socket("172.16.79.129",8888); //獲取輸出流 OutputStream os = s.getOutputStream(); Scanner scanner = new Scanner(System.in); System.out.println("請輸入你要發送的數據:"); String string = scanner.nextLine(); os.write(string.getBytes()); //獲取輸入流 InputStream is = s.getInputStream(); byte[] bys = new byte[1024]; int len = is.read(bys); String client = new String(bys, 0 , len); System.out.println("server發送來的數據:"+client); //釋放資源 s.close(); } }
服務端
public class SeverDemo { public static void main(String[] args) throws IOException { //建立接收端的Socket對象 ServerSocket ss = new ServerSocket(8888); //監聽客戶端的鏈接 Socket s = ss.accept(); //獲取輸入流,讀取顯示在控制檯 InputStream is = s.getInputStream(); byte[] bys = new byte[1024]; int len = is.read(bys); String server = new String(bys, 0 , len); System.out.println( "server接收到client發送的信息:" + server); //獲取輸出流 OutputStream os = s.getOutputStream(); os.write("hi".getBytes()); //釋放資源 s.close(); } }
執行結果
這裏有個問題,這個通訊過程全是java本身完成的嗎?答案是否認的!java通訊程序最底層仍是調用了操做系統的Socket函數,才能完成通訊!那麼接下來咱們將介紹Linux下TCP通訊過程!服務器
四、TCP通訊過程
工做過程以下:
首先服務器啓動,經過調用Socket()創建一個套接字;而後調用bind()將該套接字和本地網絡地址聯繫在一塊兒,再調用listen()使套接字作好偵聽的準備,並規定它的請求隊列的長度;以後就調用accept()來接收鏈接。客戶在創建套接字後就可調用connect()和服務器創建鏈接。鏈接一旦創建,客戶和服務器之間就能夠經過調用read()和write()來發送和接收數據。最後,待數據傳送結束後,雙方調用close()關閉套接字。網絡
使用到的一些函數:
一、使用Socket()建立套接字 Socket()系統調用建立一個用於網絡通訊的套接字,並返回該套接字的整數描述符。dom
#include <sys/socket.h int socket(int domain, int type, int protocol)
domain: 協議域, 決定了 socket 的地址類型, 在通訊中必須採用對應的地址。 type: 指定 socket 類型。 protocol: 指定協議。socket
二、綁定本地地址 bind()函數將Socket與本機上的一個端口相關聯,隨後便可在該端口監聽服務器請求。函數
1 #include <sys/socket.h 2 int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
sockfd: 是由socket()函數返回的套接字描述符 sa: 是一個指向 struct sockaddr 的指針,包括有關地址的信息,如名稱、端口和IP地址 addrlen: 是套接字地址的長度spa
三、listen()函數 使用listen()函數將套接字設置爲監聽模式,以等待鏈接請求。操作系統
1 #include <sys/socket.h> 2 int listen(int sockfd, int backlog);
sockfd: 即 socket 描述字, 由 socket() 函數建立。
backlog: 指定在請求隊列中的最大請求數, 進入的鏈接請求將在隊列中等待 accept() 它們。
四、accept()函數 請求到來後,使用accept()函數接收鏈接請求。
1 #indclude <sys/socket.h> 2 int accept(int listenfd, struct sockaddr *addr, int *addrlen);
listenfd: 服務器的 socket 描述字, 由 socket() 函數建立。
addr: 一個 const struct sockaddr 指針, 用來存放提出鏈接請求客戶端的主機的信息
addrlen: 協議地址的長度, 若是是 ipv4 的 TCP 鏈接, 通常爲 sizeof(sockaddr_in)。
五、connect函數 由客戶端調用, 與目的服務器的套接字創建一個鏈接。
1 #include <sys/socket.h> 2 int connect(int clientfd, const struct sockaddr *addr, socklen_t addrlen);
clientfd: 目的服務器的 socket 描述 addr: 一個 const struct sockaddr 指針, 包含了目的服務器 IP 和端口 addrlen: 協議地址的長度, 若是是 ipv4 的 TCP 鏈接, 通常爲 sizeof(sockaddr_in);
六、close()函數 在數據傳輸完成以後, 手動關閉鏈接。
1 #include <sys/socket.h> 2 #include <unistd.h> 3 int close(int fd);
fd: 須要關閉的鏈接 socket 描述符
總結: Linux中的socket指的就是系統底層的socket,而像java這些開發語言,由於java自己是不帶有socket通信底層實現的,因此他們所使用的socket只不過是對系統底層的Socket API進行了二次封裝,面向開發人員,其本質上仍然和Linux底層的socket是同一個東西