赞
踩
android程序开发中,Java层与JNI层使用socket进行通信:
java层提供服务
- @Override
- public void run() {
- //.............省略一万行
- while(true){
- //.............省略一万行
- try {
- //等待客户端请求
- logi(TAG, "run: Ready to accept client socket output...");
- OutputStream stream = acceptClientSocketOutput();
- if (stream == null) {
- logw(TAG, "run: continue for output is null.");
- continue;
- }
- //创建返回写入数据,可能消耗时间比较长
- byte[] data = createWriteData();
- //执行代理写入数据
- boolean res = write(stream, data);
- if (!res) {
- logw(TAG, "doProxy: Failed to write port data:" + data);
- //睡眠1秒,避免快速执行循环
- doThreadSleep(1000); // 潜在问题隐患
- } else {
- logw(TAG, "doProxy: Written port: " + portStr);
- }
- } catch (Throwable e) {
- logi(TAG, "run: proxy failed: " + e);
- } finally {
- closeClientSocket();
- }
- }
- //.............省略一万行
- }
-
- /**
- * 向文件输出流写入数据
- * <p>默认值写入端口号,子类可以复写此函数写入其他数据
- * @param stream
- * @param data 数据
- * @throws IOException
- */
- protected boolean write(OutputStream stream, byte[] data) {
- if (stream == null) {
- logw(TAG, "write: stream is null");
- return false;
- }
- if (data == null) {
- logw(TAG, "write: data is null");
- return false;
- }
- try {
- logi(TAG, "write data: " + Arrays.toString(data));
- stream.write(data);
- stream.flush();
- return true;
- } catch (IOException e) {
- logw(TAG, "write failed: " + e);
- return false;
- }
- }
JNI的C语言层为client端:
- //client获取代理数据
- int get_proxy_data(void){
- int sockfd;
- struct sockaddr_un servaddr;
- socklen_t servaddr_len=sizeof(struct sockaddr_un);
- char *path = "com.hulk.sockettest";
- sockfd = socket(AF_LOCAL, SOCK_STREAM, 0);
- socket_make_sockaddr_un(path, &servaddr, &servaddr_len);
- logi("get_proxy_data:connect:sockfd:%d\n",sockfd);
- int f = make_async_connect(sockfd, (struct sockaddr *) &servaddr, servaddr_len);
-
- if(f==-1){
- logi("get_proxy_data: failed connect f:%d, error:%s\n", f, strerror(errno));
- close(sockfd);
- return -1;
- }
-
- char buf[4096];
- memset(buf, 0x00, sizeof(buf));
-
- socket_set_timeout(sockfd, 1);
-
- int r=0;
- int count=0;
- again:
- logi("get_proxy_data:read:sockfd:%d\n",sockfd);
- r = read(sockfd, buf, sizeof(buf));
- if(errno==EAGAIN && r<=0){
- //读取数据失败:睡眠10毫秒,重试2次
- usleep(10*1000);
- count++;
- logi("get_proxy_data:read:sockfd:%d, try_again_count:%d\n",sockfd, count);
- if(count==3){
- logi("get_proxy_data: Failed to read proxy port, close sockfd:%d\n", sockfd);
- close(sockfd);
- return -1;
- }
- goto again;
- }
- logi("get_proxy_data: close sockfd:%d\n", sockfd);
- close(sockfd);
-
- if(r<=0){
- return -1;
- }
-
- int res = atoi(buf);
- return res;
- }
android跨进程使用socket通信过程中某些设备出现: java.io.IOException: Broken pipe, 只在少数设备上出现该问题,一旦出现就必须杀掉进程才能恢复。
socket通信的错误异常:
05-13 10:17:26.348 31837 32158 I ProxyThread:write data: [48, 0, 49 ...... 59, 0]
05-13 10:17:26.349 31837 32158 W ProxyThread:write failed: java.io.IOException: Broken pipe
按照常理: Java层出现 java.io.IOException: Broken pipe, 直接原因就是对方client端的socket已经关闭(close),Server端不知道被关闭了,还在继续往已经被关闭socket fd的output中写数据;
socket常识:
对方socket已经close后,存在一下两种场景: 读 和 写
此时第一次进行读/写会返回"RST"信号,抛出异常:java.net.SocketException: (Connection reset或者 Connect reset by peer:Socket write error)
再一次write:再次写入就抛出“ java.io.IOException: Broken pipe”。具体可以参考如下描述
Connection reset by peer的常见原因及解决办法 - 云+社区 - 腾讯云
知道了Broken pipe的原因,在对上面的代码进行逻辑分析,找出“ java.io.IOException: Broken pipe”的原因
1. Client端(C层)的read数据失偶尔败是可预料的,所以写了重试机制,最多读3次,每次睡眠时间为10毫秒。 Java层代理时间最长不能超过30毫秒,否则,C层就关闭了socket fd,此时Java层好网里面写入数据,就会出现“Broken pipe”;
2. Server端(Java层)创建写入数据的时间可能比较长(超过30毫秒),且在一次失败后睡眠了1秒钟,彩灯带下一个循环的对方socket请求,此时对方C层的请求socket早就被close,继续写入数据一定是“Broken pipe”,因为两边的读和写不同步,始终错位1秒钟,导致代理一直失败,杀掉进程才能恢复。
综上所述,原因定位在两方面:
1. 创建写入数据的时间较长,必须使用缓存机制,一定要确保每次创建数据的水煎在10毫秒以内,最好是8毫秒以内。这个问题可以使用缓存解决,或者使用异步线程去完成,避免排队堵塞。
2。 去掉每次代理异常之后的睡眠代码,不进行睡眠,马上进入下一次循环,及时响应Client端的请求,更快写入数据,确保中间不要断档。
代码修改如下
- @Override
- public void run() {
- //.............省略一万行
- while(true){
- //.............省略一万行
- try {
- //等待客户端请求
- logi(TAG, "run: Ready to accept client socket output...");
- OutputStream stream = acceptClientSocketOutput();
- if (stream == null) {
- logw(TAG, "run: continue for output is null.");
- continue;
- }
- //创建数据:createWriteData()中优先使用缓存数据,确保时间不能超过8毫秒
- byte[] data = createWriteData();
- //执行代理写入数据
- boolean res = write(stream, data);
- if (!res) {
- logw(TAG, "doProxy: Failed to write port data:" + data);
- } else {
- logw(TAG, "doProxy: Written port: " + portStr);
- }
- } catch (Throwable e) {
- logi(TAG, "run: proxy failed: " + e);
- } finally {
- closeClientSocket();
- }
- }
- //.............省略一万行
- }
-
- /**
- * 向文件输出流写入数据
- * <p>默认值写入端口号,子类可以复写此函数写入其他数据
- * @param stream
- * @param data 数据
- * @throws IOException
- */
- protected boolean write(OutputStream stream, byte[] data) {
- if (stream == null) {
- logw(TAG, "write: stream is null");
- return false;
- }
- if (data == null) {
- logw(TAG, "write: data is null");
- return false;
- }
- try {
- logi(TAG, "write data: " + Arrays.toString(data));
- stream.write(data);
- stream.flush();
- return true;
- } catch (IOException e) {
- logw(TAG, "write failed: " + e);
- return false;
- }
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。