技术标签: 通讯 网络 服务器 socket Android实战开发 Android
欢迎转载,转载请注明出处:http://blog.csdn.net/dmk877/article/details/51685656
Hello,大家好,今天又来写博客了,项目终于搞完了最近又有时间写写博客了。在上上篇博客中我们学习了Android中网络通讯,并用Socket实现了Android客户端与服务器的通讯,那么这一篇将进一步来完善这一功能,就是要实现两个android手机端的通讯。
如有谬误,欢迎批评指正,如有疑问欢迎留言
通过本篇博客你将学到以下知识点
①Socket的用法
②如何实现两个手机的端的通讯
③JAVA中的流的小知识
在开始之前我们先来分析以下如何实现这个通讯,首先来考虑这么几个问题
①这种实时的通讯应采取哪种通讯方式
②怎么保证是消息能到达我们想发送的那一方,也就是说A想与B交流怎么保证B收到的消息是A发过来的
③怎么保证消息的实时性,也就是说A发一条消息B能实时收到。
要想回答这三个问题其实不难,读过Android开发之网络通讯详解这篇博客的童鞋应该知道,这种及时性比较高的通讯一般采用Socket这种通讯方式,而对于第二个问题如果想让消息到达我们的指定方,针对A和B需要有一个唯一的标识,这个标识我们可以自己定义,在本文中采用的策略是为A和B定义一个ID来唯一的确定它,在A用户发送消息的时候只要指定它想把消息发送到的那个用户的ID就能保证B收到的消息是A发过来的。保证消息的实时性也就是通过使用Socket来解决的。
通讯的过程图解
从上图我们可以清楚的看到整个过程服务器充当的是一个中转的作用,clientSocket1首先会将消息封装成json发送给服务器,服务器解析后再封装将消息发送给clientSocket2。
客户端的运行效果图(注意:下面是一整张图,是一张图片):
上述运行效果就实现了两个Android机的相互通讯。两个客户端分配的id分别为0和1。在发送消息时需要指定消息的到达方。
3.1 客户端发送消息实现步骤
第一步需要做的当然就是连接到服务器,连接服务器的方法为
clientSocket=new Socket(ip,port);
即设置服务器的ip和端口号,建立连接之后通过while(true)就可以不断的监听服务器是否发送过来消息了。
第二步就是要输入你要发送的好友的id号,这样就能保证你发送的消息能按照你的预期到达接收方。
第三步就是发送消息了在发送消息的时候需要注意,我们是把消息封装成json发过去的,这个json中包含哪些信息呢?有三种信息①消息发送方id②消息的内容③消息到达方id。
封装json的代码如下
//根据clientSocket.getOutputStream得到BufferedWriter对象,从而从输出流中获取数据
mWriter=new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream(),"utf-8"));
。。。。。
//封装成json
JSONObject json = new JSONObject();
json.put("to", Integer.parseInt(friendId));
json.put("msg", msgContent);
//通过BufferedWriter对象向服务器写数据
mWriter.write(json.toString()+"\n");
//一定要调用flush将缓存中的数据写到服务器
mWriter.flush();
这个json中包含三个信息:①消息到达方id②消息的内容③消息发送方的id。封装后通过BufferWriter的write方法将消息发送到服务器。
3.2 客户端接收消息的实现
这里接收消息是用了while循环不断的监听服务器是否有消息发送过来,如果有消息发送过来则用BufferedReader一行一行的去读取消息的内容,然后显示,实现的主要代码如下:
//根据clientSocket.getInputStream得到BufferedReader对象,从而从输入流中获取数据
mReader=new BufferedReader(new InputStreamReader(clientSocket.getInputStream(),"utf-8"));
//根据clientSocket.getOutputStream得到BufferedWriter对象,从而从输出流中获取数据
mWriter=new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream(),"utf-8"));
while(isReceivingMsgReady){
if(mReader.ready()){
Message msg = handler.obtainMessage();
msg.what=2;
msg.obj=mReader.readLine();
handler.sendMessage(msg);
}
Thread.sleep(200);
}
mWriter.close();
mReader.close();
clientSocket.close();
可以看到在客户端通过while循环当mReader.ready()为true时就会读取服务器返回消息的一行,然后更新显示。
4、实现两个手机间的通讯服务器的实现
服务器端的代码我使用是MyEclipse,首先我们来看看上述发送消息的过程中服务器端运行的截图:
服务器端的功能是怎么实现的呢?毫无疑问首先要做的当然是初始化服务器并指定端口号,代码如下:
serverSocket = new ServerSocket(SOCKET_PORT);
接着当有客户端连接到服务器时会为每一个客户端开启一个子线程,并将这个子线程加入到集合中对应的代码如下
while (flag) {
//有客户端连接,时serverSocket.accept()方法会返回客户端socket
Socket clientSocket = serverSocket.accept();
SocketThread socketThread = new SocketThread(clientSocket,socketId++);
socketThread.start();
mThreadList.add(socketThread);
}
在开启的子线程SocketThread中会有一个while循环监听客户端发过来的消息,并将这条消息添加到mThreadList集合,在这个子线程中循环读取客户端发过来的消息
//循环读取客户端发过来的消息
while (flag) {
if (reader.ready()) {
String comeData=reader.readLine();
JSONObject msgJson = new JSONObject(comeData);
Message msg = new Message();
msg.setTo(msgJson.getInt("to"));
msg.setMsg(msgJson.getString("msg"));
msg.setFrom(mSocketId);
msg.setTime(getTime(System.currentTimeMillis()));
mMsgList.add(msg);
System.out.println("用户:"+mSocketId+"向用户:"+msg.getTo()+"发送的消息内容为:"+msg.getMsg());
}
Thread.sleep(100);
}
其实也就是说在服务器中为每一个连接到服务器的客户端开启一个子线程,并在这个子线程中循环监听这个客户端是否发过来消息,在上面我们看到服务器解析消息后,将这条消息封装到了Message这个bean中并添加到了集合中。Message中包含的信息从上面可以看出。到这里服务器就可以收到服务器发过来的消息了,服务器收到消息后要做的工作就是将这条消息发送给客户端,(从实现原理图中可以看出服务器起到一个中转的作用)那么怎样实现这个功能呢?它的实现也不难,从上面的描述我们可以知道只要客户端向服务器发送一条消息,我们就会将这条消息放到一个集合mMsgList中,也就是说只要服务器收到消息mMsgList这个集合就不为空,只要mMsgList这个集合不为空就说明服务器还拥有未转发的消息,需要转发。怎么实现转发呢?
首先这里有两个集合一个是Message的集合mMsgList,另一个是为每一个客户端Socket开启的一个子线程SocketThread的集合mThreadList。这两个集合的关系是每一个Message肯定是mThreadList中的一个SocketThread(客户端)发送的。
当SocketThread的id与Messsage中的to(即消息的到达方)相等时,因为每一个客户端(假设是clientSocket1)连接到服务器时都要开启一个SocketThread子线程,在这个子线程中有clientSocket1的引用,所以当相等时就说明这个Message是发送给这个clientSocket1的。然后通过这个clientSocket1的BufferedWriter将这个消息发送给这个客户端。与其对应的代码如下
while(flag) {
if(mMsgList.size() > 0) {
Message from = mMsgList.get(0);
for(SocketThread toThread : mThreadList) {
//遍历mThreadList如果to.socketID==from.to说明这个toThread与mMsgList中的这条内容是对应的
//这里toThread的作用是通过它得到这条消息的BufferedWriter,mMsgList.get(0)得到这条消息,然后通过
//BufferedWriter将这条消息发送到指定方
if(toThread.mSocketId == from.getTo()) {
//这里的writer是SocketThread中的writer,这样才能保证在调用writer.flush之后消息到达
//我们的指定方
BufferedWriter writer = toThread.writer;
JSONObject json = new JSONObject();
json.put("from", from.getFrom());
json.put("msg", from.getMsg());
json.put("time", from.getTime());
writer.write(json.toString()+"\n");
writer.flush();
System.out.println("转发消息成功");
break;
}
}
mMsgList.remove(0);
}
Thread.sleep(200);
}
这样将这条消息解析封装成json后就发送到了指定的客户端,就完成了两个android机的通讯。其实个人觉着对这些原理的知识还是需要去好好学习,弄懂的,它的实现思想对我们以后的编程也是很有作用的。
关于实现两个手机端通讯的讨论就到这里了,可能我们看到的那些通讯的软件采用的方式不是本文使用的方式,但是原理性的东西是不会变的,这篇博客也是从最原始的地方来理解手机通讯的一些简单的原理。
如有谬误,欢迎批评指正,如有疑问欢迎留言。
如果本篇博客对你有帮助就点击左上角关注,关注吧,锁定本台观看更多精彩内容0.0
这里提醒大家注意的是源码包含两部分,一部分是服务端,另一部分是客户端,服务端是MyEclipse程序,客户端就是我们的android程序。
文章浏览阅读1k次。通过使用ajax方法跨域请求是浏览器所不允许的,浏览器出于安全考虑是禁止的。警告信息如下:不过jQuery对跨域问题也有解决方案,使用jsonp的方式解决,方法如下:$.ajax({ async:false, url: 'http://www.mysite.com/demo.do', // 跨域URL ty..._nginx不停的xhr
文章浏览阅读2k次。关于在 Oracle 中配置 extproc 以访问 ST_Geometry,也就是我们所说的 使用空间SQL 的方法,官方文档链接如下。http://desktop.arcgis.com/zh-cn/arcmap/latest/manage-data/gdbs-in-oracle/configure-oracle-extproc.htm其实简单总结一下,主要就分为以下几个步骤。..._extproc
文章浏览阅读1.5w次。linux下没有上面的两个函数,需要使用函数 mbstowcs和wcstombsmbstowcs将多字节编码转换为宽字节编码wcstombs将宽字节编码转换为多字节编码这两个函数,转换过程中受到系统编码类型的影响,需要通过设置来设定转换前和转换后的编码类型。通过函数setlocale进行系统编码的设置。linux下输入命名locale -a查看系统支持的编码_linux c++ gbk->utf8
文章浏览阅读750次。今天准备从生产库向测试库进行数据导入,结果在imp导入的时候遇到“ IMP-00009:导出文件异常结束” 错误,google一下,发现可能有如下原因导致imp的数据太大,没有写buffer和commit两个数据库字符集不同从低版本exp的dmp文件,向高版本imp导出的dmp文件出错传输dmp文件时,文件损坏解决办法:imp时指定..._imp-00009导出文件异常结束
文章浏览阅读143次。当下是一个大数据的时代,各个行业都离不开数据的支持。因此,网络爬虫就应运而生。网络爬虫当下最为火热的是Python,Python开发爬虫相对简单,而且功能库相当完善,力压众多开发语言。本次教程我们爬取前程无忧的招聘信息来分析Python程序员需要掌握那些编程技术。首先在谷歌浏览器打开前程无忧的首页,按F12打开浏览器的开发者工具。浏览器开发者工具是用于捕捉网站的请求信息,通过分析请求信息可以了解请..._初级python程序员能力要求
文章浏览阅读7.6k次,点赞2次,收藏6次。@Service标注的bean,类名:ABDemoService查看源码后发现,原来是经过一个特殊处理:当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致public class AnnotationBeanNameGenerator implements BeanNameGenerator { private static final String C..._@service beanname
文章浏览阅读6.9w次,点赞73次,收藏463次。1.前序创建#include<stdio.h>#include<string.h>#include<stdlib.h>#include<malloc.h>#include<iostream>#include<stack>#include<queue>using namespace std;typed_二叉树的建立
文章浏览阅读7.1k次。在Asp.net上使用Excel导出功能,如果文件名出现中文,便会以乱码视之。 解决方法: fileName = HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8);_asp.net utf8 导出中文字符乱码
文章浏览阅读2.1k次,点赞4次,收藏23次。第一次实验 词法分析实验报告设计思想词法分析的主要任务是根据文法的词汇表以及对应约定的编码进行一定的识别,找出文件中所有的合法的单词,并给出一定的信息作为最后的结果,用于后续语法分析程序的使用;本实验针对 PL/0 语言 的文法、词汇表编写一个词法分析程序,对于每个单词根据词汇表输出: (单词种类, 单词的值) 二元对。词汇表:种别编码单词符号助记符0beginb..._对pl/0作以下修改扩充。增加单词
文章浏览阅读773次。我在使用adb.exe时遇到了麻烦.我想使用与bash相同的adb.exe shell提示符,所以我决定更改默认的bash二进制文件(当然二进制文件是交叉编译的,一切都很完美)更改bash二进制文件遵循以下顺序> adb remount> adb push bash / system / bin /> adb shell> cd / system / bin> chm..._adb shell mv 权限
文章浏览阅读6.8k次,点赞12次,收藏125次。1. 单目相机标定引言相机标定已经研究多年,标定的算法可以分为基于摄影测量的标定和自标定。其中,应用最为广泛的还是张正友标定法。这是一种简单灵活、高鲁棒性、低成本的相机标定算法。仅需要一台相机和一块平面标定板构建相机标定系统,在标定过程中,相机拍摄多个角度下(至少两个角度,推荐10~20个角度)的标定板图像(相机和标定板都可以移动),即可对相机的内外参数进行标定。下面介绍张氏标定法(以下也这么称呼)的原理。原理相机模型和单应矩阵相机标定,就是对相机的内外参数进行计算的过程,从而得到物体到图像的投影_相机-投影仪标定
文章浏览阅读2.2k次。文章目录Wayland 架构Wayland 渲染Wayland的 硬件支持简 述: 翻译一篇关于和 wayland 有关的技术文章, 其英文标题为Wayland Architecture .Wayland 架构若是想要更好的理解 Wayland 架构及其与 X (X11 or X Window System) 结构;一种很好的方法是将事件从输入设备就开始跟踪, 查看期间所有的屏幕上出现的变化。这就是我们现在对 X 的理解。 内核是从一个输入设备中获取一个事件,并通过 evdev 输入_wayland