導讀:html
網上看了不少篇有關socket本地通訊的示例,不少都是調通服務端和客戶端通訊功能後就沒有下文了,不太實用,真正開發中遇到的問題以及程序穩定性部分沒有涉及,代碼健壯性不夠,本系列(socket本地通訊篇)會先直接調通linux本地socket通訊,提供最基本的服務端和客戶端代碼,而後根據實際開發中遇到的問題和優化建議,再提供一版健壯版本的服務端代碼。再次明確一點,本篇博文不會搬移太多概念性的東西,好比三次握手協議,還有各個unix系統調用的功能。linux
1.服務端:android
先捋清調用的一個時間順序,UNIX中服務端的標準API設置以下:ubuntu
a. socket設置通訊域等信息獲取一個fd(文件描述符)socket
b. bind設置相關參數,如獲取的fd,sockaddr_un信息等tcp
c. listen標記fd,查看這個fd是否ok優化
d. accept填入fd和即將鏈接的客戶端的sockaddr_un信息,阻塞着,直到有客戶端鏈接,消除阻塞spa
(注意:以上具體API信息能夠查看man手冊,如listen,在ubuntu系統中輸入man 2 listen便可查閱)線程
明確了以上信息後,就能夠開始着手寫代碼了!代碼以下:unix
#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <netinet/in.h> #include <sys/socket.h> #include <arpa/inet.h> #include <sys/wait.h> #include <sys/un.h> #include <cutils/sockets.h> #include <utils/Log.h> #include <android/log.h> #include <linux/tcp.h> #define MYSOCKET_PATH "/tmp/mysocket" void *client_thread(void *arg) { int clifd = *(int *)arg;char *s = "hello mysocketclient\n"; while(1) { usleep(1000000); write(clifd,s,strlen(s));//也可使用send(clifd,s,strlen(s),0); } return (void *)0; } int main(int argc,char **arg) { int servfd; int ret; servfd = socket(AF_LOCAL,SOCK_STREAM,0); if(-1 == servfd) { perror("Can not create socket"); return -1; } struct sockaddr_un servaddr; bzero(&servaddr, sizeof(servaddr)); strcpy(servaddr.sun_path+1, MYSOCKET_PATH); servaddr.sun_family = AF_LOCAL; socklen_t addrlen = 1 + strlen(MYSOCKET_PATH) + sizeof(servaddr.sun_family); ret = bind(servfd, (struct sockaddr *)&servaddr, addrlen); if(-1 == ret) { perror("bind failed"); return -1; } ret = listen(servfd, 100); if(-1 == ret) { perror("listen failed"); return -1; } int clifd = -1;; pthread_t tid; struct sockaddr_un cliaddr; while(1) { printf("wait for next client connect \n"); memset(&cliaddr,0,sizeof(cliaddr)); clifd = accept(servfd,(struct sockaddr *)&cliaddr,&addrlen); if(clifd == -1) { printf("accept connect failed\n"); continue; } ret = pthread_create(&tid,NULL,client_thread,&clifd); if(0 != ret) { printf("pthread_create failed \n"); } ret = pthread_join(tid, NULL); if(0 != ret) { printf("pthread_join failed \n"); } printf("exit thread\n"); /* ret = pthread_detach(tid); if(0 != ret) { printf("pthread_detach failed \n"); return -1; }*/ } return 0; }
可知 #define MYSOCKET_PATH "/tmp/mysocket" 定義的字符串用於本地socket通訊服務端和客戶端關聯。
再看看while(1)裏面作了什麼:
代碼運行阻塞在accept這裏,等着客戶端的鏈接,若是客戶端鏈接上之後,建立一個線程傳入客戶端的fd,而後在線程裏面處理客戶端的信息,這裏建立的線程默認一直髮送字符串給客戶端。
線程的回收能夠用到pthread_join或者pthrea_detach,而後能夠等待下一個客戶端鏈接,最大鏈接數在listen的時候有設置。
服務端代碼還沒法驗證,由於客戶端代碼還沒寫好。
2.客戶端:
客戶端代碼更好寫一點,捋清順序:
1. socket設置通訊域等信息獲取一個fd(文件描述符)
2. connect根據socket設置的信息來鏈接服務端,信息中包括那個關鍵字符串MYSOCKET_PATH
代碼以下:
#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <netinet/in.h> #include <sys/socket.h> #include <arpa/inet.h> #include <sys/wait.h> #include <sys/un.h> #include <cutils/sockets.h> #include <utils/Log.h> #include <android/log.h> #define SOCKET_PATH "/tmp/mysocket" int main(int argc,char **arg) { int clifd; int ret; clifd = socket(AF_LOCAL, SOCK_STREAM, 0); if(-1 == clifd) { perror("socket create failed\n"); return -1; } struct sockaddr_un cileddr; bzero(&cileddr, sizeof(cileddr)); strcpy(cileddr.sun_path + 1, SOCKET_PATH); cileddr.sun_family = AF_LOCAL; socklen_t addrlen = sizeof(cileddr.sun_family) + strlen(SOCKET_PATH) + 1; ret = connect(clifd, (struct sockaddr *)&cileddr, addrlen); if(ret == -1) { perror("Connect fail\n"); return -1; } char getData[100]; while(1) { bzero(&getData,sizeof(getData)); ret = read(clifd,&getData,sizeof(getData)); if(ret > 0) { printf("rcv message: %s\n", getData); } } return 0; }
3. 運行程序及思考
android.mk的編寫和以前同樣,只須要更改cpp文件名以及LOCAL_MODULE名,傳送門:http://www.javashuo.com/article/p-soutduzp-eg.html
編寫完後編譯,把兩個可執行文件導入設備中,先執行服務端,而後再執行客戶端,發現通訊正常,服務端發送,客戶端接收,彷佛完美?
可是當客戶端強行退出後,服務端進程竟然退出了!!!怎麼辦,且聽下篇分解。