专栏首页编程入门之C语言Android native进程间通信实例-socket本地通信篇之——基本通信功能

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\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名,传送门:https://www.cnblogs.com/songsongman/p/11097196.html

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

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Android native进程间通信实例-binder篇之——简单的单工通信

    网上找了很多binder相关文章,大部分都是在跟踪binder实现源代码,然后再把框架代码贴出来,看着实在费力。

    啊源股
  • C语言编程入门之--第五章C语言基本运算和表达式-part4

      二进制数据中,比如一个字节的数据,它的十进制为228,二进制就为11100100,如图5.11,

    啊源股
  • Android native进程间通信实例-socket本地通信篇之——服务端进程异常退出解决办法

    想想,如果手机上使用一个聊天程序的时候,手机端关闭了聊天程序,那么远端服务器程序总不能说挂就挂吧!所以一定要查明真相。

    啊源股
  • LightOJ_1038 Race to 1 Again

      给一个数n, 每次操作是随机的选择一个[1,N]区间内能够被n整除的数进行除法, 然后得到一个新的n。

    若羽
  • C/C++常用头文件及函数汇总

    C/C++头文件一览 C #include <assert.h>    //设定插入点 #include <ctype.h>     //字符处理 #inclu...

    互联网金融打杂
  • 【C++100问】深度总结STL基本容器的使用

    文章首发于本人CSDN账号:https://blog.csdn.net/tefuirnever

    我是管小亮
  • 运行程序时报错“Value too large for defined data type”

    此错误对应的出错代码为EOVERFLOW,原因可能是目标文件超过2GB大小。

    一见
  • C++头文件中的bits/stdc++.h——万能头文件

    所以,以后只要包含上这个头文件,就不用再写一大堆头文件了,也不用担心写一个函数,编译之后告诉你没有定义了,以后写代码,就决定是你了!

    种花家的奋斗兔
  • Visual Studio 中万能头文件编译不了的解决方案

    很多轻量级IDE cb devcpp 包括vscode都支持万能头文件,但是vs没有,但是敲代码的时候敲一个万能头文件岂不是省下很多事

    glm233
  • AtCoder Beginner Contest 168 C

    昨晚做这个时钟的题,真的是,做的我吐了,忘了考虑几小时几分钟的时候,不是对应在整数点的,而是有偏差的

    用户7727433

扫码关注云+社区

领取腾讯云代金券