Android環境下通過SOCKET傳遞Parcel包並解出資料的例子
原先認為分開的過程都做過了,合併起來應該不是什麼問題。但昨天一下午卻未能成功,一直到今天上午才基本上弄了出來。現將程式的原始碼放在下面:
程式裡面的一些無用的除錯資訊沒有去掉,都已經註釋掉了,不影響編譯執行。如果覺得影響閱讀,可先複製下來,再將其中的除錯資訊刪除即可。
/****************** server program (server.cpp)*****************/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <stdlib.h>
#include <sys/un.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <utils/Parcel.h>
using namespace android;
Parcel p;
void write_Parcel();
int main()
{
int sockfd,newfd,ret,send_num,send_num_total=0;
char buf[200];
struct sockaddr_in server_addr;
// remove("/data/server.socket");/*不管有沒有,先刪除一下,否則如果該檔案已經存在的的話,bind會失敗。*/
// memset(&server_addr,0,sizeof(server_addr));
server_addr.sin_family=AF_INET;
// strcpy(server_addr.sin_path,"/data/server.socket");
server_addr.sin_addr.s_addr=INADDR_ANY;
server_addr.sin_port=htons(5678);
sockfd=socket(AF_INET,SOCK_STREAM,0);
printf("this is the socket_server *TCP* mode.\n");
if (sockfd<0)
{
printf("呼叫socket函式建立socket描述符出錯!\n");
exit(1);
}
printf("@呼叫socket函式建立socket描述符成功!\n");
ret=bind(sockfd,(struct sockaddr *)(&server_addr),sizeof(server_addr));
if (ret<0)
{
printf("呼叫bind函式繫結套接字與地址出錯!\n");
exit(2);
}
printf("呼叫bind函式繫結套接字與地址成功!\n");
ret=listen(sockfd,4);
if (ret<0)
{
printf("呼叫listen函數出錯,無法宣告伺服器已經可以接受連線!\n");
exit(3);
}
printf("呼叫listen函式成功,宣告伺服器已經可以接受連線請求!\n");
newfd=accept(sockfd,NULL,NULL);/*newfd連線到呼叫connect的客戶端*/
if (newfd<0)
{
printf("呼叫accept函數出錯,無法接受連線請求,建立連線失敗!\n");
exit(4);
}
printf("呼叫accept函式成功,伺服器與客戶端建立連線成功!\n");
write_Parcel();
while (1)
{
send_num=send(newfd,p.data(), p.dataSize(),MSG_DONTWAIT);
if (send_num<0)
printf("呼叫send函式失敗!");
else
{
send_num_total+=send_num;
printf("呼叫send函式成功,本次傳送%d個位元組。目前共傳送了%d個位元組的資料。\n",send_num,send_num_total);
}
sleep(2);
}
}
/****************** 向Parcel內寫入資料 ********************/
void write_Parcel()
{
status_t status;
size_t parcel_position;
int intp=987654;
char charp='g';
float floatp=3.14159;
double doublep=12345.6789012;
long longp=1234567890;
char *stringp="this is my parcel test! this is my parcel test! this is my parcel test! this is my parcel test!";
// printf("1:Parcel包的大小是%d位元組,Parcel結構體的大小是%d位元組\n",sizeof(p),sizeof(Parcel));
// readParcel(&p);
parcel_position = p.dataPosition();/*備份位置*/
// printf("當前Parcel的讀寫位置是:%d\n",parcel_position);
/*************寫int型別***************/
status=p.writeInt32(intp);
if (status==NO_ERROR)
printf("write int type success!\n");
else
printf("write int type fail,the errno=%d\n",errno);
/*************寫char型別***************/
status=p.writeInt32(charp);
if (status==NO_ERROR)
printf("write char type success!\n");
else
printf("write char type fail,the errno=%d\n",errno);
/*************寫Float型別***************/
status=p.writeFloat(floatp);
if (status==NO_ERROR)
printf("write Float type success!\n");
else
printf("write Float type fail,the errno=%d\n",errno);
/*************寫Double型別***************/
status=p.writeDouble(doublep);
if (status==NO_ERROR)
printf("write Double type success!\n");
else
printf("write Double type fail,the errno=%d\n",errno);
/*************寫long型別***************/
status=p.writeInt64(longp);
if (status==NO_ERROR)
printf("write long type success!\n");
else
printf("write long type fail,the errno=%d\n",errno);
/*************寫String型別***************/
status=p.writeCString(stringp);
if (status==NO_ERROR)
printf("write String type success!\n");
else
printf("write String type fail,the errno=%d\n",errno);
// printf("2:Parcel包的大小是%d位元組,Parcel結構體的大小是%d位元組\n",sizeof(p),sizeof(Parcel));
// readParcel(&p);
/*************將parcel讀寫位置置回原位***************/
p.setDataPosition(parcel_position);
printf("3:Parcel包的大小是%d位元組,Parcel結構體的大小是%d位元組\n",sizeof(p),sizeof(Parcel));
// readParcel(&p);
/*************讀出變數***************/
printf("讀出的int型別變數為:%d\n",p.readInt32());
printf("讀出的char型別變數為:%c\n",(char)p.readInt32());
printf("讀出的Float型別變數為:%f\n",(float)p.readFloat());
printf("讀出的Double型別變數為:%f\n",(double)p.readDouble());
printf("讀出的long型別變數為:%ld\n",(long)p.readInt64());
printf("讀出的字串為:%s\n",p.readCString());
printf("4:Parcel包的大小是%d位元組,Parcel結構體的大小是%d位元組\n",sizeof(p),sizeof(Parcel));
// readParcel(&p);
}
/****************** client program (client.cpp)*****************/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <stdlib.h>
#include <sys/un.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <utils/Parcel.h>
using namespace android;
Parcel p;
int main()
{
int sockfd,ret,recv_num,recv_num_total=0;
char buf[124];
struct sockaddr_in server_addr;
// memset(&server_addr,0,sizeof(server_addr));
server_addr.sin_family=AF_INET;
// strcpy(server_addr.sin_path,"/data/server.socket");
server_addr.sin_addr.s_addr=INADDR_ANY;
server_addr.sin_port=htons(5678);
sockfd=socket(AF_INET,SOCK_STREAM,0);
printf("this is the socket_client *TCP* mode.\n");
if (sockfd<0)
{
printf("呼叫socket函式建立socket描述符出錯!\n");
exit(1);
}
printf("呼叫socket函式建立socket描述符成功!\n");
ret=connect(sockfd,(struct sockaddr *)(&server_addr),sizeof(server_addr));
if (ret<0)
{
printf("呼叫connect函式失敗,客戶端連線伺服器失敗!\n ");
exit(2);
}
printf("呼叫connect函式成功,客戶端連線伺服器成功!\n");
while (1)
{
recv_num=recv(sockfd,buf,sizeof(buf),0);
p.setData((unsigned char *) buf, sizeof(buf));
if (recv_num<0)
printf("呼叫recv接收失敗!\n");
else if(recv_num>0)
{
recv_num_total+=recv_num;
printf("呼叫recv函式成功,本次接收到%d個位元組。共收到%d個位元組的資料。\n",recv_num,recv_num_total);
printf("伺服器端:呼叫recv接收成功!本次接收到%d個位元組,共收到%d個位元組的資料。\n",recv_num,recv_num_total);
printf("讀出的int型別變數為:%d\n",p.readInt32());
printf("讀出的char型別變數為:%c\n",(char)p.readInt32());
printf("讀出的Float型別變數為:%f\n",(float)p.readFloat());
printf("讀出的Double型別變數為:%f\n",(double)p.readDouble());
printf("讀出的long型別變數為:%ld\n",(long)p.readInt64());
printf("讀出的字串為:%s\n",p.readCString());
}
// else/*收到資料為0,表明伺服器與客戶端的連線已經中斷*/
// {
// printf("與客戶端的連線已中斷,當前共收到%d個位元組的資料。伺服器將再次等待客戶端的連線。\n",recv_num_total);
// newfd=accept(sockfd,NULL,NULL);/*當客戶端退出後,再次開始接收客戶端的連線*/
// }
sleep(2);
}
}
/****************** Android Makefile檔案 (Android.mk)*****************/
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
server.cpp
LOCAL_SHARED_LIBRARIES := \
libutils \
libcutils
LOCAL_PRELINK_MODULE := false
LOCAL_MODULE:= socket_server_s
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
client.cpp
LOCAL_SHARED_LIBRARIES := \
libutils \
libcutils
LOCAL_PRELINK_MODULE := false
LOCAL_MODULE:= socket_client_s
include $(BUILD_EXECUTABLE)
將這三個檔案放在一個資料夾下,我給這個資料夾起了個名字叫parcel_send,其實叫什麼都無所謂。然後把parcel_send放到android原始碼根目錄下的/development目錄下面。
用emulator命令啟動模擬器後,再另開啟一個linux終端,用cd命令進入android原始碼根目錄下,su取得root許可權後,先後執行“make socket_server_s”和“make socket_client_s”對程式進行編譯。
編譯通過後,將生成的可執行檔案送入android環境的/data目錄下:
adb push (android原始碼根目錄)/out/target/product/generic/system/bin/socket_server_s /data
adb push (android原始碼根目錄)/out/target/product/generic/system/bin/socket_client_s /data
分別再開兩個linux終端,用adb shell命令進入模擬器終端,cd /data,先後執行"./socket_server_s"和“./socket_client_s”,即可看到程式執行。
/////////////////////////////////////////////伺服器端程式執行結果/////////////////////////////////////////////
# ./socket_server_s
this is the socket_server *TCP* mode.
@呼叫socket函式建立socket描述符成功!
呼叫bind函式繫結套接字與地址成功!
呼叫listen函式成功,宣告伺服器已經可以接受連線請求!
呼叫accept函式成功,伺服器與客戶端建立連線成功!
write int type success!
write char type success!
write Float type success!
write Double type success!
write long type success!
write String type success!
3:Parcel包的大小是48位元組,Parcel結構體的大小是48位元組
讀出的int型別變數為:987654
讀出的char型別變數為:g
讀出的Float型別變數為:3.141590
讀出的Double型別變數為:12345.678901
讀出的long型別變數為:1234567890
讀出的字串為:this is my parcel test! this is my parcel test! this is my parcel test! this is my parcel test!
4:Parcel包的大小是48位元組,Parcel結構體的大小是48位元組
呼叫send函式成功,本次傳送124個位元組。目前共傳送了124個位元組的資料。
呼叫send函式成功,本次傳送124個位元組。目前共傳送了248個位元組的資料。
呼叫send函式成功,本次傳送124個位元組。目前共傳送了372個位元組的資料。
呼叫send函式成功,本次傳送124個位元組。目前共傳送了496個位元組的資料。
呼叫send函式成功,本次傳送124個位元組。目前共傳送了620個位元組的資料。
呼叫send函式成功,本次傳送124個位元組。目前共傳送了744個位元組的資料。
呼叫send函式成功,本次傳送124個位元組。目前共傳送了868個位元組的資料。
…………………………
/////////////////////////////////////////////客戶端程式執行結果/////////////////////////////////////////////
# ./socket_client_s
this is the socket_client *TCP* mode.
呼叫socket函式建立socket描述符成功!
呼叫connect函式成功,客戶端連線伺服器成功!
呼叫recv函式成功,本次接收到124個位元組。共收到124個位元組的資料。
伺服器端:呼叫recv接收成功!本次接收到124個位元組,共收到124個位元組的資料。
讀出的int型別變數為:987654
讀出的char型別變數為:g
讀出的Float型別變數為:3.141590
讀出的Double型別變數為:12345.678901
讀出的long型別變數為:1234567890
讀出的字串為:this is my parcel test! this is my parcel test! this is my parcel test! this is my parcel test!
呼叫recv函式成功,本次接收到124個位元組。共收到248個位元組的資料。
伺服器端:呼叫recv接收成功!本次接收到124個位元組,共收到248個位元組的資料。
讀出的int型別變數為:987654
讀出的char型別變數為:g
讀出的Float型別變數為:3.141590
讀出的Double型別變數為:12345.678901
讀出的long型別變數為:1234567890
讀出的字串為:this is my parcel test! this is my parcel test! this is my parcel test! this is my parcel test!
呼叫recv函式成功,本次接收到124個位元組。共收到372個位元組的資料。
伺服器端:呼叫recv接收成功!本次接收到124個位元組,共收到372個位元組的資料。
讀出的int型別變數為:987654
讀出的char型別變數為:g
讀出的Float型別變數為:3.141590
讀出的Double型別變數為:12345.678901
讀出的long型別變數為:1234567890
讀出的字串為:this is my parcel test! this is my parcel test! this is my parcel test! this is my parcel test!
呼叫recv函式成功,本次接收到124個位元組。共收到496個位元組的資料。
伺服器端:呼叫recv接收成功!本次接收到124個位元組,共收到496個位元組的資料。
讀出的int型別變數為:987654
讀出的char型別變數為:g
讀出的Float型別變數為:3.141590
讀出的Double型別變數為:12345.678901
讀出的long型別變數為:1234567890
讀出的字串為:this is my parcel test! this is my parcel test! this is my parcel test! this is my parcel test!
呼叫recv函式成功,本次接收到124個位元組。共收到620個位元組的資料。
伺服器端:呼叫recv接收成功!本次接收到124個位元組,共收到620個位元組的資料。
讀出的int型別變數為:987654
讀出的char型別變數為:g
讀出的Float型別變數為:3.141590
讀出的Double型別變數為:12345.678901
讀出的long型別變數為:1234567890
讀出的字串為:this is my parcel test! this is my parcel test! this is my parcel test! this is my parcel test!
呼叫recv函式成功,本次接收到124個位元組。共收到744個位元組的資料。
伺服器端:呼叫recv接收成功!本次接收到124個位元組,共收到744個位元組的資料。
讀出的int型別變數為:987654
讀出的char型別變數為:g
讀出的Float型別變數為:3.141590
讀出的Double型別變數為:12345.678901
讀出的long型別變數為:1234567890
讀出的字串為:this is my parcel test! this is my parcel test! this is my parcel test! this is my parcel test!
呼叫recv函式成功,本次接收到124個位元組。共收到868個位元組的資料。
伺服器端:呼叫recv接收成功!本次接收到124個位元組,共收到868個位元組的資料。
讀出的int型別變數為:987654
讀出的char型別變數為:g
讀出的Float型別變數為:3.141590
讀出的Double型別變數為:12345.678901
讀出的long型別變數為:1234567890
讀出的字串為:this is my parcel test! this is my parcel test! this is my parcel test! this is my parcel test!
…………………………………………
=============================
可以看到,例子程式成功實現了將資料封裝入parcel、傳送到socket、從socket接收、從接收到的parcel中解出資料的過程。
存在的問題:在客戶端程式中,有一句char buf[124];,這個buf是用來接收socket傳過來的parcel包的,但問題是,客戶端無法確定parcel包的大小。在實驗中,最開始並不是用的124這個資料,而是一個小的多的數,結果解出的資料就出現了混亂和丟失。而使用sizeof(p)和sizeof(Parcel)均無法得出正確的parcel包大小(這一點可以從伺服器端執行結果中看出,在parcel包大小為124的情況下,仍然顯示“Parcel包的大小是48位元組,Parcel結構體的大小是48位元組“)。因此無奈之下,只能通過檢視服務端程式的send函式的返回值,才得知過程中傳送了124個位元組,因此再修改客戶端程式,也才有了這句的設定:char buf[124];。
當然,如果在實際應用中,接收和傳送端程式都是自己編寫,自己心裡有數的話,那麼也無所謂。只要傳送多少位元組,然後接收端就相應接多少位元組就可以了。但是如果不能滿足這個條件,也就是說傳送端傳送的位元組數不確定或者不知道的話,就有一定麻煩了。對於這個問題我的解決思路有三種(但未經嚴格實驗驗證):
1、把char buf[124];中的124換為傳送端可能傳送的最大數值,這樣就都可以接收到了,而不會出現丟失資料。
2、傳送端在傳送parcel資料之前,先把資料包的大小發過來。接收端根據這個資訊再做適當調整。
3、參考android原始碼的解決方法,android原始碼的“/system/core/libcutils/record_stream.c”中有這方面的較好的解決方法,可以直接呼叫它裡面提供的函式,或根據自己的需要在它的基礎上加以修改。
相關推薦
Android環境下通過SOCKET傳遞Parcel包並解出資料的例子
之前做過了在android下通過socket傳送資料的實驗,也做過了parcel包的製作和解包的實驗(這兩個實驗的源程式之前都在本部落格的其他文章中貼過)。昨天和今天把這兩個過程合併了起來:即在Android環境下,甲程式(C++程式)將資料封裝在Parcel中,並把Par
Windows環境下Ruby離線安裝gem包
ruby gem 離線安裝 mongo 在上一篇博文中,我記錄了如何在Windows環境下進行Ruby操作MongoDB數據庫的環境配置。其中在最後一步講述了安裝MongoDB的驅動包。使用的是gem在線安裝方式。本文章的目的是為了在目標機器或環境無法連接互聯網時,如何使用gem進行gem工具
關於maven環境下使用pom.xml引入包名.lastUpdate包的解決辦法
文件 date clip 下使用 console -o maven pom i-o 今天在導入POI-OOXML的時候老師缺失xmlbeans包,而且刷新pom文件總是生成一個lastupdate文件,大小為1KB,終於找到解決辦法。 1.首先刪除想要的jar包所在文件夾
Linux環境下通過rpm安裝gcc的順序
首先檢視Linux版本: [[email protected] ~]# lsb_release -a LSB Version: :core-3.1-amd64:core-3.1-ia32:core-3.1-noarch:graphics-3.1-amd64:graphics-3
Mac及Android環境下的JNI學習
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!  
Ubuntu16.04環境下通過Cmake管理Opencv專案
Ubuntu16.04環境下通過Cmake管理Opencv專案 1、新建qt cmake工程 New Project -> Non-Qt Project -> Plain C++ Application 2、CMakeLists.txt檔案內
Android環境下的GDB除錯
gdb是GNU開發的針對Linux/Unix環境下程式的除錯工具。為了節約目標系統的資源,gdb通常採用gdb+gdbserver的方式進行除錯。 在Android GDB除錯場景下,gdb執行在PC端,gdbserver執行在Android系統中。在實際的除錯過程中,PC端的gdb參照
pthon3環境下利用socket實現server,client互動例項
1、例項要求 使用socket實現一個基於C/S架構的通訊程式 (1)客戶端傳送給伺服器請求,傳送表徵身份的使用者名稱和密碼(“admin”,“123456”); (2)伺服器根據客戶端發來的資訊驗證身份,如果驗證錯誤,返回“refuse”字串,並且斷開連線通道;
Solaris環境下使用snoop命令抓包
(1)報文抓取 Solaris中自帶有snoop抓包工具,通過執行相應的命令抓取。 抓取目的地址為10.8.3.250的資料包,並存放到/opt/cap250的檔案裡 snoop -o /opt/cap250 host 10.8.3.250 (2)報文下載 方法
Linux環境下通過docker搭建PHP的LAMP開發環境
想必作為一個Web開發的程式設計師,近些年對docker一定不會陌生,Docker 是一個開源的應用容器引擎,讓開發者可以打包他們的應用以及依賴包到一個可移植的容器中,然後釋出到任何流行的 Linux 機器上,也可以實現虛擬化。容器是完全使用沙箱機制,相互之間不會有任何介面。使用dock
【順序表】純C環境下,函式傳遞的指標指向報錯及解決
之前開始學順序表的時候,就沒有很好地弄懂,函式裡指標的傳遞這一塊,今天把錯誤範例和一些解決方式拿出來分析一下。 網上有很多掛羊頭賣狗肉的c語言教程,函式是引用呼叫的,就很誤導人。 Wrong: typedef struct { int *elem; in
windows環境下的socket程式設計(tcp檔案傳輸的實現)
開發環境 使用codeclock軟體進行程式設計 新建專案選擇console application完成相應的步驟即可。在專案下有main.c的檔案只需要將程式碼寫入其中即可。 程式碼設計 客戶端 client #include <std
windows環境下pip安裝python的包時候提示invalid syntax
剛學python,用pip安裝出錯,如下圖: 檢查了一下環境變數,然而並沒沒問題。在stackowerflow上逛了一圈,找到了解決方法: 在命令列下cd 進對應python的script檔案目錄,再執行pip指令即可。
Openstack學習筆記(七)-在Win環境下通過XManager(xshell)遠端開啟eclipse
1.首先關閉防火牆 sudo ufw disable 2.安裝協議 sudo apt-get install xrdp sudo apt-get install vnc4server tightvncserver sudo apt-get in
SUSE環境下通過YaST安裝軟體
為了提升使用者在雲伺服器上的軟體安裝效率,減少下載和安裝軟體的成本,騰訊雲提供了YaST下載源。作業系統為SUSE10的雲伺服器,開發者可通過YaST快速安裝軟體。 1. 安裝步驟 1. 列出軟體源 登入作業系統為Linux的雲伺服器後,預設已獲取root許可權:注意:嚴
windows環境下通過xshell把檔案傳遞給Linux虛擬機器
第一步,下載並xshell遠端登入軟體,開啟xshell,點選左上角的“新建”選項 得到如下介面 請在住主機選項出填寫linux虛擬機器的IP地址,在選擇“確定”,如果不知道IP地址,可以先返回虛擬機器,並在終端介面書寫命令#ifconfig,便可以檢視IP地址 第二步,遠端登入linux系統 第一步
生產環境下通過pm2部署node簡單例項
首先應該有一個要部署的專案 (1)建立一個資料夾project (2)在資料夾project中建立app.js檔案 (3)app.js內容如下 const http=require('http') http.createServer(function(req,res){ r
Edison平臺eclipse環境下通過MRAA庫使用IIC、SPI、UART例程(BMI160、MS8607)
MRAA庫IIC例程:以下程式是操作MS8607感測器mraa_init();/* initialize I2C on bus 0 */m_i2c = mraa_i2c_init(1);mraa_i2c_frequency(m_i2c,MRAA_I2C_FAST);//400
打包Golang並在Android環境下執行
參考連結 打包在android下的可執行程式 打包的程式碼如下,作用是輸出執行的作業系統的資訊: func main() { fmt.Print("Go runs on ") s
CentOS7環境下通過Docker安裝sentry
一,安裝Docker1 解除安裝舊版本sudo yum remove docker docker-common docker-selinux docker-engine2 安裝依賴包sudo yum install -y yum-utils device-mapper-pe