之前做过了在android下通过socket发送数据的实验,也做过了parcel包的制作和解包的实验(这两个实验的源程序之前都在本博客的其他文章中贴过)。昨天和今天把这两个过程合并了起来:即在Android环境下,甲程序(C++程序)将数据封装在Parcel中,并把Parcel发送到SOCKET;乙程序(C++程序)通过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”中有这方面的较好的解决方法,可以直接调用它里面提供的函数,或根据自己的需要在它的基础上加以修改。
原先认为分开的过程都做过了,合并起来应该不是什么问题。但昨天一下午却未能成功,一直到今天上午才基本上弄了出来。现将程序的源代码放在下面:
程序里面的一些无用的调试信息没有去掉,都已经注释掉了,不影响编译运行。如果觉得影响阅读,可先复制下来,再将其中的调试信息删除即可。
/****************** 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”中有这方面的较好的解决方法,可以直接调用它里面提供的函数,或根据自己的需要在它的基础上加以修改。