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. oracle service name sid , 用户 和 表空间

    oracle 的四个概念: 数据库: 就是一堆静态的数据文件.注意是静态的 instance 实例: 可以类比数据库连接. 实例就是为了操作数据库而开辟的进程和内存空间,有了这个实例你才能操作数据库. ...

  2. spring实现listener(转)

    博主说未经同意,不能转载,我这种小码农,他应该不会在乎 原创地址:http://blog.csdn.net/caihaijiang/article/details/8629725 spring 允许 ...

  3. [Bat]UNC路径不支持的2种解决方法

    UNC(Universal Naming Convention),\\servername\sharename,其中servername是服务器名.sharename是共享资源的名称 1.使用push ...

  4. zepto 源码 $.contains 学习笔记

    $.contains(parent,node)  返回值为一个布尔值 ==> boolean parent,node我们需要检查的节点检查父节点是否包含给定的dom节点,如果两者是相同的节点,返 ...

  5. 利用IO和File类实现拷贝文件目录问题

    /* 复制文件夹 参数 File src,File dest */ public static void copy(File src,File dest){ if (src.isDirectory() ...

  6. 2019.03.28 bzoj3595: [Scoi2014]方伯伯的Oj(splay+map+set)

    传送门 题意简述: 给一个有优先级的nnn个人的序列,初始的时候第iii个人排名为iii,现在有mmm个操作,种类如下: 把编号为xxx的改成yyy,输出改前xxx的排名 把编号为xxx放到队首,输出 ...

  7. Vue控制路由滚动行为

    跳转路由时,要求跳转到指定路由的某个地方,可以使用scrollBehavior方法控制. 用法: scrollBehavior(to,from,savedPosition){   } scrollBe ...

  8. ScrollView无法滚动

    ScrollView视图无法滚动 箭头所指地方应该用dp单位的数值

  9. Visual Studio学习记录

    1,一些快捷键记录 1,折叠 ctrl+M+A: 折叠所有代码[官方名:折叠所有大纲提示] ctrl + M + O:折叠全部代码[官方:折叠到定义],但是这个貌似只能折叠代码,xml之类的无效.m+ ...

  10. SQL Server数据库设置自动备份策略

    一. 简单介绍 SQL Server自带的维护计划是一个非常有用的维护工具,能够完成大部分的数据库的维护任务. 数据库的备份也是日常工作中非常重要的一个环节.备份的方法非常的多. 今天给大家介绍最简单 ...