Android NDK网络通信篇(五)之TCP通信篇

来源:互联网 发布:rgb转化到hsv的算法 编辑:程序博客网 时间:2024/06/06 04:29

Android NDK网络通信篇(五)

TCP通信篇

前言

本篇首先和大家讲解一下NDK原生TCP网络通信的函数,之后再和大家详细讲解一下TCP客户端和服务端网络通信的流程并提供对应的示例代码。

TCP通信相关的头文件

#include <sys/socket.h>

#include <sys/un.h>

#include <sys/endian.h>

#include <sys/types.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <unistd.h>

TCP通信函数解析

创建TCP socket

int socket(int domain, int type, int protocal);

参数解析:

domain:指定将会产生通讯的socket域,并且选择用到的协议族。android平台目前支持以下协议族:

l  PF_LOCAL:主机内部通讯协议族,该协议族使用物理上运行在同一台设备上的应用程序可以使用Socket APIs进行通信。

l  PF_INET:IPv4协议族,该协议族使得运行的应用程序可以与网络上的其它应用程序进行通讯。

type:指通信的类型,支持以下两种类型:

SOCK_STREAM:供TCP协议使用。

SOCK_DGRAM:供UDP协议使用。

protocal:指定将会用到的协议。对于大部分协议族和协议类型来说,只能使用一个协议。为了选择默认的协议,该参数可以设为零。

如果socket创建成功,将会返回对应的socket描述符,否则返回-1。

示例代码:

int NewTcpSocket(JNIEnv *env,jobject obj){    int sd=socket(PF_INET,SOCK_STREAM,0);    return sd;}

绑定TCP socket

int bind(int socketDescriptor, const struct sockaddr* address, int addressLength);

参数解析:

socketDescriptor:指服务端socket

address:指socket要绑定的服务端地址结构体

addressLength:指定address结构体的大小

如果绑定成功,返回0,否则返回-1

示例代码:

sockaddr_in getSockaddr_in(const char *ip,int port){    sockaddr_in address;    memset(&address,0, sizeof(sockaddr_in));    address.sin_family=AF_INET;    address.sin_port=htons(port);    inet_aton(ip,&address.sin_addr);    return address;}
int BindSocket(JNIEnv *env,jobject obj,int sd,const char *ip,int port){    sockaddr_in address=getSockaddr_in(ip,port);    int result=bind(sd,(sockaddr *)&address, sizeof(address));    return result;}

监听TCP连接

int listen(intsocketDescriptor, int backlog);

参数解析:

socketDescriptor:指服务端socket

backlog:指服务端可以接受的最大的连接数,如果连接请求超过这个最大数,超过的请求就会排队。

如果函数调用成功返回0,否则返回-1

示例代码:

int ListenOnSocket(JNIEnv*env,jobject obj,intsd,int backlog){
    int result=listen(sd,backlog);
    return result;
}

接受TCP连接

int accept(intsocketDescriptor, struct sockaddr* address, socklen_t* addressLength);

参数解析:

socketDescriptor:指服务端socket

address:指sockaddr结构体,这个结构体将被填入客户端的信息

addressLength:address结构体的大小

如果函数调用成功返回0,否则返回-1

示例代码:

int AcceptClientSocket(JNIEnv*env,jobject obj,intsd){
    sockaddr_in address;
    socklen_t len= sizeof(address);
    int client_socket=accept(sd,(sockaddr*)&address,&len);
    return client_socket;
}

接收TCP消息

ssize_t recv(intsocketDescriptor, void* buffer,size_t bufferLength, intflags);

参数解析:

socketDescriptor:指客户端socket

buffer:指接收的数据缓冲区指针

bufferLength:指接收的数据缓冲区的大小

flags:指接收需要的额外标志,默认传0

如果函数调用成功返回0,否则返回-1

示例代码:

int ReceiveFromSocket(JNIEnv*env,jobject obj,intsd,char *buffer,intbufferLen){
    int receive_size=recv(sd,buffer,bufferLen-1,0);
    return receive_size;
}

发送TCP消息

ssize_t send(intsocketDescriptor, const void* buffer,size_t bufferLength, intflags);

参数解析:

socketDescriptor:指客户端socket

buffer:指接收的数据缓冲区指针

bufferLength:指接收的数据缓冲区的大小

flags:指接收需要的额外标志,默认传0

如果函数调用成功返回0,否则返回-1

示例代码:

int SendToSocket(JNIEnv*env,jobject obj,intsd,const char *buffer,intbufferLen){
    int send_size=send(sd,buffer,bufferLen,0);
    return send_size;
}

获取客户端socket 信息

int getpeername(intsocketDescriptor, struct sockaddr* address, socklen_t* addressLength);

参数解析:

socketDescriptor:指客户端socket

address:指sockaddr结构体,这个结构体将被填入客户端的信息

addressLength:address结构体的大小

如果函数调用成功返回0,否则返回-1

示例代码:

struct SocketInfo{
    int port;
    const char *ip;
};

SocketInfo * GetClientSocketInfo(JNIEnv*env,jobject obj,intsd){
    sockaddr_in address;
    socklen_t addressLength= sizeof(address);
    getpeername(sd,(sockaddr *)&address,&addressLength);
    SocketInfo *socketInfo=newSocketInfo;
    socketInfo->port=ntohs(address.sin_port);
    socketInfo->ip=inet_ntoa(address.sin_addr);
    return socketInfo;
}

获取服务端socket信息

int getsockname(intsocketDescriptor, struct sockaddr* address, socklen_t* addressLength);

参数解析:

socketDescriptor:指服务端socket

address:指sockaddr结构体,这个结构体将被填入服务端的信息

addressLength:address结构体的大小

如果函数调用成功返回0,否则返回-1

示例代码:

struct SocketInfo{
    int port;
    const char *ip;
};

SocketInfo * GetServerSocketInfo(JNIEnv*env,jobject obj,intsd){
    sockaddr_in address;
    socklen_t addressLength= sizeof(address);
    getsockname(sd,(sockaddr *)&address,&addressLength);
    SocketInfo *socketInfo=newSocketInfo;
    socketInfo->port=ntohs(address.sin_port);
    socketInfo->ip=inet_ntoa(address.sin_addr);
    return socketInfo;
}

连接服务端socket

int connect(intsocketDescriptor, const struct sockaddr* address, socklen_t addressLength);

参数解析:

socketDescriptor:指客户端socket

address:指要连接的服务端地址结构体

addressLength:指定address结构体的大小

如果绑定成功,返回0,否则返回-1

示例代码:

sockaddr_in getSockaddr_in(const char*ip,int port){
    sockaddr_in address;
    memset(&address,0, sizeof(sockaddr_in));
    address.sin_family=AF_INET;
    address.sin_port=htons(port);
    inet_aton(ip,&address.sin_addr);

    return address;
}

int ConnectSocket(JNIEnv*env,jobject obj,intsd,const char *ip,intport){
    sockaddr_in address=getSockaddr_in(ip,port);

    int result=connect(sd,(sockaddr*)&address, sizeof(address));
    return result;
}

TCP服务端

TCP服务端程序流程图

1.   创建TCP socket(socket())

2.   绑定TCP socket(bind())

3.   监听TCP连接(listen())

4.   接受TCP连接(accept())

5.   接收TCP消息(recv())

6.   发送TCP消息(send())

TCP服务端程序示例

JavaVM *gVM;
jobject gThiz;

jint JNI_OnLoad(JavaVM*vm, void *reserved){
    gVM=vm;
    return JNI_VERSION_1_4;
}

 

extern "C"
JNIEXPORT voidJNICALL
Java_com_kgdwbb_jnistudy_MainActivity_startServerSocket(JNIEnv* env,jobject thiz){
    if(gThiz==NULL){
        gThiz=env->NewGlobalRef(thiz);
    }
    pthread_t pthread;
    pthread_create(&pthread,NULL,serverThread,NULL);
}

 

void *serverThread(void* args){
    JNIEnv *env;
    gVM->AttachCurrentThread(&env,NULL);
    int server_socket=NewTcpSocket(env,gThiz);
   BindSocket(env,gThiz,server_socket,gServerIP,gServerPort);
    SocketInfo *serverSocketInfo=GetServerSocketInfo(env,gThiz,server_socket);
   __android_log_print(ANDROID_LOG_VERBOSE,"hello","client ip=%s,client port=%d",serverSocketInfo->ip,serverSocketInfo->port);
   ListenOnSocket(env,gThiz,server_socket,100);


    int client_socket=AcceptClientSocket(env,gThiz,server_socket);
    SocketInfo *clientSocketInfo=GetClientSocketInfo(env,gThiz,client_socket);

    __android_log_print(ANDROID_LOG_VERBOSE,"hello","client ip=%s,client port=%d",clientSocketInfo->ip,clientSocketInfo->port);

    while(true){
        char buffer[1024];
        int receive_size=ReceiveFromSocket(env,gThiz,client_socket,buffer,1024);
        if(receive_size==0||env->ExceptionOccurred()!=NULL)break;
        if(receive_size>0){
            __android_log_print(ANDROID_LOG_VERBOSE,"hello","receive datafrom client is%s",buffer);

            int send_size=SendToSocket(env,gThiz,client_socket,buffer,receive_size);
            if(send_size==0||env->ExceptionOccurred()!=NULL)break;
            __android_log_print(ANDROID_LOG_VERBOSE,"hello","send data toclient is%s",buffer);
        }
    }

    CloseSocket(env,gThiz,server_socket);
    __android_log_print(ANDROID_LOG_VERBOSE,"hello","%s","serversocket close");

    gVM->DetachCurrentThread();

    return (void*)1;
}

TCP客户端

TCP客户端程序流程图

1.   创建客户端socket(socket())

2.   和服务器进行连接(connect())

3.   发送消息到服务器(send())

4.   接收服务器返回的消息(recv())

TCP客户端程序示例

JavaVM *gVM;
jobject gThiz;

jint JNI_OnLoad(JavaVM*vm, void *reserved){
    gVM=vm;
    return JNI_VERSION_1_4;
}

 

extern "C"
JNIEXPORT voidJNICALL
Java_com_kgdwbb_jnistudy_MainActivity_startClientSocket(JNIEnv* env,jobject thiz){
    if(gThiz==NULL){
        gThiz=env->NewGlobalRef(thiz);
    }
    pthread_t pthread;
    pthread_create(&pthread,NULL,clientThread,NULL);
}

 

void * clientThread(void* args){
    JNIEnv *env;
    gVM->AttachCurrentThread(&env,NULL);

    int client_socket=NewTcpSocket(env,gThiz);
   ConnectSocket(env,gThiz,client_socket,gServerIP,gServerPort);

    const char *msg="helloboy";
    int len=strlen(msg);
    int send_size=SendToSocket(env,gThiz,client_socket,msg,len);
    if(send_size==0||env->ExceptionOccurred()!=NULL){
       CloseSocket(env,gThiz,client_socket);
        return (void*)1;
    }
    __android_log_print(ANDROID_LOG_VERBOSE,"hello","send data toserver is%s",msg);

    char buffer2[1024];
    int receive_size=ReceiveFromSocket(env,gThiz,client_socket,buffer2,1024);
    if(receive_size==0||env->ExceptionOccurred()!=NULL){
       CloseSocket(env,gThiz,client_socket);
        return (void*)1;
    }
    __android_log_print(ANDROID_LOG_VERBOSE,"hello","receiveddata from server is%s",buffer2);

    CloseSocket(env,gThiz,client_socket);
    __android_log_print(ANDROID_LOG_VERBOSE,"hello","%s","clientsocket close");

    gVM->DetachCurrentThread();

    return (void*)1;
}

结束语

本篇详细介绍了TCP C/S通信相关的原生函数,以及TCP C/S通信的流程和详细的示例代码。重点是TCP C/S通信的流程,希望大家多多理解记忆。

还有别忘了,学完之后,亲自动手写一个简单的TCP C/S通信的小demo练习一下,你定会受益匪浅。

原创粉丝点击