Socket:

有服务器和客户端之分,其是对TCP/IP的封装,使用IP地址加端口,确定一个唯一的点。在Internet上的主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务。值得注意的是用户使用的端口最好大于1024,因为小于1024的大部分端口都是被系统占用的。此章将实现安卓socket客户端编程。

安卓的线程基本机制

一个程序就是一个进程,一个进程里可以有多个线程,每个进程必须有一个主线程。对应安卓一个应用程序就是一个进程,其主线程就是平常所说的安卓主UI线程。安卓实现多线程编程,其有一个重要的原则就是更新UI必须在主线程,但耗时操作必须在子线程中,如果耗时操作在主线程编写(如网络访问)当阻塞时间达到一定时,应用就会强制退出,那网络访问就面临着一个不可避免的问题:子线程更新UI操作如何实现。

Handler

Handler主要用于异步消息的处理: 有点类似辅助类,封装了消息投递、消息处理等接口。当发出一个消息之后,首先进入一个消息队列,发送消息的函数即刻返回,而另外一个部分在消息队列中逐一将消息取出,然后对消息进行处理,也就是发送消息和接收消息不是同步的处理。 这种机制通常用来处理相对耗时比较长的操作。

Message

Handler接收与处理的消息对象,其中消息类型有

public int arg1和public int arg2:存放简单的整数类型消息

public Object obj:发送给接收器的任意对象,不管是整数,字符串,某个类对象均可

public int what:用户自定义的消息代码,这样接受者可以了解这个消息的信息,每个handler各自包含自己的消息代码,所以不用担心自定义的消息跟其他handlers有冲突。

 安卓端实现效果

在同一网络下的一个设备开启一个端口的监听,做为socket服务器,并获取到服务器设备的IP地址和端口号,将其格式化为 “IP:端口” 进行输入,如 “193.169.44.198:8081” ,点击连接即可。安卓作为socket客户端与服务器交互数据。

编程实现

获取网络访问权限:

实现socket编程,必须开启网络访问权限

<uses-permission android:name="android.permission.INTERNET" />

编写Handler消息处理类:

handler消息处理类是MainActivity类的内部类,当消息队列不为空时将自动进入,获取到消息值并分析其中内容

 private Handler mainhandler=new Handler(){
@Override
public void handleMessage(Message msg) {
//获取到命令,进行命令分支
int handi=msg.arg1;
switch (handi){
case 0:
String ormsg=(String)msg.obj;
disSocket();//断开网络
Toast.makeText(MainActivity.this,"发生错误=>:"+ormsg,Toast.LENGTH_SHORT).show();
break;
case 1:
Toast.makeText(MainActivity.this,"连接成功",Toast.LENGTH_SHORT).show();
break;
case 2:
//收到数据
String str1=(String)msg.obj;
main_rx.setText(str1);
break;
default:break;
}
}
};

连接按钮监听:

当连接按钮按下时,将会立即获取输入框的内容并进行字符串分隔,得到IP地址和端口号,开启线程进行网络连接

 //连接按钮监听
main_conn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String strip=main_ip.getText().toString().trim();
if(strip.indexOf(":")>=0){ //开始启动连接线程
new Socket_thread(strip).start(); } }
});

发送数据按钮监听:

当发送数据按钮按下时,将会立即获取到发送输入框的内容,分别可以调用字符串发送函数和十六进制发送函数进行数据发送

 //发送按钮监听
main_send.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//得到输入框内容
final String senddata=main_tx.getText().toString().trim(); if(!senddata.equals("")){
//发送因为使用的是线程,所以先后顺序不一定
//发送字符串数据
sendStrSocket(senddata);
//发送十六进制数据
sendByteSocket(new byte[]{0x01,0x02,0x03}); }else Toast.makeText(MainActivity.this,"输入不可为空",Toast.LENGTH_SHORT).show();
}
});

开始网络连接线程:

该类为MainActivity类的内部类,实现线程连接socket服务器,并获取输入输出流,并开启接收线程

 class Socket_thread extends Thread
{
private String IP="";//ip地址
private int PORT=0;//端口号
public Socket_thread(String strip){
//构造方法需要传递服务器的IP地址和端口号
//如: 192.168.43.222:8099
//进行字符串分隔,得到服务器IP地址和端口号
String[] stripx= strip.split(":");
this.IP=stripx[0];
this.PORT=Integer.parseInt(stripx[1]);
}
@Override
public void run() {
try { disSocket();//断开上次连接
if(sock !=null){
outx.close();
inx.close();
sock.close();//关闭
sock=null;
}
//开始连接服务器,此处会一直处于阻塞,直到连接成功
sock=new Socket(this.IP,this.PORT); //阻塞停止,表示连接成功,发送连接成功消息
Message message=new Message();
message.arg1=1;
mainhandler.sendMessage(message); }catch (Exception e) {
Message message=new Message();
message.arg1=0;
message.obj="连接服务器时异常";
mainhandler.sendMessage(message); System.out.println("建立失败////////////////////////////////////////////");
e.printStackTrace();
return;
}
try {
//获取到输入输出流
outx=sock.getOutputStream();
inx=sock.getInputStream();
} catch (Exception e) {
//发送连接失败异常
Message message=new Message();
message.arg1=0;
message.obj="获取输入输出流异常";
mainhandler.sendMessage(message); System.out.println("流获取失败////////////////////////////////////////////");
e.printStackTrace();
return;
} // new Outx().start();
new Inx().start();
}
}

关闭socket函数:

关闭socket之前将先关闭输入输出流,这样才能更加安全的关闭socket

 private void disSocket(){
//如果不为空,则断开socket
if(sock !=null){
try {
outx.close();
inx.close();
sock.close();//关闭
sock = null;
}catch (Exception e){
//发送连接失败异常
Message message=new Message();
message.arg1=0;
message.obj="断开连接时发生错误";
mainhandler.sendMessage(message); }
} }

数据接收线程实现: 

接收线程将实现数据的接收,并把接收到的数据通过消息发送给处理类,特别注意的是 inx.read(bu) 返回如果是 -1 则表示服务器断开了连接或者其它非主动调用关闭socket方法断开造成的错误

 //循环接收数据
class Inx extends Thread{
@Override
public void run() {
while(true){ byte[] bu=new byte[1024];
try {
//得到-1表示服务器断开
int conut=inx.read(bu);//设备重启,异常 将会一直停留在这
if(conut==-1){
//发送连接失败异常
Message message=new Message();
message.arg1=0;
message.obj="服务器断开";
mainhandler.sendMessage(message);
disSocket();//断开连接
System.out.println("**********服务器异常*********:"+conut);
return;
} //必须去掉前后空字符,不然有这个会有1024个字符每次
strread=new String(bu,"GBK").trim();
//发送出收到的数据
Message message=new Message();
message.arg1=2;
message.obj=strread;
mainhandler.sendMessage(message); } catch (IOException e) {
System.out.println(e); }
} }}

发送字符串函数:

网络编程的最终发送的内容是字节,所以发送字符串需要通过getBytes进行编码

 //发送字符串
private void sendStrSocket(final String senddata){
new Thread(new Runnable() {
@Override
public void run() {
try {
//可以经过编码发送字符串
outx.write(senddata.getBytes("gbk"));//"utf-8" } catch (Exception e) {
//发送连接失败异常
Message message=new Message();
message.arg1=0;
message.obj="数据发送异常";
mainhandler.sendMessage(message);
}
}
}).start();
}

发送十六进制函数:

通过字节数组,可以实现多个十六进制数据的发送

 //发送十六进制
private void sendByteSocket(final byte[] senddata){
new Thread(new Runnable() {
@Override
public void run() {
try {
//发送十六进制
outx.write(senddata); } catch (Exception e) {
//发送连接失败异常
Message message=new Message();
message.arg1=0;
message.obj="数据发送异常";
mainhandler.sendMessage(message);
}
}
}).start();
}

参考:

https://blog.csdn.net/rabbit_in_android/article/details/50585156

https://www.imooc.com/article/25134?block_id=tuijian_wz

安卓之必须了解的实时通信(Socket)的更多相关文章

  1. unity3d,java,c#,python,rospy的socket通信测试

    1.C#在与其他人通信时,最好不要用tcpclient来承接其他语言,会收不到用户名,最好都用socket. 2.unity3d在与java通信时,对方返回我unity3d发的数据流会打印收到一个类, ...

  2. Android和C#实时视频传输Demo

    说起去年的Demo.以今天的免费整齐优势. 原理很easy,虽然没有写android申请书.但,好了~ 高级语言是相通的.傲慢约.就这么简单研究了一下api后,找到相机对象有一个预览回调方法. 意识到 ...

  3. 热情组——项目冲刺 Day5

    项目相关 作业相关 具体描述 班级 班级链接 作业要求 链接地址 团队名称 热情组 作业目标 实现软件制作,以及在福大的传播 Github链接 链接地址 SCRUM部分: 成员昵称 昨日目标 开始时间 ...

  4. 安卓Socket连接实现连接实现发送接收数据,openwrt wifi转串口连接单片机实现控制

    安卓Socket连接实现连接实现发送接收数据,openwrt wifi转串口连接单片机实现控制 socket 连接采用流的方式进行发送接收数据,采用thread线程的方式. 什么是线程?  详细代码介 ...

  5. 安卓socket聊天

    安卓基于Socket通信(服务器配合) 1.话不多说进入正题,先创建服务端,在Android Studio中创建Java代码,如下图所示: 选择Java Library 需要改名字的自己随意 2.创建 ...

  6. Web实时通信之Socket.IO

    前面两篇文章使用了Ajax long polling和WebSocket两种常用的Web实时通信方式构建了简单的聊天程序. 但是,由于浏览器的兼容问题,不是所有的环境都可以使用WebSocket这种比 ...

  7. 用Socket开发的一枚小型实时通信App

    Socket 英文原意是插座. 在网络世界里, 当一台主机温柔而体贴的同时提供多个服务时, 每个服务被绑定在一个端口上, 而每个端口就好像一个小插座. 用户们连接对应的插座去获取相应的服务. 在Nod ...

  8. 安卓Socket开发注意事项

    如果要在安卓app里用到Socket通信,要满足: 1.在写代码的activity里import进Socket相关的包,这个很easy,如果你不知道要import什么包,也可以先不指定,可       ...

  9. 安卓使用Socket发送中文,C语言服务端接收乱码问题解决方式

    今天用安卓通过Socket发送数据到电脑上使用C语言写的服务端,发送英文没有问题,可当把数据改变成中文时,服务端接收到的数据确是乱码. 突然想到.VS的预处理使用的是ANSI编码.而安卓网络数据都是U ...

随机推荐

  1. python 数据库mysql、redis及发送邮件

    python 关系型数据库链接使用--mysql import pymysql # 引用mysql模块 # 创建连接,指定数据库的ip地址,账号.密码.端口号.要操作的数据库.字符集coon = py ...

  2. Maven 属性

    maven 中使用 ${属性名} 来访问属性 内置属性 (maven 预定义,用户可以直接使用的属性) ${basedir}  表示项目根目录,即包含 pom.xml 文件的目录.同 ${projec ...

  3. [AI]SKLearn章1 快速入门

    SciKit learn的简称是SKlearn,是一个python库,专门用于机器学习的模块. SKlearn包含的机器学习方式: 分类,回归,无监督,数据降维,数据预处理等等,包含了常见的大部分机器 ...

  4. SBT实操指南

    参考资料:1.英文官方文档2.中文官方文档,内容翻译的不全 SBT是类似maven和gradle的自动构建和包依赖管理工具,SBT是Scala技术体系下的包管理工具,都是Lightbend公司开发的, ...

  5. JS獲取URL的參數

    function GetQueryString(name) { var reg = new RegExp("(^|&)"+ name +"=([^&]*) ...

  6. C# ListView应用

    C#  ListView应用 1. 添加表头标题的方法 a. 直接在ListView控件上编写 b. 通过代码编写 //动态添加lstv_ReaderList列表头 /* lstv_ReaderLis ...

  7. shell脚本编写某一文件夹内拷贝某一段文件(有则跳过没有则拷贝)

    必须是同一台服务器下,或者挂载目录,不同服务器下没办法查询目录中是否有该文件 如果不在同一服务器下,可以把要查询的那个服务器的文件夹设置共享挂在到当前服务器 或者可以把脚本写到要拷贝的服务器上,那么s ...

  8. Redis的启动及配置

    在redis已经安装完成的情况下,进入redis/bin目录下,输入命令: ./redis-server,就可以直接启动redis了,效果如图所示: 但是此时终端无法进行任何操作,按CTRL+c命令, ...

  9. Maven之pom.xml配置文件详解

    此文非原创,摘自:https://www.baidu.com/link?url=GlGgW21nijIiULDZj0RfPH8ofqGMqEnAzXiym7O3hfrZM5nFH2enukemBNTX ...

  10. xml文档格式学习笔记

    xml入门经典 (pdf书籍) https://www.cnblogs.com/zhaopengcheng/p/6848802.html