android socket什么意思
android socket
安卓插座
socket
[英][ˈsɒkɪt][美][ˈsɑ:kɪt]
n.
插座; 燈座; 窩,穴; [解]眼窩,孔窩;
vt.
把…裝入插座; 給…配插座;
第三人稱單數:sockets復數:sockets現在分詞:socketing過去式:
1
Never overload an electrical socket.
千萬不要使電源插座過載。
2
Now when He saw that He did not prevail against him, He touched the socket of his hip; and the socket of Jacob's hip was out of joint as He wrestled with him.
那人見自己勝不過他,就將他的大腿窩摸了一把,雅各的大腿窩正在摔跤的時候就扭了。
Android-Socket
由于二者不屬于同一層面,所以本來是沒有可比性的。但隨著發展,默認的Http里封裝了下面幾層的使用,所以才會出現Socket & HTTP協議的對比:(主要是工作方式的不同):
Socket可理解為一種特殊的文件,在服務器和客戶端各自維護一個文件,并使用SocketAPI函數對其進行文件操作。在建立連接打開后,可以向各自文件寫入內容供對方讀取或讀取對方內容,通信結束時關閉文件。在UNIX哲學中“一切皆文件”,文件的操作模式基本為“打開-讀寫-關閉”三大步驟,Socket其實就是這個模式的一個實現。
創建socket的時候,也可以指定不同的參數創建不同的socket描述符,socket函數的三個參數分別為:
當我們調用socket創建一個socket時,返回的socket描述字它存在于協議族(address family,AF_XXX)空間中,但沒有一個具體的地址。如果想要給它賦值一個地址,就必須調用bind()函數,否則就當調用connect()、listen()時系統會自動隨機分配一個端口。
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
函數的三個參數分別為:
如果作為一個服務器,在調用socket()、bind()之后就會調用listen()來監聽這個socket,如果客戶端這時調用connect()發出連接請求,服務器端就會接收到這個請求。
TCP服務器端依次調用socket()、bind()、listen()之后,就會監聽指定的socket地址了。TCP客戶端依次調用socket()、connect()之后就想TCP服務器發送了一個連接請求。TCP服務器監聽到這個請求之后,就會調用accept()函數取接收請求,這樣連接就建立好了。之后就可以開始網絡I/O操作了,即類同于普通文件的讀寫I/O操作。
注意:accept的第一個參數為服務器的socket描述字,是服務器開始調用socket()函數生成的,稱為監聽socket描述字;而accept函數返回的是已連接的socket描述字。一個服務器通常通常僅僅只創建一個監聽socket描述字,它在該服務器的生命周期內一直存在。內核為每個由服務器進程接受的客戶連接創建了一個已連接socket描述字,當服務器完成了對某個客戶的服務,相應的已連接socket描述字就被關閉。
萬事具備只欠東風,至此服務器與客戶已經建立好連接了。可以調用網絡I/O進行讀寫操作了,即實現了網咯中不同進程之間的通信!網絡I/O操作有下面幾組:
read()/write()
recv()/nd()
readv()/writev()
recvmsg()/ndmsg()
recvfrom()/ndto()
我推薦使用recvmsg()/ndmsg()函數,這兩個函數是最通用的I/O函數,實際上可以把上面的其它函數都替換成這兩個函數。
從圖中可以看出,當客戶端調用connect時,觸發了連接請求,向服務器發送了SYN J包,這時connect進入阻塞狀態;服務器監聽到連接請求,即收到SYN J包,調用accept函數接收請求向客戶端發送SYN K ,ACK J+1,這時accept進入阻塞狀態;客戶端收到服務器的SYN K ,ACK J+1之后,這時connect返回,并對SYN K進行確認;服務器收到ACK K+1時,accept返回,至此三次握手完畢,連接建立。
總結:客戶端的connect在三次握手的第二個次返回,而服務器端的accept在三次握手的第三次返回。
某個應用進程首先調用clo主動關閉連接,這時TCP發送一個FIN M;
另一端接收到FIN M之后,執行被動關閉,對這個FIN進行確認。它的接收也作為文件結束符傳遞給應用進程,因為FIN的接收意味著應用進程在相應的連接上再也接收不到額外數據;
一段時間之后,接收到文件結束符的應用進程調用clo關閉它的socket。這導致它的TCP也發送一個FIN N;
接收到這個FIN的源發送端TCP對它進行確認。
這樣每個方向上都有一個FIN和ACK。
所謂短連接,即連接只保持在數據傳輸過程,請求發起,連接建立,數據返回,連接關閉。它適用于一些實時數據請求,配合輪詢來進行新舊數據的更替。
https://github.com/nuisanceless/MySocketDemo
https://github.com/xuuhaoo/OkSocket
如何用socket實現android手機與手機之間的通信
有兩種方案:
1、在PC機上建立服務器,手機與手機之間的通信通過服務器進行中轉
2、一部手機作為服務器,另一部手機作為客戶端接入該手機
一般是第一種方案
示例代碼:
1、pc端:
rverSocket=newServerSocket(5648);//在5648端口進行偵聽
Socketsk=rverSocket.accept();//如果有接入,則創建對應的socket;
2、手機端:
socket=newSocket("tobacco5648.xicp.net",5648);//連接socket
3、消息輸入輸出:
pw=newPrintWriter(socket.getOutputStream());//消息輸出
pw.println("發送消息");
pw.flush();
br=newBufferedReader(newInputStreamReader(socket.getInputStream()));//消息接收
while((str=br.readLine())!=null){
//接收消息
}
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>();
privateServerSocketrver=null;
privateExecutorServicemExecutorService=null;//threadpool
publicstaticvoidmain(String[]args){
newMain();
}
publicMain(){
try{
rver=newServerSocket(PORT);
mExecutorService=Executors.newCachedThreadPool();//createathreadpool
System.out.println("服務器已啟動...");
Socketclient=null;
while(true){
client=rver.accept();
//把客戶端放入客戶端集合中
mList.add(client);
mExecutorService.execute(newService(client));//startanewthreadtohandletheconnection
}
}catch(Exceptione){
e.printStackTrace();
}
}
classServiceimplementsRunnable{
privateSocketsocket;
privateBufferedReaderin=null;
privateStringmsg="";
publicService(Socketsocket){
this.socket=socket;
try{
in=newBufferedReader(newInputStreamReader(socket.getInputStream()));
//客戶端只要一連到服務器,便向客戶端發送下面的信息。
msg="服務器地址:"+this.socket.getInetAddress()+"cometoal:"
+mList.size()+"(服務器發送)";
this.ndmsg();
}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.clo();
msg="ur:"+socket.getInetAddress()
+"exittotal:"+mList.size();
socket.clo();
this.ndmsg();
break;
//接收客戶端發過來的信息msg,然后發送給客戶端。
}el{
msg=socket.getInetAddress()+":"+msg+"(服務器發送)";
this.ndmsg();
}
}
}
}catch(Exceptione){
e.printStackTrace();
}
}
/**
*循環遍歷客戶端集合,給每個客戶端都發送信息。
*/
publicvoidndmsg(){
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();
}
}
}
}
}
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
Android的Socket是全雙工的嗎
是的,可以同時讀寫的,這個“同時”讀寫 有點虛,計算機執行的時候是有先后的,你可以開啟2個線程 進行socket讀寫,一個線程讀,一個線程寫,不知道你的業務上是如何處理的,如果高并發可以考慮 MIMA NETTY框架,具體還是要看代碼的實現方式,socket只是套接字,全雙工是指同一個socket連接 同時完成收和發的功能