android socket java和C通信

来源:互联网 发布:linux自动挂载硬盘 编辑:程序博客网 时间:2024/06/06 00:01

原址

最近一直在弄一些Android跟socket相关的东西,打算好好总结一下Android socket的使用。其实Android是将进程的概念给弱化了的,但是进程还是存在的,基本上一般的app,如果你没有特殊处理的话,一个app就是一个进程。我们平时开发App的时候,更多的是基于Android基本组件概念的,也就是说Android四大组件 Activity,Service,BroadCast receiver,Content provider。但是如果需要在进程间通信,socket 是一个很不错的选择。当然,上一篇博客也讲到过管道。管道也是可以实现进程间通信的。下面主要从NDK以及java层对Android socket 相关的一些东西进行总结介绍。

我们在其他地方使用socket 一般是一个地址,一个端口。但是在Android中,建立socket 是通过一个叫做socketName 的东西。客户端和服务器端使用同一个socketName就可以建立连接。服务器端建一个指定socketName的socketserver,客户端连接到指定socketName的Socket服务器。下面介绍具体的方式。

NDK 层

android jni socket 其实就是linux socket。本身android就是基于linux 内核的。所以linux那一套东西,在android上面基本上是适用的,当然也有一些不同。

SOCKET SERVER

首先描述一下整个过程。先通过函数android_get_socket(SOCKET_NAME)来获取一个监听号。然后使用得到的监听号,以及设置的最大请求数,监听这个监听号。最后调用accept 函数等待请求。接收到数据方面的东西其实跟linux是一样的。下面看从网友提供的代码简化的具体的代码

#define SOCKET_NAME "socketnamexxx"#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 <sys/wait.h>#include <sys/un.h>#include <cutils/sockets.h>#include <utils/Log.h>#include <android/log.h>int main(){    char log[200];    int connect_number = 6;    int fdListen = -1, new_fd = -1;    int ret;    struct sockaddr_un peeraddr;    socklen_t socklen = sizeof (peeraddr);    int numbytes ;    char buff[256];    //这一步很关键,就是获取init.rc中配置的名为 "htfsk" 的socket    //fdListen = android_get_control_socket(SOCKET_NAME);    //上面这个api 可能在高版本的ndk中已经不存在了。    fdListen = socket(AF_LOCAL, SOCK_STREAM, PF_UNIX);    if(fdListen < 0)        return (false);    // set socket is reusable    int option = true;    if(setsockopt(fdListen, SOL_SOCKET, SO_REUSEADDR, (char *)&option, sizeof(option)) < 0)    {        //close(fdListen);        return (false);    }    if(bind(fdListen, (const struct sockaddr*)& peeraddr, socklen) < 0)    {        close(fdListen);        return (false);    }    if (fdListen < 0) {        sprintf(log,"Failed to get socket '" SOCKET_NAME "' errno:%d", errno);        __android_log_write(ANDROID_LOG_DEBUG,"FTM_JNI",log);        exit(-1);    }    //开始监听    ret = listen(fdListen, connect_number);        if (ret < 0) {  //小于0 表示监听失        sprintf(log,"Listen result %d",ret);            __android_log_write(ANDROID_LOG_DEBUG,"FTM_JNI",log);        exit(-1);    }    //等待Socket客户端发启连接请求    new_fd = accept(fdListen, (struct sockaddr *) &peeraddr, &socklen);    sprintf(log,"Accept_fd %d",new_fd);    __android_log_write(ANDROID_LOG_DEBUG,"FTM_JNI",log);    if (new_fd < 0 ) {        sprintf(log,"%d",errno);        __android_log_write(ANDROID_LOG_DEBUG,"FTM_JNI",log);        perror("accept error");        exit(-1);    }    while(1){        //循环等待Socket客户端发来消息        __android_log_write(ANDROID_LOG_DEBUG,"FTM_JNI","Waiting for receive");        if((numbytes = recv(new_fd,buff,sizeof(buff),0))==-1){            sprintf(log,"%d",errno);            __android_log_write(ANDROID_LOG_DEBUG,"FTM_JNI",log);            perror("recv");            continue;        }        //发送消息回执给Socket客户端        if(send(new_fd,buff,strlen(buff),0)==-1)        {            perror("send");            close(new_fd);            exit(0);        }            }    close(new_fd);    close(fdListen);    return 0;}

其实认真跟linux的函数比较一下就会发现。这个函数android_get_socket(SOCKET_NAME) 是将端口,都整合好了。通过SocketName来指定对应的位置。弱化了原本端口和地址的概念。其他的accept,接收recv 和发送 send 其实是跟linux一样的。

SOCKET CLIENT

Client 也是依据SOCKET_NAME 来与对应的Server建立连接。具体的Demo程序如下:

char[10] socketName = "socketnamexxx"; //socketName  int clientFD = 0 ;socklen_t serverLen = 0;struct sockaddr_un serverAddr;memset(&serverAddr, 0, sizeof(serverAddr));//init socket// check socket name to avoid overflowif (strlen(socketName) > (int) sizeof(serverAddr.sun_path))    return 0;serverAddr.sun_path[0] = '\0';strcpy(serverAddr.sun_path + 1, socketName);serverAddr.sun_family = AF_LOCAL;serverLen = 1 +  strlen(socketName) + offsetof(struct sockaddr_un, sun_path);if ((clientFD = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {    __android_log_write(ANDROID_LOG_ERROR, LOGTAG_CCLIENT,            "create socket error");    return 0;}sockaddr pserverAddr = (sockaddr) serverAddr;if (connect(&clientFD, &pserverAddr, serverLen) == -1) {    //连接失败            return 0;}char[6] data="hello";if (send(clientFD, data, 6, 0) == -1) {    __android_log_write(ANDROID_LOG_ERROR, LOGTAG_CCLIENT,            "send message error");    close(clientFD);    return 0;}

Java层

在java层的话,其实相对来说就简单了。当然指定位置的同样是SocketName。另外两个就是LocalSocket 跟LocalServerSocket了。接着就是java 通用的输入输出流了。

借用一下网友的图片来直接描述一下他们直接的关系:

android-socket.jpg

在Client Socket 范例代码是:

LocalSocket socket = new LocalSocket();LocalSocketAddress address = new LocalSocketAddress(SocketName,LocalSocketAddress.Namespace.RESERVED);//第二个参数是命名空间,直接用上面那个enum 选择一个就好socket.connect(address);//socket.getInputStream(); 读取//socket.getOutputStream(); 写入

在Server Socket 如下:

LocalServerSocket server = new LocalServerSocket(SocketName);try{    LocalSocket receiver = server.accept();}catch(IOException e){    e.printStackTrace();}//读取与写入跟客户端类似,都是利用流来做的。//receiver.getInputStream(); 读取//receiver.getOutputStream(); 写入

总结

其实Socket 连接关键还是在于地址。客户端和服务器端,其实都是指定了相同的地址。服务器端监听,客户端向该地址请求。在Android socket中,它替换了直接的地址,而是用名称来标志,这样就变得更加简单。但是万变不离其宗,概念还是依据于基本的socket的。

之后再写一个demo程序。但是个人有时候觉得,demo能够让你快速了解,但是到了一定程度后,跑个程序时累赘。信手拈来才是Perfect的。

https://github.com/xxxzhi/AndroidSocketDemo


大四已经逐渐完了,在读研方面,曾对我有醍醐灌顶之效的一句话是:厚积薄发。