最近在开发过程中遇到一个需求:Java 层和 C 层两个进程之间用Socket 通信。利用LocalServerSocket 类和 ServerSocket 类可以很容易实现Java 层和 C 层进程间通信。
1.Java层作为Server,利用Android LocalServerSocket类创建。LocalServerSocket 被Android 封装得过于简单,不能设置 timeout 和 reuse 端口。
/**
* Create the server socket by Android LocalServerSocket
*/
public void createSocket() {
new Thread() {
@Override
public void run() {
LocalSocket receiver = null;
try {
mSocketServer = new LocalServerSocket(SOCKET_NAME);
receiver = mSocketServer.accept();
if (receiver != null) {
handleSockectRequest(receiver);
}
} catch (Throwable t) {
e.printStackTrace();
} finally {
if (receiver != null) {
try {
receiver.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (mSocketServer != null) {
try {
mSocketServer.close();
} catch (IOException e) {
e.printStackTrace();
}
mSocketServer = null;
}
}
}
}.start();
}
/**
* Force to close the socket if it exist.
*/
public void closeSocket() {
if (mSocketServer != null) {
try {
mSocketServer.close();
} catch (IOException e) {
e.printStackTrace();
}
mSocketServer = null;
}
}
/**
* Start the handle the socket client request.
*/
private void handleSockectRequest(final LocalSocket receiver) {
InputStream input = null;
BufferedReader reader = null;
try {
input = receiver.getInputStream();
reader = new BufferedReader(new InputStreamReader(input));
String line = reader.readLine();
if (line != null && line.length() > 0) {
StringBuffer buffer = new StringBuffer();
buffer.append(line).append(END_SYMBOL);
String response = buffer.toString();
if (response != null && response.length() > 0) {
sendToSocket(receiver, response.getBytes());
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (input != null) {
try {
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* Send back data to the socket client.
*/
private void sendToSocket(final LocalSocket receiver, byte[] data) {
OutputStream output = null;
try {
output = receiver.getOutputStream();
output.write(data);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (output != null) {
try {
output.flush();
output.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
若Java层作为Client,具体代码如下:
public void createSocketCliet() {
LocalSocket so = new LocalSocket();
LocalSocketAddress addr = new LocalSocketAddress(SOCKET_NAME, LocalSocketAddress.Namespace.ABSTRACT);
try {
so.connect(addr);
} catch (IOException e) {
e.printStackTrace();
}
}
若C 层作为Client,具体代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#include <android/log.h>
#define SEND_BUFFER_LENGTH 0x43
#define RECEIVE_BUFFER_LENGTH 0x1000
#define MAX_INDEX (0x24-1)
#define MIN_INDEX 0
#define END_OF_DATA_SYMBOL "#" //customer define
#define SOCKET_NAME "my_socket_name"
#define LOG_TAG "myTag"
#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, fmt, ##args)
#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##args)
#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, fmt, ##args)
int main() {
//1.generate random request data
char rawDataTable[] = {"abcdefghijklmnopqrstuvwxyz0123456789"};
char requestData[SEND_BUFFER_LENGTH] = {0};
srand((unsigned) time(NULL));
int i = 0;
for (i = 0; i < SEND_BUFFER_LENGTH; i++) {
int index = rand() % (MAX_INDEX - MIN_INDEX) + MIN_INDEX;
requestData[i] = rawDataTable[index];
}
LOGD("cookie:%s", requestData);
//2. create socket
int s = socket(AF_UNIX, SOCK_STREAM, 0);
if (s < 0) {
LOGD("failed to create socket");
return -1;
}
//3. connect to local server
struct sockaddr_un server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sun_family = AF_UNIX;
server_addr.sun_path[0] = '\0'; /* abstract namespace */
strcpy(&server_addr.sun_path[1], SOCKET_NAME);
int server_addr_len = 1 + strlen(SOCKET_NAME) + offsetof(struct sockaddr_un, sun_path);
if (connect(s, (struct sockaddr *) &server_addr, server_addr_len) < 0) {
LOGD("failed to connect local server");
return -1;
}
//4. set send/receive timeout
struct timeval timeout = {10,0}; //timeout=10s
setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(struct timeval));
setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(struct timeval));
//5. send request data to socket server
if (send(s, requestData, strlen(requestData), 0) != strlen(requestData)) {
LOGD("send cookie to server failed");
close(s);
if (errno == EAGAIN) {
LOGD("send data timeout");
return -1;
}
return -1;
}
//6. read response data from socket server
char *recv_buffer = (char *)malloc(RECEIVE_BUFFER_LENGTH);
if (recv_buffer == NULL) {
LOGD("allocate recv_buffer failed");
close(s);
return -1;
} else {
memset(recv_buffer, 0, RECEIVE_BUFFER_LENGTH);
}
int recv_length = 0;
for (i = 0; i < RECEIVE_BUFFER_LENGTH; i++) {
char b = '\0';
int bytes = recv(s, &b, 1, MSG_WAITALL);
if (bytes < 1) {
LOGD("receive bytes from server failed");
close(s);
if (recv_buffer != NULL) {
free(recv_buffer);
}
if (errno == EAGAIN) {
LOGD("recv data timeout");
return -1;
} else {
return -1;
}
}
//Judge if reach the end of data
if (b == END_OF_DATA_SYMBOL) {
recv_length = i;
recv_buffer[i] = '\0';
break;
} else {
recv_buffer[i] = b;
}
}
if (recv_length == 0) {
LOGD("receive bytes from server failed");
}
if (recv_buffer != NULL) {
free(recv_buffer);
}
close(s);
}
2.Java层作为Server,利用Java 原生类 ServerSocket创建,可以设置setReuseAddress,setSoTimeout,更加灵活:
/**
* Create the server socket by Java ServerSocket
*/
public void createJavaServerSocket() {
new Thread() {
@Override
public void run() {
Socket receiver = null;
try {
mSocketServer = new ServerSocket();
mSocketServer.setReuseAddress(true);
mSocketServer.setSoTimeout(SOCKET_TIME_OUT);
mSocketServer.bind(new InetSocketAddress(SOCKET_BIND_PORT));
receiver = mSocketServer.accept();
if (receiver != null) {
handleSockectRequest(receiver);
}
} catch (Throwable t) {
if (ALILog.LOGENABLE) {
ALILog.e("createJavaServerSocket", "Error = " + t.toString());
}
} finally {
if (receiver != null) {
try {
receiver.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (mSocketServer != null) {
if (ALILog.LOGENABLE) {
ALILog.d("createJavaServerSocket", "close Socket... ");
}
try {
mSocketServer.close();
} catch (IOException e) {
if (ALILog.LOGENABLE) {
ALILog.e("createJavaServerSocket", "close Socket error = " + e.toString());
}
}
mSocketServer = null;
}
}
}
}.start();
}
C 层作为Client
,具体代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/socket.h>
#include <sys/endian.h>
#include <linux/in.h>
#include <errno.h>
#include <arpa/inet.h>
#include "Alog.h"
#include "rsa.h"
#include "base64.h"
#define END_OF_DATA_SYMBOL '#'
#define MAX_RAW_DATA_LENGTH 0x40
#define MAX_RAW_DATA_BUFFER_LENGTH 0x43
#define ENCRYPTED_DATA_LENGTH 0x80
#define MAX_ENCRYPTED_DATA_LENGTH 0x1000
#define MAX_INDEX (0x24-1)
#define MIN_INDEX 0
#define SOCKET_PORT 19521
static char DATA_TABLE[] = {"abcdefghijklmnopqrstuvwxyz0123456789"};
/**
* Send the raw data to local socket and get back the response data
*/
char* getDataFromLocalSocket(char* rawData) {
//1. create socket
int s = socket(AF_INET, SOCK_STREAM, 0);
if (s < 0) {
LOGE("failed to create socket");
return NULL;
}
//2. connect to socket server
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(SOCKET_PORT);
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
if(connect(s, (struct sockaddr *) &server_addr, sizeof(struct sockaddr)) < 0) {
LOGE("failed to connect local server");
close(s);
return NULL;
}
//3. set send/receive timeout
struct timeval timeout = {10,0}; //timeout=10s
setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(struct timeval));
setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(struct timeval));
//4. send raw data
if (send(s, rawData, strlen(rawData), 0) != strlen(rawData)) {
LOGE("send data to server failed");
if (errno == EAGAIN) {
LOGE("send data timeout");
}
close(s);
return NULL;
}
//5. allocate receive data buffer
char *recvBuffer = (char *)malloc(MAX_ENCRYPTED_DATA_LENGTH);
if (recvBuffer == NULL) {
LOGE("allocate recvBuffer failed");
close(s);
return NULL;
} else {
memset(recvBuffer, 0, MAX_ENCRYPTED_DATA_LENGTH);
}
//6. receive encrypt data
int recvLength = 0;
int i = 0;
for (i = 0; i < MAX_ENCRYPTED_DATA_LENGTH; i++) {
char b = '\0';
int bytes = recv(s, &b, 1, MSG_WAITALL);
if (bytes < 1) {
LOGE("receive bytes from server failed");
if (errno == EAGAIN) {
LOGE("receive data timeout");
}
free(recvBuffer);
close(s);
return NULL;
}
if (b == END_OF_DATA_SYMBOL) {
recvLength = i;
recvBuffer[i] = '\0';
break;
} else {
recvBuffer[i] = b;
}
}
//7. close socket and return
close(s);
if (recvLength == 0) {
LOGE("receive data failed: recvLength == 0");
free(recvBuffer);
return NULL;
} else {
return recvBuffer;
}
}
int main() {
//1. generate random data
char rawData[MAX_RAW_DATA_BUFFER_LENGTH] = {0};
srand((unsigned) time(NULL));
int i = 0;
for (i = 0; i < MAX_RAW_DATA_LENGTH; i++) {
int index = rand() % (MAX_INDEX - MIN_INDEX) + MIN_INDEX;
rawData[i] = DATA_TABLE[index];
}
rawData[MAX_RAW_DATA_BUFFER_LENGTH-3] = '\r';
rawData[MAX_RAW_DATA_BUFFER_LENGTH-2] = '\n';
rawData[MAX_RAW_DATA_BUFFER_LENGTH-1] = '\0';
LOGD("rawData: %s", rawData);
//2. get encrypt data from server
char* encyptData = getDataFromLocalSocket(rawData);
if (encyptData != NULL) {
LOGD("receive encrypt data: %s", encyptData);
free(encyptData);
} else {
LOGE("Error: receive NULL data from server");
}
}
3. C层实现TCP 协议
Server 端代码tcp_server.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
int server_sockfd;//服务器端套接字
int client_sockfd;//客户端套接字
int len;
struct sockaddr_in my_addr; //服务器网络地址结构体
struct sockaddr_in remote_addr; //客户端网络地址结构体
int sin_size;
char buf[BUFSIZ]; //数据传送的缓冲区
memset(&my_addr,0,sizeof(my_addr)); //数据初始化--清零
my_addr.sin_family=AF_INET; //设置为IP通信
my_addr.sin_addr.s_addr=INADDR_ANY;//服务器IP地址--允许连接到所有本地地址上
my_addr.sin_port=htons(8000); //服务器端口号
/*创建服务器端套接字--IPv4协议,面向连接通信,TCP协议*/
if((server_sockfd=socket(PF_INET,SOCK_STREAM,0))<0)
{
perror("socket");
return 1;
}
/*将套接字绑定到服务器的网络地址上*/
if (bind(server_sockfd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr))<0)
{
perror("bind");
return 1;
}
/*监听连接请求--监听队列长度为5*/
listen(server_sockfd,5);
sin_size=sizeof(struct sockaddr_in);
/*等待客户端连接请求到达*/
if((client_sockfd=accept(server_sockfd,(struct sockaddr *)&remote_addr,&sin_size))<0)
{
perror("accept");
return 1;
}
printf("accept client %s/n",inet_ntoa(remote_addr.sin_addr));
len=send(client_sockfd,"Welcome to my server/n",21,0);//发送欢迎信息
/*接收客户端的数据并将其发送给客户端--recv返回接收到的字节数,send返回发送的字节数*/
while((len=recv(client_sockfd,buf,BUFSIZ,0))>0))
{
buf[len]='/0';
printf("%s/n",buf);
if(send(client_sockfd,buf,len,0)<0)
{
perror("write");
return 1;
}
}
close(client_sockfd);
close(server_sockfd);
return 0;
}
Client 端代码tcp_client.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
int client_sockfd;
int len;
struct sockaddr_in remote_addr; //服务器端网络地址结构体
char buf[BUFSIZ]; //数据传送的缓冲区
memset(&remote_addr,0,sizeof(remote_addr)); //数据初始化--清零
remote_addr.sin_family=AF_INET; //设置为IP通信
remote_addr.sin_addr.s_addr=inet_addr("127.0.0.1");//服务器IP地址
remote_addr.sin_port=htons(8000); //服务器端口号
/*创建客户端套接字--IPv4协议,面向连接通信,TCP协议*/
if((client_sockfd=socket(PF_INET,SOCK_STREAM,0))<0)
{
perror("socket");
return 1;
}
/*将套接字绑定到服务器的网络地址上*/
if(connect(client_sockfd,(struct sockaddr *)&remote_addr,sizeof(struct sockaddr))<0)
{
perror("connect");
return 1;
}
printf("connected to server/n");
len=recv(client_sockfd,buf,BUFSIZ,0);//接收服务器端信息
buf[len]='/0';
printf("%s",buf); //打印服务器端信息
/*循环的发送接收信息并打印接收信息--recv返回接收到的字节数,send返回发送的字节数*/
while(1)
{
printf("Enter string to send:");
scanf("%s",buf);
if(!strcmp(buf,"quit")
break;
len=send(client_sockfd,buf,strlen(buf),0);
len=recv(client_sockfd,buf,BUFSIZ,0);
buf[len]='/0';
printf("received:%s/n",buf);
}
close(client_sockfd);//关闭套接字
return 0;
}
4. C层实现UDP 协议
Server 端代码udp_server.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
int server_sockfd;
int len;
struct sockaddr_in my_addr; //服务器网络地址结构体
struct sockaddr_in remote_addr; //客户端网络地址结构体
int sin_size;
char buf[BUFSIZ]; //数据传送的缓冲区
memset(&my_addr,0,sizeof(my_addr)); //数据初始化--清零
my_addr.sin_family=AF_INET; //设置为IP通信
my_addr.sin_addr.s_addr=INADDR_ANY;//服务器IP地址--允许连接到所有本地地址上
my_addr.sin_port=htons(8000); //服务器端口号
/*创建服务器端套接字--IPv4协议,面向无连接通信,UDP协议*/
if((server_sockfd=socket(PF_INET,SOCK_DGRAM,0))<0)
{
perror("socket");
return 1;
}
/*将套接字绑定到服务器的网络地址上*/
if (bind(server_sockfd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr))<0)
{
perror("bind");
return 1;
}
sin_size=sizeof(struct sockaddr_in);
printf("waiting for a packet.../n");
/*接收客户端的数据并将其发送给客户端--recvfrom是无连接的*/
if((len=recvfrom(server_sockfd,buf,BUFSIZ,0,(struct sockaddr *)&remote_addr,&sin_size))<0)
{
perror("recvfrom");
return 1;
}
printf("received packet from %s:/n",inet_ntoa(remote_addr.sin_addr));
buf[len]='/0';
printf("contents: %s/n",buf);
close(server_sockfd);
return 0;
}
Client 端代码udp_client.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
int client_sockfd;
int len;
struct sockaddr_in remote_addr; //服务器端网络地址结构体
int sin_size;
char buf[BUFSIZ]; //数据传送的缓冲区
memset(&remote_addr,0,sizeof(remote_addr)); //数据初始化--清零
remote_addr.sin_family=AF_INET; //设置为IP通信
remote_addr.sin_addr.s_addr=inet_addr("127.0.0.1");//服务器IP地址
remote_addr.sin_port=htons(8000); //服务器端口号
/*创建客户端套接字--IPv4协议,面向无连接通信,UDP协议*/
if((client_sockfd=socket(PF_INET,SOCK_DGRAM,0))<0)
{
perror("socket");
return 1;
}
strcpy(buf,"This is a test message");
printf("sending: '%s'/n",buf);
sin_size=sizeof(struct sockaddr_in);
/*向服务器发送数据包*/
if((len=sendto(client_sockfd,buf,strlen(buf),0,(struct sockaddr *)&remote_addr,sizeof(struct sockaddr)))<0)
{
perror("recvfrom");
return 1;
}
close(client_sockfd);
return 0;
}