Android native進程間通訊實例-socket本地通訊篇之——基本通訊功能

導讀: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

編寫完後編譯,把兩個可執行文件導入設備中,先執行服務端,而後再執行客戶端,發現通訊正常,服務端發送,客戶端接收,彷佛完美?

 

 可是當客戶端強行退出後,服務端進程竟然退出了!!!怎麼辦,且聽下篇分解。

相關文章
相關標籤/搜索