Alljoyn是高通开发的一个开源的p2p通信框架,支持wifi/蓝牙和wifi-direct。目前已经有了很多基于这个框架开发的联机游戏。
在Alljoyn的sdk里有很多例子,其中有一个通过raw session发送信息的,对这个例子略加修改就可以实现两个手机之间文件的传输。
在client端,其发送的信息是通过一个FileOutputStream将要发送的信息写入到一个中间文件来发送到对方的。所以我们只要将要发送的文件通过流的形式输入到这个FileOutputStream中就可以了。
if (mStreamUp == true) {
String string = (String) msg.obj + "n";
try {
// logInfo(String.format("Writing %s to output stream",
// string));
// mOutputStream.write(string.getBytes());
// logInfo(String.format("Flushing stream", string));
// mOutputStream.flush();
String filepath = "/sdcard/test.txt";
File file = new File(filepath);
InputStream in =new FileInputStream(file);
byte[] buffer = new byte[1024];
int read;
long total = 0;
while ((read = in.read(buffer)) != -1) {
mOutputStream.write(buffer, 0, read);
total+=read;
Log.d("test","total:"+total);
if(total>100000){
mOutputStream.flush();
total=0;
//容易出现io exception,所以稍微等待一下。
Thread.sleep(100);
}
}
mOutputStream.flush();
Log.d("test","flush");
in.close();
} catch (IOException ex) {
Log.e("test",ex.toString());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
break;
}
其中前面注释掉的代码是实例程序原来的,下面的就是我修改的,将文件输入到输出流中。需要注意的是如果不加中间的sleep的话就会报IO Exception,这是因为raw使用的是非阻塞式socket发送的,如果发送得过快而网络传输得慢就会产生异常,中断传输。就好像一个大水池,一个水管往里注水,一个水管往外放水,如果注水的速度大于放水的速度的话水池就会溢出。所以在写入的时候稍微睡眠一下,也就是稍微控制一下注入的速度,让底层把内容发出去。
虽然这样可以在一定程度上减少异常的发生,但是传输的文件如果太大的话还是容易产生异常的,在这种情况下可以使用try catch来捕获这个异常然后休眠一段时间从中断的位置重新传输。
在server端,我们只需要将接受到的流写入到一个新建的文件中就可以了。其中注释掉的也是其原来的程序。
mReader = new Thread(new Runnable() {
public void run() {
logInfo("Thread running");
Looper.myLooper().prepare();
StringBuilder stringBuilder = new StringBuilder();
try {
while (true) {
int c;
/*
* Wait until a character is available.
*/
if (reader.ready() == false) {
Thread.sleep(100);
continue;
}
/*
* Build a string out of the characters and
* when we see a newline, cook up a toast
* do display them.
*/
if(saveFile("/mnt/sdcard/test.txt",is)){
Toast.makeText(Service.this, "save success", Toast.LENGTH_LONG).show();
}else{
Toast.makeText(Service.this, "save faild", Toast.LENGTH_LONG).show();
}
// try {
// c = reader.read();
// logInfo(String.format("Read %d from stream", c));
// stringBuilder.append((char)c);
// if (c == 10) {
// String s = stringBuilder.toString();
// logInfo(String.format("Read %s from stream", s));
// Message toastMsg = mHandler.obtainMessage(MESSAGE_POST_TOAST, s);
// mHandler.sendMessage(toastMsg);
// stringBuilder.delete(0, stringBuilder.length() - 1);
// }
// } catch (IOException ex) {
// }
}
} catch (Throwable ex) {
logInfo(String.format("Exception reading raw Java stream: %s", ex.toString()));
}
logInfo("Thread exiting");
}
}, "reader");
mReader.start();我们还需要加入一个保存文件的函数:
private static boolean saveFile(String filePath, InputStream is) {
boolean saved = false;
byte[] buffer = new byte[1024];
FileOutputStream fos = null;
File file = null;
try {
try {
file = new File(filePath);
fos = new FileOutputStream(file,true);
} catch (Exception e) {
Log.e(TAG,"creat file error");
}
int readlen = -1;
long readCount = 0;
while (true) {
saved = true;
try {
readlen = is.read(buffer);
} catch (IOException e) {
Log.e(TAG,"read inputstream error");
} finally {
if (readlen <= 0) {
break;
}
}
readCount += readlen;
Log.d("test","FILE length::::::::::::::::::::"+readCount);
try {
fos.write(buffer, 0, readlen);
} catch (IOException e) {
Log.e(TAG,"write file error");
}
}
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
Log.e(TAG, e.toString());
}
}
fos = null;
}
Log.d("test","saved:"+saved);
return saved;
}好了,这样在client端的sd卡的根目录中新建一个test.txt文件,当两个手机连接起来后,client端随便发送一个字符串就可以触发传输文件的操作了。
通过alljoyn传输文件(2)
上一篇文章介绍了使用alljoyn的raw方式进行文件的传输,其实那种方法不是很实用,并不是一种好的方式,这篇文章介绍一下使用alljoyn的signal方式进行文件的传输。
在alljoyn的sdk里面有个chat的例子,这个例子就是使用signal传输手机间的聊天信息的。我们所要做的就是将其修改一下来传输我们的文件。
首先修改ChatInterface.java里面的接口,将参数从string 类型改成byte[]类型,如下所示:
点击(此处)折叠或打开
public void Chat(byte[ str) throws Bu***ception;
这就表示我们传输的不是字符串而是byte数组了。所以接下来的问题就是将文件内容转换成一系列的byte数组发出去然后接收这些byte数组并重新组成文件就可以了。首先来看发送,发送时在AllJoynService.java的doSendMessages()方法。
点击(此处)折叠或打开
try {
if (mJoinedToSelf) {
if (mHostChatInterface != null) {
// mHostChatInterface.Chat(message);
}
} else {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy年MM月dd日
HH:mm:ss ");
Date curDate = new Date(System.currentTimeMillis());//
获取当前时间
String str = formatter.format(curDate);
Log.d("test", "start:" + str+"::::");
String filepath = "/sdcard/Picture/100ANDRO/MOV_0426.mp4";
File file = new File(filepath);
InputStream in = null;
try {
in = new FileInputStream(file);
} catch (FileNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
byte[ buffer = new byte[102400;
int read = 0;
while ((read = in.read(buffer)) != -1) {
mChatInterface.Chat(buffer);
buffer = new byte[102400;
}
in.close();
curDate = new Date(System.currentTimeMillis());//
获取当前时间
str = formatter.format(curDate);
Log.d("test", "end:" + str+"::::");
}
} catch (Bu***ception ex) {
mChatApplication.alljoynError(ChatApplication.Module.USE,
"Bus exception while sending message: (" + ex + ")");
} catch (IOException e) {
Log.d("test", e.toString());
}
在发送的前后加上系统的时间这样就可以测试发送文件花费的时间。接收是在Chat(byte[] string) 方法里面。
点击(此处)折叠或打开
public void Chat(byte[ string) {
/*
* See the long comment in doJoinSession() for more explanation of why
* this is needed.
*
* The only time we allow a signal from the hosted session ID to pass
* through is if we are in mJoinedToSelf state. If the source of the
* signal is us, we also filter out the signal since we are going to
* locally echo the signal.
*/
String uniqueName = mBus.getUniqueName();
MessageContext ctx = mBus.getMessageContext();
Log.i(TAG, "Chat():
use sessionId is " + mUseSessionId);
Log.i(TAG, "Chat():
message sessionId is " + ctx.sessionId);
/*
* Always drop our own signals which may be echoed back from the system.
*/
if (ctx.sender.equals(uniqueName)) {
Log.i(TAG, "Chat():
dropped our own signal received on session "
+ ctx.sessionId);
return;
}
/*
* Drop signals on the hosted session unless we are joined-to-self.
*/
if (mJoinedToSelf == false && ctx.sessionId == mHostSessionId) {
Log.i(TAG, "Chat():
dropped signal received on hosted session "
+ ctx.sessionId + "
when not joined-to-self");
return;
}
/*
* To keep the application simple, we didn't force users to choose a
* nickname. We want to identify the message source somehow, so we just
* use the unique name of the sender's bus attachment.
*/
String nickname = ctx.sender;
nickname = nickname
.substring(nickname.length() - 10, nickname.length());
// Log.i(TAG, "Chat(): signal " + string + " received from nickname " +
// nickname);
// mChatApplication.newRemoteUserMessage(nickname, "get message");
try {
try {
if (file == null) {
file = new File("/mnt/sdcard/abc.mp4");
}
fos = new FileOutputStream(file, true);
} catch (Exception e) {
Log.e(TAG, "creat
file error");
}
try {
fos.write(string, 0, string.length);
} catch (IOException e) {
Log.e(TAG, "write
file error");
}
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
Log.e(TAG, e.toString());
}
}
fos = null;
}
}
这样就可以进行文件的传输了,测试表明这个方法比上个方法更快更稳定了。
|