本篇首先和大家讲解一下NDK原生TCP网络通信的函数,之后再和大家详细讲解一下TCP客户端和服务端网络通信的流程并提供对应的示例代码。
#include
#include
#include
#include
#include
#include
#include
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; }
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; }
int listen(int socketDescriptor, int backlog);
参数解析:
socketDescriptor:指服务端socket
backlog:指服务端可以接受的最大的连接数,如果连接请求超过这个最大数,超过的请求就会排队。
如果函数调用成功返回0,否则返回-1
示例代码:
int ListenOnSocket(JNIEnv *env,jobject obj,int sd,int backlog){
int result=listen(sd,backlog);
return result;
}
int accept(int socketDescriptor, struct sockaddr* address, socklen_t* addressLength);
参数解析:
socketDescriptor:指服务端socket
address:指sockaddr结构体,这个结构体将被填入客户端的信息
addressLength:address结构体的大小
如果函数调用成功返回0,否则返回-1
示例代码:
int AcceptClientSocket(JNIEnv *env,jobject obj,int sd){
sockaddr_in address;
socklen_t len= sizeof(address);
int client_socket=accept(sd,(sockaddr*)&address,&len);
return client_socket;
}
ssize_t recv(int socketDescriptor, void* buffer, size_t bufferLength, int flags);
参数解析:
socketDescriptor:指客户端socket
buffer:指接收的数据缓冲区指针
bufferLength:指接收的数据缓冲区的大小
flags:指接收需要的额外标志,默认传0
如果函数调用成功返回0,否则返回-1
示例代码:
int ReceiveFromSocket(JNIEnv *env,jobject obj,int sd,char *buffer,int bufferLen){
int receive_size=recv(sd,buffer,bufferLen-1,0);
return receive_size;
}
ssize_t send(int socketDescriptor, const void* buffer, size_t bufferLength, int flags);
参数解析:
socketDescriptor:指客户端socket
buffer:指接收的数据缓冲区指针
bufferLength:指接收的数据缓冲区的大小
flags:指接收需要的额外标志,默认传0
如果函数调用成功返回0,否则返回-1
示例代码:
int SendToSocket(JNIEnv *env,jobject obj,int sd,const char *buffer,int bufferLen){
int send_size=send(sd,buffer,bufferLen,0);
return send_size;
}
int getpeername(int socketDescriptor, 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,int sd){
sockaddr_in address;
socklen_t addressLength= sizeof(address);
getpeername(sd,(sockaddr *)&address,&addressLength);
SocketInfo *socketInfo=new SocketInfo;
socketInfo->port=ntohs(address.sin_port);
socketInfo->ip=inet_ntoa(address.sin_addr);
return socketInfo;
}
int getsockname(int socketDescriptor, 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,int sd){
sockaddr_in address;
socklen_t addressLength= sizeof(address);
getsockname(sd,(sockaddr *)&address,&addressLength);
SocketInfo *socketInfo=new SocketInfo;
socketInfo->port=ntohs(address.sin_port);
socketInfo->ip=inet_ntoa(address.sin_addr);
return socketInfo;
}
int connect(int socketDescriptor, 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,int sd,const char *ip,int port){
sockaddr_in address=getSockaddr_in(ip,port);
int result=connect(sd,(sockaddr *)&address, sizeof(address));
return result;
}
1. 创建TCP socket(socket())
2. 绑定TCP socket(bind())
3. 监听TCP连接(listen())
4. 接受TCP连接(accept())
5. 接收TCP消息(recv())
6. 发送TCP消息(send())
JavaVM *gVM;
jobject gThiz;
jint JNI_OnLoad(JavaVM *vm, void *reserved){
gVM=vm;
return JNI_VERSION_1_4;
}
extern "C"
JNIEXPORT void JNICALL
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;
}
1. 创建客户端socket(socket())
2. 和服务器进行连接(connect())
3. 发送消息到服务器(send())
4. 接收服务器返回的消息(recv())
JavaVM *gVM;
jobject gThiz;
jint JNI_OnLoad(JavaVM *vm, void *reserved){
gVM=vm;
return JNI_VERSION_1_4;
}
extern "C"
JNIEXPORT void JNICALL
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练习一下,你定会受益匪浅。