Socket在Android網絡編程中,有著非常重要的作用。
Socket基本概念
即套接字,是應用層 與 TCP/IP 協議族通信的中間軟件抽象層,表現為一個封裝了 TCP / IP協議族 的編程接口(API)。
從設計模式的角度看來,Socket其實就是一個門面模式,它把複雜的TCP/IP協議族隱藏在Socket接口後面,對用戶來說,一組簡單的接口就是全部,讓Socket去組織數據,以符合指定的協議。
借用下網上結構圖:
Socket
IP地址和端口號組成了Socket,都是成對出現。
<code>Socket ={(IP地址1:PORT端口號),(IP地址2:PORT端口號)}/<code>
單獨的Socke是沒用任何作用的,基於一定的協議(TCP或者UDP)下的Socket編程才能進行數據傳輸。
Socket工作流程
Socket工作流程
服務端先初始化Socket,然後與端口綁定(bind),對端口進行監聽(listen),調用accept阻塞,等待客戶端連接。
客戶端初始化一個socket,然後連接服務器(connect),如果連接成功,這時客戶端與服務端的連接就建立了。
客戶端發送數據請求,服務端接收請求並處理請求,然後把回應數據發給客戶端,客戶端讀取數據,最後關閉數據,一次交互結束。
分類
Socket使用類型有兩種:
- 基於TCP協議,流套接字,採用流的方式提供可靠的字節流服務
- 基於UDP協議,數據報套接字,採用數據報文提供數據打包發送的服務
基於TCP的Socket編程
主要API
Socket
構造方法
<code>public Socket(String host, int port) throws UnknownHostException, IOException/<code>
創建流套接字並將其連接到指定主機上的指定端口號。
- host: 主機地址
- port: 端口號
getInputStream
返回Socket的輸入流,用戶接受數據。
getOutputStream
返回Socket的輸出流,用於發送數據。
ServerSocket
Socket的服務端實現
構造函數
<code>public ServerSocket(int port) throws IOException/<code>
創建服務端Socket,綁定到指定端口。
- port: 端口號
accept
<code>public Socket accept() throws IOException/<code>
監聽並接受到此套接字的連接。該方法將阻塞,直到建立連接。
示例
服務端
<code>public class Server { public static void main(String[] args) throws IOException { //1. 創建ServerSocket ServerSocket serverSocket = new ServerSocket(8888); //2. 監聽 Socket socket = serverSocket.accept(); System.out.println("server start listen"); //3. 輸入流 InputStream is = socket.getInputStream(); InputStreamReader reader = new InputStreamReader(is); BufferedReader br = new BufferedReader(reader); String content = null; StringBuffer sb = new StringBuffer(); while ((content = br.readLine()) != null) { sb.append(content); } System.out.println("server receiver: " + sb.toString()); socket.shutdownInput(); br.close(); reader.close(); is.close(); socket.close(); serverSocket.close(); }}/<code>
非常簡單的Socket服務端,接收到客戶端的數據,就會關閉當前的連接。這個示例只是展示了一個完整的流程。
如果需要複雜的服務端實現,可以使用Netty、Mina或者其他Socket框架。
客戶端
<code>//1. 創建客戶端Socket socket = new Socket("your ip", 8888);//2. 輸出流OutputStream os = socket.getOutputStream();//3. 發送數據os.write("Hello world".getBytes());System.out.println("send message");os.flush();socket.shutdownOutput();os.close();socket.close();/<code>
客戶端就是連接後,發送了一份數據,就關閉連接了。
這樣就實現了客戶端和服務端的通信。
基於UDP的Socket編程
主要API
DatagramPacket
用來包裝接收和發送的數據。
- 構造接收數據包
<code>public DatagramPacket(byte[] buf,int length)/<code>
用來接收長度為 length 的數據包。
- 構造發送數據包
<code>DatagramPacket(byte[] buf, int length,SocketAddress address)DatagramPacket(byte[] buf, int length, InetAddress address, int port)/<code>
用來將長度為 length 的包發送到指定主機上的指定端口號。
DatagramSocket
用來發送和接收數據報包的套接字。
構造方法
<code>//創建數據報套接字並將其綁定到本地主機上的指定端口DatagramSocket(int port) //創建數據報套接字,將其綁定到指定的本地地址DatagramSocket(int port, InetAddress laddr)/<code>
發送數據
<code>void send(DatagramPacket p)/<code>
DatagramPacket 包含的信息指示:將要發送的數據、其長度、遠程主機的 IP 地址和遠程主機的端口號
接收數據
<code>void receive(DatagramPacket p)/<code>
當此方法返回時,DatagramPacket的緩衝區填充了接收的數據。
示例
服務端
<code>public class UDPServer { public static void main(String[] args) throws IOException { byte[] buf = new byte[1024]; // receive // 1.create DatagramPacket packet = new DatagramPacket(buf, buf.length); // 2.create udp socket DatagramSocket socket = new DatagramSocket(8888); // 3. receive start socket.receive(packet); // 4. receive data System.out.println("sever: " + new String(buf, 0, buf.length)); // send DatagramPacket p = new DatagramPacket(buf, buf.length, packet.getAddress(), packet.getPort()); socket.send(p); socket.close(); }}/<code>
客戶端
<code>// sendInetAddress address = InetAddress.getByName("your ip");//1.create packetDatagramPacket packet = new DatagramPacket(bytes, bytes.length, address, 8888);//2.create socketDatagramSocket socket = new DatagramSocket();//3.send datasocket.send(packet);// receive//1.create packetfinal byte[] bytes = new byte[1024];DatagramPacket receiverPacket = new DatagramPacket(bytes, bytes.length);socket.receive(receiverPacket);System.out.println("client: " + new String(bytes, 0, bytes.length));socket.close();/<code>
客戶端和服務端的實現,都比較簡單。
關於Socket編程,就介紹好了,這篇只是開了頭,最主要的還是得去項目中實踐。
參考
- Android:這是一份很詳細的Socket使用攻略
- Scoket編程
閱讀更多 Coder淘金 的文章