zoukankan      html  css  js  c++  java
  • Android native进程间通信实例-socket本地通信篇之——基本通信功能

    导读:

      网上看了很多篇有关socket本地通信的示例,很多都是调通服务端和客户端通信功能后就没有下文了,不太实用,真正开发中遇到的问题以及程序稳定性部分没有涉及,代码健壮性不够,本系列(socket本地通信篇)会先直接调通linux本地socket通信,提供最基本的服务端和客户端代码,然后根据实际开发中遇到的问题和优化建议,再提供一版健壮版本的服务端代码。再次明确一点,本篇博文不会搬移太多概念性的东西,比如三次握手协议,还有各个unix系统调用的功能。

       

    1.服务端:

      先捋清调用的一个时间顺序,UNIX中服务端的标准API设置如下:

    a. socket设置通信域等信息获取一个fd(文件描述符)

    b. bind设置相关参数,如获取的fd,sockaddr_un信息等

    c. listen标记fd,查看这个fd是否ok

    d. accept填入fd和即将连接的客户端的sockaddr_un信息,阻塞着,直到有客户端连接,消除阻塞

    (注意:以上具体API信息可以查看man手册,如listen,在ubuntu系统中输入man 2 listen即可查阅)

      

    明确了以上信息后,就可以开始着手写代码了!代码如下:

    #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
    ";
        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 
    ");
            memset(&cliaddr,0,sizeof(cliaddr));
            clifd = accept(servfd,(struct sockaddr *)&cliaddr,&addrlen);
            if(clifd == -1)
            {
                printf("accept connect failed
    ");
                continue;
            }
            ret = pthread_create(&tid,NULL,client_thread,&clifd);
            if(0 != ret)
            {
                printf("pthread_create failed 
    ");
            }
    
            ret = pthread_join(tid, NULL);
            if(0 != ret)
            {
                printf("pthread_join failed 
    ");
            }
            printf("exit thread
    ");
            /*
            ret = pthread_detach(tid);
            if(0 != ret)
            {
                printf("pthread_detach failed 
    ");
                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
    ");
            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
    ");
            return -1;
        }
    
        char getData[100];    
        while(1)
        {
            bzero(&getData,sizeof(getData));
            ret = read(clifd,&getData,sizeof(getData));
            if(ret > 0)
            {
                printf("rcv message: %s
    ", getData);
            }
        }
    
        return 0;
    }

    3. 运行程序及思考

    android.mk的编写和之前一样,只需要更改cpp文件名以及LOCAL_MODULE名,传送门:https://www.cnblogs.com/songsongman/p/11097196.html

    编写完后编译,把两个可执行文件导入设备中,先执行服务端,然后再执行客户端,发现通信正常,服务端发送,客户端接收,似乎完美?

     但是当客户端强行退出后,服务端进程居然退出了!!!怎么办,且听下篇分解。

  • 相关阅读:
    Atitit.隔行换色  变色 css3 结构性伪类选择器
    Atitit.jpg png格式差别以及解决jpg图片不显示的问题
    Atitit.jpg png格式差别以及解决jpg图片不显示的问题
    Atitit.预定义变量与变量预处理器
    Atitit.jpg png格式差别以及解决jpg图片不显示的问题
    Atitit.预定义变量与变量预处理器
    Atitit.增强系统稳定性----虚拟内存的设置
    Atitit.md5 实现原理
    Atitit.增强系统稳定性----虚拟内存的设置
    Atitit.md5 实现原理
  • 原文地址:https://www.cnblogs.com/songsongman/p/11187844.html
Copyright © 2011-2022 走看看