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. Java和C++的数组比较

    Java:数组的定义:如下两种方式都可以:int array[];int[] array; 注意:数组在定义后,不能直接使用.必须在初始化后才能使用.初始化有两种方式:1.静态初始化:int arra ...

  2. OPENCV中特征提取和匹配的步骤

    1.定义特征提取器和描述子提取器: cv::Ptr<cv::FeatureDetector> detector; cv::Ptr<cv::DescriptorExtractor> ...

  3. 口试Linq题

    LINQ to SQL与IQueryable 理解IQueryable的最简单方式就是,把它看作一个查询,在执行的时候,将会生成结果序列. LINQ to Object和LINQ to SQL有何区别 ...

  4. 使用 Chrome 浏览器插件 Web Scraper 10分钟轻松实现网页数据的爬取

    web scraper 下载:Web-Scraper_v0.2.0.10 使用 Chrome 浏览器插件 Web Scraper 可以轻松实现网页数据的爬取,不写代码,鼠标操作,点哪爬哪,还不用考虑爬 ...

  5. Oracle中将Clob字段转换成字符串

    1. 利用dbms_lob.substr()方法可将对应字段转换成字符串如下 select dbms_lob.substr(content) from NEWS 该方法有个缺点,当content字段长 ...

  6. 贝叶斯公式52张牌猜黑桃A策略

    贝叶斯公式52张牌猜黑桃A策略 考虑有208平行世界,其中有4个世界(1/52)的黑桃A方在第一张牌的位置,余下204个世界中,有4个世界的黑桃A在第2张牌的位置,4个世界在第3张牌的位置..... ...

  7. Unity3D编辑器扩展(二)——定义自己的窗口

    上一篇我们讲了如何定义菜单按钮 https://www.cnblogs.com/xiaoyulong/p/10115053.html 这一篇我们讲如何定义自己的窗口. 定义窗口我们需要继承 Edito ...

  8. JS prototype chaining(原型链)整理中······

    初学原型链整理 构造器(constructor).原型(prototype).实例(instance); 每一个构造器都有一个prototype对象,这个prototype对象有一个指针指向该构造器: ...

  9. 写给笨蛋徒弟的学习手册(3)—C#中15个预定义数据类型

    在C#中学习中,你会很早的遇到预定义数据类型这个概念,但你有没有仔细想过它存在的意义?正所谓“存在即合理”,预定义数据类型的存在目的主要有俩个方面,一是为了增加程序的安全性,同时减轻编译器负担,加快编 ...

  10. 正则表达式校验yyyymmdd

    正则表达式为 ([\\d]{4}(((0[13578]|1[02])((0[1-9])|([12][0-9])|(3[01])))|(((0[469])|11)((0[1-9])|([12][1-9] ...