一、Socket通信簡介
Android與服務器的通信方式主要有兩種,一是Http通信,一是Socket通信。兩者的最大差異在于,http連接使用的是“請求—響應方式”,即在請求時建立連接通道,當客戶端向服務器發送請求后,服務器端才能向客戶端返回數據。而Socket通信則是在雙方建立起連接后就可以直接進行數據的傳輸,在連接時可實現信息的主動推送,而不需要每次由客戶端想服務器發送請求。 那么,什么是socket?Socket又稱套接字,在程序內部提供了與外界通信的端口,即端口通信。通過建立socket連接,可為通信雙方的數據傳輸傳提供通道。socket的主要特點有數據丟失率低,使用簡單且易于移植。
1.1什么是Socket Socket
是一種抽象層,應用程序通過它來發送和接收數據,使用Socket可以將應用程序添加到網絡中,與處于同一網絡中的其他應用程序進行通信。簡單來說,Socket提供了程序內部與外界通信的端口并為通信雙方的提供了數據傳輸通道。
1.2Socket的分類
根據不同的的底層協議,Socket的實現是多樣化的。本指南中只介紹TCP/IP協議族的內容,在這個協議族當中主要的Socket類型為流套接字(streamsocket)和數據報套接字(datagramsocket)。流套接字將TCP作為其端對端協議,提供了一個可信賴的字節流服務。數據報套接字使用UDP協議,提供數據打包發送服務。 下面,我們來認識一下這兩種Socket類型的基本實現模型。
二、Socket 基本通信模型
三、Socket基本實現原理
3.1基于TCP協議的Socket
服務器端首先聲明一個ServerSocket對象并且指定端口號,然后調用Serversocket的accept()方法接收客戶端的數據。accept()方法在沒有數據進行接收的處于堵塞狀態。(Socketsocket=serversocket.accept()),一旦接收到數據,通過inputstream讀取接收的數據。
客戶端創建一個Socket對象,指定服務器端的ip地址和端口號(Socketsocket=newSocket("172.168.10.108",8080);),通過inputstream讀取數據,獲取服務器發出的數據(OutputStreamoutputstream=socket.getOutputStream()),最后將要發送的數據寫入到outputstream即可進行TCP協議的socket數據傳輸。
3.2基于UDP協議的數據傳輸
服務器端首先創建一個DatagramSocket對象,并且指點監聽的端口。接下來創建一個空的DatagramSocket對象用于接收數據(bytedata[]=newbyte[1024;]DatagramSocketpacket=newDatagramSocket(data,data.length)),使用DatagramSocket的receive方法接收客戶端發送的數據,receive()與serversocket的accepet()類似,在沒有數據進行接收的處于堵塞狀態。
客戶端也創建個DatagramSocket對象,并且指點監聽的端口。接下來創建一個InetAddress對象,這個對象類似與一個網絡的發送地址(InetAddressserveraddress=InetAddress.getByName("172.168.1.120")).定義要發送的一個字符串,創建一個DatagramPacket對象,并制定要講這個數據報包發送到網絡的那個地址以及端口號,最后使用DatagramSocket的對象的send()發送數據。*(Stringstr="hello";bytedata[]=str.getByte();DatagramPacketpacket=new DatagramPacket(data,data.length,serveraddress,4567);socket.send(packet);)
四、android 實現socket簡單通信
前言:添加權限
<!-- 允許應用程序改變網絡狀態-->
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
<!--允許應用程序改變WIFI連接狀態 -->
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE">
<!--允許應用程序訪問有關的網絡信息-->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<!--允許應用程序訪問WIFI網卡的網絡信息-->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<!--允許應用程序完全使用網絡-->
<uses-permission android:name="android.permission.INTERNET"/>
4.1使用TCP協議通信
android端實現:
protectedvoidconnectServerWithTCPSocket() {
Socket?socket;
try{//?創建一個Socket對象,并指定服務端的IP及端口號
socket?=newSocket("192.168.1.32",1989);
//?創建一個InputStream用戶讀取要發送的文件。
InputStream?inputStream?=newFileInputStream("e://a.txt");
//?獲取Socket的OutputStream對象用于發送數據。
OutputStream?outputStream?=?socket.getOutputStream();
//?創建一個byte類型的buffer字節數組,用于存放讀取的本地文件
bytebuffer[]?=newbyte[4*1024];
inttemp?=0;
//?循環讀取文件
while((temp?=?inputStream.read(buffer))?!=?-1)?{
//?把數據寫入到OuputStream對象中
outputStream.write(buffer,0,?temp);
}
//?發送讀取的數據到服務端
outputStream.flush();
/**?或創建一個報文,使用BufferedWriter寫入,看你的需求?**/
//??????????String?socketData?=?"[2143213;21343fjks;213]";
//??????????BufferedWriter?writer?=?new?BufferedWriter(new?OutputStreamWriter(
//??????????????????socket.getOutputStream()));
//??????????writer.write(socketData.replace("\n",?"?")?+?"\n");
//??????????writer.flush();
/************************************************/
}catch(UnknownHostException?e)?{
e.printStackTrace();
}catch(IOException?e)?{
e.printStackTrace();
}
}
服務器端簡單實現:
publicvoidServerReceviedByTcp() {
//?聲明一個ServerSocket對象
ServerSocket?serverSocket?=null;
try{
//?創建一個ServerSocket對象,并讓這個Socket在1989端口監聽
serverSocket?=newServerSocket(1989);
//?調用ServerSocket的accept()方法,接受客戶端所發送的請求,
//?如果客戶端沒有發送數據,那么該線程就停滯不繼續
Socket?socket?=?serverSocket.accept();
//?從Socket當中得到InputStream對象
InputStream?inputStream?=?socket.getInputStream();
bytebuffer[]?=newbyte[1024*4];
inttemp?=0;
//?從InputStream當中讀取客戶端所發送的數據
while((temp?=?inputStream.read(buffer))?!=?-1)?{
System.out.println(newString(buffer,0,?temp));
}
serverSocket.close();
}catch(IOException?e)?{
e.printStackTrace();
}
}
4.2使用UDP協議通信
客戶端發送數據實現:
protectedvoidconnectServerWithUDPSocket() {
DatagramSocket?socket;
try{
//創建DatagramSocket對象并指定一個端口號,注意,如果客戶端需要接收服務器的返回數據,
//還需要使用這個端口號來receive,所以一定要記住
socket?=newDatagramSocket(1985);
//使用InetAddress(Inet4Address).getByName把IP地址轉換為網絡地址
InetAddress?serverAddress?=?InetAddress.getByName("192.168.1.32");
//Inet4Address?serverAddress?=?(Inet4Address)?Inet4Address.getByName("192.168.1.32");
String?str?="[2143213;21343fjks;213]";//設置要發送的報文
bytedata[]?=?str.getBytes();//把字符串str字符串轉換為字節數組
//創建一個DatagramPacket對象,用于發送數據。
//參數一:要發送的數據??參數二:數據的長度??參數三:服務端的網絡地址??參數四:服務器端端口號
DatagramPacket?packet?=newDatagramPacket(data,?data.length?,serverAddress?,10025);
socket.send(packet);//把數據發送到服務端。
}catch(SocketException?e)?{
e.printStackTrace();
}catch(UnknownHostException?e)?{
e.printStackTrace();
}catch(IOException?e)?{
e.printStackTrace();
}
}
客戶端接收服務器返回的數據:
publicvoidReceiveServerSocketData() {
DatagramSocket?socket;
try{
//實例化的端口號要和發送時的socket一致,否則收不到data
socket?=newDatagramSocket(1985);
bytedata[]?=newbyte[4*1024];
//參數一:要接受的data?參數二:data的長度
DatagramPacket?packet?=newDatagramPacket(data,?data.length);
socket.receive(packet);
//把接收到的data轉換為String字符串
String?result?=newString(packet.getData(),?packet.getOffset(),
packet.getLength());
socket.close();//不使用了記得要關閉
System.out.println("the?number?of?reveived?Socket?is??:"+?flag
+"udpData:"+?result);
}catch(SocketException?e)?{
e.printStackTrace();
}catch(IOException?e)?{
e.printStackTrace();
}
}
服務器接收客戶端實現:
publicvoidServerReceviedByUdp(){
//創建一個DatagramSocket對象,并指定監聽端口。(UDP使用DatagramSocket)
DatagramSocket?socket;
try{
socket?=newDatagramSocket(10025);
//創建一個byte類型的數組,用于存放接收到得數據
bytedata[]?=newbyte[4*1024];
//創建一個DatagramPacket對象,并指定DatagramPacket對象的大小
DatagramPacket?packet?=newDatagramPacket(data,data.length);
//讀取接收到得數據
socket.receive(packet);
//把客戶端發送的數據轉換為字符串。
//使用三個參數的String方法。參數一:數據包?參數二:起始位置?參數三:數據包長
String?result?=newString(packet.getData(),packet.getOffset()?,packet.getLength());
}catch(SocketException?e)?{
e.printStackTrace();
}catch(IOException?e)?{
e.printStackTrace();
}
}
五、總結:
使用UDP方式android端和服務器端接收可以看出,其實android端和服務器端的發送和接收大庭相徑,只要端口號正確了,相互通信就沒有問題,TCP使用的是流的方式發送,UDP是以包的形式發送。