之前看到一篇文章介紹到App之間的五種通訊方式,它分別有URL Scheme,Keychain,UIPastedboard,UIDocumentInteractionController以及利用socket進行本地通訊。前面4種都有用到過,也相對比較簡單,幾行代碼的事。對於最後一種之前一直沒用到過(原諒我還是個小白),所以今天試著寫了下,這兒記錄在這裡和大家分享。
好了,廢話不多說,開始:
首先,說下它的原理,其實很簡單,一個App在本地的連接埠進行TCP的bind和listen,另外一個App在本地同一個連接埠進行connect,這樣就建立了一個正常的TCP串連,可以想傳什麼資料就傳什麼資料。下面開始先建立服務端:
1,首先用socket()函數建立一個通訊端
/* * socket返回一個int值,-1為建立失敗 * 第一個參數指明了協議族/域 ,通常有AF_INET(IPV4)、AF_INET6(IPV6)、AF_LOCAL * 第二個參數指定一個套介面類型:SOCK_STREAM,SOCK_DGRAM、SOCK_SEQPACKET等 * 第三個參數指定相應的傳輸協議,諸如TCP/UDP等,一般設定為0來使用這個預設的值 */int sock = socket(AF_INET, SOCK_STREAM, 0);if(sock == -1){ close(sock); NSLog(@"socket error : %d",sock);<br> return;}
2,綁定本機地址和連接埠號碼
// 地址結構體資料,記錄ip和連接埠號碼struct sockaddr_in sockAddr;// 聲明使用的協議sockAddr.sin_family = AF_INET;// 擷取原生ip,轉換成char類型的const char *ip = [[self getIPAddress] cStringUsingEncoding:NSASCIIStringEncoding];// 將ip賦值給結構體,inet_addr()函數是將一個點分十進位的IP轉換成一個長整數型數sockAddr.sin_addr.s_addr = inet_addr(ip);// 設定連接埠號碼,htons()是將整型變數從主機位元組順序轉變成網路位元組順序sockAddr.sin_port = htons(12345);/* * bind函數用於將通訊端關聯一個地址,返回一個int值,-1為失敗 * 第一個參數指定通訊端,就是前面socket函數調用返回額通訊端 * 第二個參數為指定的地址 * 第三個參數為地址資料的大小 */int bd = bind(sock,(struct sockaddr *) &sockAddr, sizeof(sockAddr));if(bd == -1){ close(sock); NSLog(@"bind error : %d",bd); return;}
3,監聽綁定的地址
/* * listen函數使用主動串連套接介面變為被串連介面,使得可以接受其他進程的請求,返回一個int值,-1為失敗 * 第一個參數是之前socket函數返回的通訊端 * 第二個參數可以理解為串連的最大限制 */int ls = listen(sock,20);if(ls == -1){ close(sock); NSLog(@"listen error : %d",ls); return;}
4,下面就是等待用戶端的串連,使用accept()(由於accept函數會阻塞線程,在等待串連的過程中會一直卡著,所以建議將其放在子線程裡面)
// 1,開啟一個子線程NSTread *recvThread = [[NSThread alloc] initwithTarget:self selector:@selector(recvData) object: nil];[recvThread start]; - (void)recvData{ // 2,等待用戶端串連// 聲明一個地址結構體,用於後面接收用戶端返回的地址 struct sockaddr_in recvAddr;// 地址大小 socklen_t recv_size = sizeof(struct sockaddr_in);/* * accept()函數在串連成功後會返回一個新的通訊端(self.newSock),用於之後和這個用戶端之前收發資料 * 第一個參數為之前監聽的通訊端,之前是局部變數,現在需要改為全域的 * 第二個參數是一個結果參數,它用來接收一個傳回值,這個傳回值指定用戶端的地址 * 第三個參數也是一個結果參數,它用來接收recvAddr結構體的代銷,指明其所佔的位元組數 */self.newSock = accept(self.sock,(struct sockaddr *) &recvAddr, &recv_size);// 3,來到這裡就代表已經串連到一個新的用戶端,下面就可以進行收發資料了,主要用到了send()和recv()函數 ssize_t bytesRecv = -1; // 返回資料位元組大小 char recvData[128] = ""; // 返回資料緩衝區// 如果一端中斷連線,recv就會馬上返回,bytesrecv等於0,然後while迴圈就會一直執行,所以判斷等於0是跳出去 while(1){ bytesRecv = recv(self.newSocket,recvData,128,0); // recvData為收到的資料 if(bytesRecv == 0){ break; } }}
5,發送資料
- (void)sendMessage{ char sendData[32] = "hello client"; ssize_t size_t = send(self.newSocket, sendData, strlen(sendData), 0); }
用戶端那邊就主要分為:建立通訊端,根據ip和連接埠號碼擷取服務端的主機地址,然後再串連,串連成功過後就能夠向服務端收發資料了,下面我們看代碼。
1,和服務端一樣用socket函數建立通訊端
int sock = socket(AF_INET, SOCK_STREAM,0);if(sock == -1){ NSLog(@"socket error : %d",sock); return;}
2,擷取主機的地址
NSString *host = [self getIPAddress]; // 擷取本機ip地址// 返回對應於給定主機名稱的包含主機名稱字和地址資訊的hostent結構指標struct hostent *remoteHostEnt = gethostbyname([host UTF8String]);if(remoteHostEnt == NULL){ close(sock); NSLog(@"無法解析伺服器主機名稱"); return;}<br>// 配置通訊端將要串連主機的ip地址和連接埠號碼,用於connect()函數struct in_addr *remoteInAddr = (struct in_addr *)remoteHost->h_addr_list[0];struct sockaddr_in socktPram;socketPram.sin_family = AF_INT;socketPram.sin_addr = *remoteInAddr;socketPram.sin_port = htons([port intValue]);
3,使用connect()函數串連主機
/* * connect函數通常用於用戶端簡曆tcp串連,串連指定地址的主機,函數返回一個int值,-1為失敗 * 第一個參數為socket函數建立的通訊端,代表這個通訊端要串連指定主機 * 第二個參數為通訊端sock想要串連的主機地址和連接埠號碼 * 第三個參數為主機地址大小 */int con = connect(sock, (struct sockaddr *) &socketPram, sizeof(socketPram));if(con == -1){ close(sock); NSLog(@"串連失敗"); return;}NSLog("串連成功"); // 來到這代表串連成功;
4,串連成功之後就可以收發資料了
- (IBAction)senddata:(id)sender { // 發送資料 char sendData[32] = "hello service"; ssize_t size_t = send(self.sock, sendData, strlen(sendData), 0); NSLog(@"%zd",size_t);} - (void)recvData{ // 接受資料,放在子線程 ssize_t bytesRecv = -1; char recvData[32] = ""; while (1) { bytesRecv = recv(self.sock, recvData, 32, 0); NSLog(@"%zd %s",bytesRecv,recvData); if (bytesRecv == 0) { break; } }}
好了,利用socket在本地進行兩個App的通訊就這樣就行了。第一次寫博文,一是記錄下自己的心得,二是和大家一起分享,文中有不對的地方希望大家可以指出。最後附上Demo的地址,兩個項目,有興趣的大家可以下下來試下。
https://pan.baidu.com/s/1nvcvC8p
以上就是對iOS App之間的通訊 -local socket 的資料整理,後續繼續補充相關資料,謝謝大家對本站的支援!