當前位置:首頁 » 安卓系統 » android40socket

android40socket

發布時間: 2023-10-01 02:27:57

1. android socket有幾種方法

/***第一種:客戶端Socket通過構造方法連接伺服器***/
//客戶端Socket可以通過指定IP地址或域名兩種方式來連接伺服器端,實際最終都是通過IP地址來連接伺服器
//新建一個Socket,指定其IP地址及埠號
Socket socket = new Socket("192.168.0.7",80);
/***Socket 客戶端 一些常用設置***/
//客戶端socket在接收數據時,有兩種超時:1.連接伺服器超時,即連接超時;2.連接伺服器成功後,接收伺服器數據超時,即接收超時
//*設置socket 讀取數據流的超時時間
socket.setSoTimeout(5000);
//發送數據包,默認為false,即客戶端發送數據採用Nagle演算法
//但是對於實時交互性高的程序,建議其改為true,即關閉Nagle演算法,客戶端每發送一次數據,無論數據包大小都會將這些數據發送出去
socket.setTcpNoDelay(true);
//設置客戶端socket關閉時,close()方法起作用時延遲1分鍾關閉,如果1分鍾內盡量將未發送的數據包發送出去
socket.setSoLinger(true, 60);
//設置輸出流的發送緩沖區大小,默認是8KB,即8096位元組
socket.setSendBufferSize(8096);
//設置輸入流的接收緩沖區大小,默認是8KB,即8096位元組
socket.setReceiveBufferSize(8096);
//作用:每隔一段時間檢查伺服器是否處於活動狀態,如果伺服器端長時間沒響應,自動關閉客戶端socket
//防止伺服器端無效時,客戶端長時間處於連接狀態
socket.setKeepAlive(true);
/*** Socket客戶端向伺服器端發送數據 ****/
//客戶端向伺服器端發送數據,獲取客戶端向伺服器端輸出流
OutputStream os = socket.getOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(os);
BufferedWriter bw = new BufferedWriter(osw);
//代表可以立即向伺服器端發送單位元組數據
socket.setOOBInline(true);
//數據不經過輸出緩沖區,立即發送
socket.sendUrgentData(65);//"A"
//向伺服器端寫數據,寫入一個緩沖區
//註:此處字元串最後必須包含「\r\n\r\n」,告訴伺服器HTTP頭已經結束,可以處理數據,否則會造成下面的讀取數據出現阻塞
//在write()方法中可以定義規則,與後台匹配來識別相應的功能,例如登錄Login()方法,可以寫為write("Login|test,123 \r\n\r\n"),供後台識別;
bw.write("Login|test,123 \r\n\r\n");
//發送緩沖區中數據,必須有
bw.flush();

/*** Socket客戶端讀取伺服器端響應數據 ****/
//socket.isConnected代表是否連接成功過
if((socket.isConnected() == true) && (socket.isClosed() == false)){//判斷Socket是否處於連接狀態
//客戶端接收伺服器端的響應,讀取伺服器端向客戶端的輸入流
InputStream is = socket.getInputStream();
//緩沖區
byte[] buffer = new byte[is.available()];
//讀取緩沖區
is.read(buffer);
//轉換為字元串
String responseInfo = new String(buffer);
//日誌中輸出
Log.i("TEST", responseInfo);
} //關閉網路
socket.close();
/***第二種:通過connect方法連接伺服器***/
Socket socket_other = new Socket();
//使用默認的連接超時
socket_other.connect(new InetSocketAddress("192.168.0.7",80));
//連接超時2s
socket_other.connect(new InetSocketAddress("192.168.0.7",80),2000);
//關閉socket
socket_other.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

2. Android socket源碼解析(三)socket的connect源碼解析

上一篇文章著重的聊了socket服務端的bind,listen,accpet的邏輯。本文來著重聊聊connect都做了什麼?

如果遇到什麼問題,可以來本文 https://www.jianshu.com/p/da6089fdcfe1 下討論

當服務端一切都准備好了。客戶端就會嘗試的通過 connect 系統調用,嘗試的和服務端建立遠程連接。

首先校驗當前socket中是否有正確的目標地址。然後獲取IP地址和埠調用 connectToAddress 。

在這個方法中,能看到有一個 NetHooks 跟蹤socket的調用,也能看到 BlockGuard 跟蹤了socket的connect調用。因此可以hook這兩個地方跟蹤socket,不過很少用就是了。

核心方法是 socketConnect 方法,這個方法就是調用 IoBridge.connect 方法。同理也會調用到jni中。

能看到也是調用了 connect 系統調用。

文件:/ net / ipv4 / af_inet.c

在這個方法中做的事情如下:

注意 sk_prot 所指向的方法是, tcp_prot 中 connect 所指向的方法,也就是指 tcp_v4_connect .

文件:/ net / ipv4 / tcp_ipv4.c

本質上核心任務有三件:

想要能夠理解下文內容,先要明白什麼是路由表。

路由表分為兩大類:

每個路由器都有一個路由表(RIB)和轉發表 (fib表),路由表用於決策路由,轉發表決策轉發分組。下文會接觸到這兩種表。

這兩個表有什麼區別呢?

網上雖然給了如下的定義:

但實際上在Linux 3.8.1中並沒有明確的區分。整個路由相關的邏輯都是使用了fib轉發表承擔的。

先來看看幾個和FIB轉發表相關的核心結構體:

熟悉Linux命令朋友一定就能認出這裡面大部分的欄位都可以通過route命令查找到。

命令執行結果如下:

在這route命令結果的欄位實際上都對應上了結構體中的欄位含義:

知道路由表的的內容後。再來FIB轉發表的內容。實際上從下面的源碼其實可以得知,路由表的獲取,實際上是先從fib轉發表的路由字典樹獲取到後在同感加工獲得路由表對象。

轉發表的內容就更加簡單

還記得在之前總結的ip地址的結構嗎?

需要進行一次tcp的通信,意味著需要把ip報文准備好。因此需要決定源ip地址和目標IP地址。目標ip地址在之前通過netd查詢到了,此時需要得到本地發送的源ip地址。

然而在實際情況下,往往是面對如下這么情況:公網一個對外的ip地址,而內網會被映射成多個不同內網的ip地址。而這個過程就是通過DDNS動態的在內存中進行更新。

因此 ip_route_connect 實際上就是選擇一個緩存好的,通過DDNS設置好的內網ip地址並找到作為結果返回,將會在之後發送包的時候填入這些存在結果信息。而查詢內網ip地址的過程,可以成為RTNetLink。

在Linux中有一個常用的命令 ifconfig 也可以實現類似增加一個內網ip地址的功能:

比如說為網卡eth0增加一個IPV6的地址。而這個過程實際上就是調用了devinet內核模塊設定好的添加新ip地址方式,並在回調中把該ip地址刷新到內存中。

注意 devinet 和 RTNetLink 嚴格來說不是一個存在同一個模塊。雖然都是使用 rtnl_register 注冊方法到rtnl模塊中:

文件:/ net / ipv4 / devinet.c

文件:/ net / ipv4 / route.c

實際上整個route模塊,是跟著ipv4 內核模塊一起初始化好的。能看到其中就根據不同的rtnl操作符號注冊了對應不同的方法。

整個DDNS的工作流程大體如下:

當然,在tcp三次握手執行之前,需要得到當前的源地址,那麼就需要通過rtnl進行查詢內存中分配的ip。

文件:/ include / net / route.h

這個方法核心就是 __ip_route_output_key .當目的地址或者源地址有其一為空,則會調用 __ip_route_output_key 填充ip地址。目的地址為空說明可能是在回環鏈路中通信,如果源地址為空,那個說明可能往目的地址通信需要填充本地被DDNS分配好的內網地址。

在這個方法中核心還是調用了 flowi4_init_output 進行flowi4結構體的初始化。

文件:/ include / net / flow.h

能看到這個過程把數據中的源地址,目的地址,源地址埠和目的地址埠,協議類型等數據給記錄下來,之後內網ip地址的查詢與更新就會頻繁的和這個結構體進行交互。

能看到實際上 flowi4 是一個用於承載數據的臨時結構體,包含了本次路由操作需要的數據。

執行的事務如下:

想要弄清楚ip路由表的核心邏輯,必須明白路由表的幾個核心的數據結構。當然網上搜索到的和本文很可能大為不同。本文是基於LInux 內核3.1.8.之後的設計幾乎都沿用這一套。

而內核將路由表進行大規模的重新設計,很大一部分的原因是網路環境日益龐大且復雜。需要全新的方式進行優化管理系統中的路由表。

下面是fib_table 路由表所涉及的數據結構:

依次從最外層的結構體介紹:

能看到路由表的存儲實際上通過字典樹的數據結構壓縮實現的。但是和常見的字典樹有點區別,這種特殊的字典樹稱為LC-trie 快速路由查找演算法。

這一篇文章對於快速路由查找演算法的理解寫的很不錯: https://blog.csdn.net/dog250/article/details/6596046

首先理解字典樹:字典樹簡單的來說,就是把一串數據化為二進制格式,根據左0,右1的方式構成的。

如圖下所示:

這個過程用圖來展示,就是沿著字典樹路徑不斷向下讀,比如依次讀取abd節點就能得到00這個數字。依次讀取abeh就能得到010這個數字。

說到底這種方式只是存儲數據的一種方式。而使用數的好處就能很輕易的找到公共前綴,在字典樹中找到公共最大子樹,也就找到了公共前綴。

而LC-trie 則是在這之上做了壓縮優化處理,想要理解這個演算法,必須要明白在 tnode 中存在兩個十分核心的數據:

這負責什麼事情呢?下面就簡單說說整個lc-trie的演算法就能明白了。

當然先來看看方法 __ip_dev_find 是如何查找

文件:/ net / ipv4 / fib_trie.c

整個方法就是通過 tkey_extract_bits 生成tnode中對應的葉子節點所在index,從而通過 tnode_get_child_rcu 拿到tnode節點中index所對應的數組中獲取葉下一級別的tnode或者葉子結點。

其中查找index最為核心方法如上,這個過程,先通過key左移動pos個位,再向右邊移動(32 - bits)演算法找到對應index。

在這里能對路由壓縮演算法有一定的理解即可,本文重點不在這里。當從路由樹中找到了結果就返回 fib_result 結構體。

查詢的結果最為核心的就是 fib_table 路由表,存儲了真正的路由轉發信息

文件:/ net / ipv4 / route.c

這個方法做的事情很簡單,本質上就是想要找到這個路由的下一跳是哪裡?

在這裡面有一個核心的結構體名為 fib_nh_exception 。這個是指fib表中去往目的地址情況下最理想的下一跳的地址。

而這個結構體在上一個方法通過 find_exception 獲得.遍歷從 fib_result 獲取到 fib_nh 結構體中的 nh_exceptions 鏈表。從這鏈表中找到一模一樣的目的地址並返回得到的。

文件:/ net / ipv4 / tcp_output.c

3. 如何用socket實現android手機與手機之間的通信

參考一般的java的socket編程,如果通過手機網路,就不要使用UDP即可。

4. Android socket通信協議的封裝和解析

android socket通信協議的封裝和解析,其實是和java一樣的,都是通過http中的相關知識來封裝和解析,主要是通過多次握手,如下代碼:

importjava.io.BufferedReader;
importjava.io.BufferedWriter;
importjava.io.IOException;
importjava.io.InputStreamReader;
importjava.io.OutputStreamWriter;
importjava.io.PrintWriter;
importjava.net.ServerSocket;
importjava.net.Socket;
importjava.util.ArrayList;
importjava.util.List;
importjava.util.concurrent.ExecutorService;
importjava.util.concurrent.Executors;


publicclassMain{
privatestaticfinalintPORT=9999;
privateList<Socket>mList=newArrayList<Socket>();
privateServerSocketserver=null;
=null;//threadpool

publicstaticvoidmain(String[]args){
newMain();
}
publicMain(){
try{
server=newServerSocket(PORT);
mExecutorService=Executors.newCachedThreadPool();//createathreadpool
System.out.println("伺服器已啟動...");
Socketclient=null;
while(true){
client=server.accept();
//把客戶端放入客戶端集合中
mList.add(client);
mExecutorService.execute(newService(client));//
}
}catch(Exceptione){
e.printStackTrace();
}
}
{
privateSocketsocket;
privateBufferedReaderin=null;
privateStringmsg="";

publicService(Socketsocket){
this.socket=socket;
try{
in=newBufferedReader(newInputStreamReader(socket.getInputStream()));
//客戶端只要一連到伺服器,便向客戶端發送下面的信息。
msg="伺服器地址:"+this.socket.getInetAddress()+"cometoal:"
+mList.size()+"(伺服器發送)";
this.sendmsg();
}catch(IOExceptione){
e.printStackTrace();
}

}

@Override
publicvoidrun(){
try{
while(true){
if((msg=in.readLine())!=null){
//當客戶端發送的信息為:exit時,關閉連接
if(msg.equals("exit")){
System.out.println("ssssssss");
mList.remove(socket);
in.close();
msg="user:"+socket.getInetAddress()
+"exittotal:"+mList.size();
socket.close();
this.sendmsg();
break;
//接收客戶端發過來的信息msg,然後發送給客戶端。
}else{
msg=socket.getInetAddress()+":"+msg+"(伺服器發送)";
this.sendmsg();
}
}
}
}catch(Exceptione){
e.printStackTrace();
}
}
/**
*循環遍歷客戶端集合,給每個客戶端都發送信息。
*/
publicvoidsendmsg(){
System.out.println(msg);
intnum=mList.size();
for(intindex=0;index<num;index++){
SocketmSocket=mList.get(index);
PrintWriterpout=null;
try{
pout=newPrintWriter(newBufferedWriter(
newOutputStreamWriter(mSocket.getOutputStream())),true);
pout.println(msg);
}catch(IOExceptione){
e.printStackTrace();
}
}
}
}
}

5. android socket接收事件為什麼沒有監聽事件,要用死循環呢

  1. socket是Java API(編程文檔介面),為了直接使用Socekt服務,谷歌直接把Java的Socket模塊照搬過來的

Android 的 onClikcListener onTouchEvent 等是Android API(編程文檔介面)

2.設計目的

編程介面全都是根據需求設定的,比如Google事先考慮開發者有使用監聽事件的需求,才預定義了onClickListener onItemListener這些介面,並且將這些底層實現,封裝到了c和C++層,開發者只需要直接使用Google事先為我們准備好的介面即可。

Socket ,首先Socket並不是Google事先設計好的,Socke中文名稱作套接字,你網路搜套接字編程,可以認識到Socket開發是專門的編程技術,而Socket本身又是網路通信協議的基礎設施,Socket的誕生歷史,高於Android,甚至高於Java,Socekt是計算機提供進程通信能力的編程介面,確切的說,它甚至可以提供不同主機間不同進程的通信能力,(包括同一主機里不同進程的通信能力)

主機?端系統?網路協議?運輸層?傳輸層?套接字?埠?

我說的這些專業名詞,也許你一時半會並不能理解

值得慶幸的是,當你了解到這里,你起碼能想明白,為什麼有的人說,Android 程序,也可以做伺服器了,因為Socket 提供了其他端系統訪問Android程序的能力,能被請求訪問的程序,可以稱作服務端。

想深入了解Socket的設計原理、設計本意,需要深厚的計算機網路知識,在這里我建議你閱讀《計算機網路-自頂向下方法》閱讀前三章,也許你對網路編程會有更深的理解。

總結來說:

Android的Listener系列監聽事件,其實只是響應用戶I/O操作而已,是人與硬體設備的通信,安卓系統提供維持監聽事件的能力,所以你能根據某些事件作出響應

Socket的功能,是提供進程通信的能力,安卓系統並不能直接控制Socket的生命周期,它第一層設計是Java代碼,並不是Google自己研發的,第二層、第三層已經直接深入到運輸層協議、計算機系統層原理了,Google為了省事,直接照搬Java API ,無可厚非。

熱點內容
小學學業查詢賬號密碼是什麼 發布:2023-12-11 07:45:17 瀏覽:950
無線路由器如何設為列印伺服器 發布:2023-12-11 07:40:51 瀏覽:501
徵信密碼忘記了怎麼辦 發布:2023-12-11 07:40:03 瀏覽:240
matlab圖像壓縮 發布:2023-12-11 07:26:52 瀏覽:182
c語言隊列的程序 發布:2023-12-11 07:26:13 瀏覽:274
安卓手機如何更改儲存路徑 發布:2023-12-11 07:20:42 瀏覽:448
如何直接登錄伺服器 發布:2023-12-11 07:05:45 瀏覽:723
伺服器如何節能 發布:2023-12-11 07:00:17 瀏覽:116
雨雨影視源碼 發布:2023-12-11 06:51:10 瀏覽:808
python如何定義數組 發布:2023-12-11 06:39:36 瀏覽:655