前篇
首先我們需要了解一下socket的通信原理,大家百度就可以找到這張圖片
- java實現(xiàn)socket通信
首先服務(wù)器端
ServerSocket server = new ServerSocket(8081);
try {
Socket client = server.accept();
try {
BufferedReader input =
new BufferedReader(new InputStreamReader(client.getInputStream()));
boolean flag = true;
int count = 1;
while (flag) {
System.out.println(連接的第 + count + 次!);
count++;
}
} finally {
client.close();
}
} finally {
server.close();
}
}
然后客戶端
Socket client = new Socket(127.0.0.1, 8081);
幾個重要方法
- 1._socket函數(shù):對應(yīng)于普通文件的打開操作
int socket(int __af, int __type, int __protocol);
1.__af:即協(xié)議域缩滨,又稱為協(xié)議族(family)兼搏。常用的協(xié)議族有芹助,AF_INET励翼、AF_INET6缚去、AF_LOCAL(或稱AF_UNIX路克,Unix域socket),AF_ROUTE等
2.__type:指定socket類型樟结。常用的socket類型有,SOCK_STREAM精算、SOCK_DGRAM瓢宦、SOCK_RAW、SOCK_PACKET灰羽、SOCK_SEQPACKET等等
3. __protocol:protocol:故名思意驮履,就是指定協(xié)議。常用的協(xié)議有廉嚼,IPPROTO_TCP玫镐、IPPTOTO_UDP、IPPROTO_SCTP怠噪、IPPROTO_TIPC等恐似,它們分別對應(yīng)TCP傳輸協(xié)議、UDP傳輸協(xié)議傍念、STCP傳輸協(xié)議矫夷、TIPC傳輸協(xié)議
- 2.bind函數(shù)
_socketcall int bind(int __fd, const struct sockaddr* __addr, socklen_t __addr_length);
__fd:上面第一步創(chuàng)建得到的返回值
__addr:指向要綁定給__fd的協(xié)議地址
__addr_lenght:對應(yīng)的是地址的長度
3.listen()、connect()函數(shù)
listen函數(shù)將socket變?yōu)楸粍宇愋偷谋锘保却蛻舻倪B接請求
客戶端通過調(diào)用connect函數(shù)來建立與TCP服務(wù)器的連接4.accept()函數(shù)
監(jiān)聽客戶端發(fā)送的請求双藕,其方法會返回一個返回值代表全新的socket描述詞,就是第一步返回值fd一個意思
__socketcall int accept(int __fd, struct sockaddr* __addr, socklen_t* __addr_length);
__addr:代表返回客戶端的協(xié)議地址
__addr_length參數(shù):為協(xié)議地址的長度
- 5.read函數(shù)
ssize_t read(int __fd, void* __buf, size_t __count) __overloadable
__RENAME_CLANG(read);
__fd:accept返回成功后的返回值socket描述詞
- 6.execlp用一個新的進程映像替換當前進程映像
直接貼代碼
#include <sys/select.h>
#include <unistd.h>
#include <sys/socket.h>
#include <pthread.h>
#include <signal.h>
#include <sys/wait.h>
#include <android/log.h>
#include <sys/types.h>
#include <sys/un.h>
#include <error.h>
#include <stdlib.h>
#include <linux/signal.h>
#include <unistd.h>
void child_do_work();
int child_create_channel();
void child_listen_msg();
//com.peakmain.mall.ndk 自己的包名 peakmain.sock自己起個名字秦陋,阿貓阿狗都可以
const char *PATH = "/data/data/com.peakmain.mall.ndk/peakmain.sock";
int childfd;
const char *userId;
/**
* 服務(wù)端讀取信息
* 客戶端
*/
int child_create_channel() {
//int __af 協(xié)議域蔓彩,又稱協(xié)議族 常用的有 AF_INET 和 AF_INET6
//int __type 為數(shù)據(jù)傳輸方式/套接字類型,常用的有 SOCK_STREAM(流格式套接字/面向連接的套接字) 和 SOCK_DGRAM(數(shù)據(jù)報套接字/無連接的套接字)
// int __protocol 常用的有 IPPROTO_TCP 和 IPPTOTO_UDP驳概,分別表示 TCP 傳輸協(xié)議和 UDP 傳輸協(xié)議
int fd = socket(AF_LOCAL, SOCK_STREAM, 0);
struct sockaddr_un addr;
unlink(PATH);
//清空內(nèi)存
memset(&addr, 0, sizeof(sockaddr_un));
addr.sun_family = AF_LOCAL;
strcpy(addr.sun_path, PATH);
int connfd;
if (bind(fd, (const sockaddr *) &addr, sizeof(sockaddr_un)) < 0) {
LOGE("綁定錯誤");
return 0;
}
LOGE("綁定成功");
//最多同時連接5個
listen(fd, 5);
//while 保證宿主連接成功
while (1) {
//返回值 客戶端的地址 //阻塞函數(shù)
if ((connfd = accept(fd, NULL, NULL)) < 0) {
if (errno == EINTR) {
continue;
} else {
LOGE("讀取錯誤");
return 0;
}
}
childfd = connfd;
LOGE("apk 父進程連接上 %d", childfd);
break;
}
return 1;
}
/**
* 創(chuàng)建服務(wù)端socket
* 讀取
*/
void child_listen_msg() {
fd_set rfds;
//時間 3秒
struct timeval timeout = {3, 0};
while (1) {
//清空內(nèi)容
FD_ZERO(&rfds);
//重置
FD_SET(childfd, &rfds);
//選擇范圍 一般+1
int range = select(childfd + 1, &rfds, NULL, NULL, &timeout);
if (range > 0) {
char pkg[256] = {0};
//保證讀到的信息是 指定apk客戶端
if (FD_ISSET(childfd, &rfds)) {
int result = read(childfd, pkg, sizeof(pkg));
//開啟服務(wù)
//com.peakmain.mall.ndk.ProcessService 自己的服務(wù)全名
execlp("am", "am", "startservice", "--user", userId,
"com.peakmain.mall.ndk.ProcessService", NULL);
break;
}
}
}
}
void child_do_work() {
//創(chuàng)建和開啟socket 服務(wù)端
if (child_create_channel()) {
child_listen_msg();
}
}
extern "C"
JNIEXPORT void JNICALL
Java_com_peakmain_mall_ndk_Watcher_createWathcher(JNIEnv *env, jobject instance, jstring userId_) {
userId = env->GetStringUTFChars(userId_, 0);
//開雙進程
pid_t pid = fork();
LOGE("pid是%d",pid);
if (pid < 0) {
//失敗
} else if (pid == 0) {
//子進程 守護進程
child_do_work();
} else if (pid > 0) {
//父進程不做處理
}
env->ReleaseStringUTFChars(userId_, userId);
}
//客戶端
extern "C"
JNIEXPORT void JNICALL
Java_com_peakmain_mall_ndk_Watcher_connectMonitor(JNIEnv *env, jobject instance) {
int socked;
while (1) {
socked = socket(AF_LOCAL, SOCK_STREAM, 0);
if (socked < 0) {
LOGE("客戶端連接失敗0");
return;
}
struct sockaddr_un addr;
//清空內(nèi)存
memset(&addr, 0, sizeof(sockaddr));
addr.sun_family = AF_LOCAL;
strcpy(addr.sun_path, PATH);
if (connect(socked, (sockaddr *) &addr, sizeof(sockaddr_un)) < 0) {
LOGE("客戶端連接失敗1");
close(socked);
sleep(1);
//在來下一次嘗試連接直到成功為止
continue;
}
LOGE("客戶端連接成功");
break;
}
}
使用service的oncreate方法
Watcher watcher=new Watcher();
watcher.createWathcher(String.valueOf(Process.myUid()));
watcher.connectMonitor();
Timer timer=new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
Log.d("TAG","服務(wù)開啟"+i);
i++;
}
},0,3*1000);
大家再做的時候去結(jié)合socket圖去做赤嚼,然后根據(jù)圖可以找到相應(yīng)的方法,整體還是不難的顺又。圖片中Recv其實可以理解為Read,Send理解為write