安卓Socket通信实例
1、Socket通信必须知道的地方
-
首先,Socket通信采用TCP模式,客户端需要连接到服务端
-
采用网络,需要打开Internet权限
-
需要合理调用线程,熟悉线程与输入输出流的使用
-
socket服务端和客户端编码方式不一致将有可出现中文乱码问题
-
在收发数据前,必须和对方建立可靠的连接。
- 一个TCP连接必须要经过三次“对话”才能建立,其中的过程非常复杂,
- 过程:主机A向主机B发出连接请求数据包:“我想给你发数据,可以吗?”,这是第一次对话;
- 主机B向主机A发送同意连接和要求同步(同步就是两台主机一个在发送,一个在接收,协调工作)的数据包:“可以,你什么时候发?”,这是第二次对话;
- 主机A再发出一个数据包确认主机B的要求同步:“我现在就发,你接着吧!”,这是第三次对话。三次“对话”的目的是使数据包的发送和接收同步,经过三次“对话”之后,主机A才向主机B正式发送数据。
2、文件输入流与输出流的讲解 :
🙂
不管你从磁盘读,从网络读,或者从键盘读,读到内存,就是InputStream。
不管你写到磁盘,写到网络,或者写到屏幕,都是OuputStream。
🍎
我们所说的流,都是针对内存说的,比如为什么打印到屏幕上就是System.out.println();而从屏幕等待用户输入的却是System.in呢?因为对于内存来说,把字符串打印到屏幕上是从内存流向屏幕这个显示器的,也就是输出,而从屏幕等待用户输入呢?就是等待键盘将字符输入到内存中。
所以,你根本就不用死记硬背,当你遇到IO的时候,就想两件事,第一,我的内存是中心,第二看看流的方向(矢量)!
那我访问网络,看网页是什么呢 网络--------------->内存 是 in 因为我们访问页面是要抓取该页面得一个html文件,那我要是在网络上输入帐号密码登陆呢? 是不是内存的东西要写到该服务器上呢,所以当然是out了!
同样socket编程用到更多的IO,这里分别用Server(服务器端)和Client(客户端)来说明
Server: 遇到请求,网络----->内存 IN 服务器应答, 内存------->网络 OUT
-----------------------------------------------------------------
Client: 请求服务, 内存----->网络 OUT 服务器应答, 网络------->内存 IN
3、分步解析(根据实际调用顺序)
Manifest联网请求:
<uses-permission android:name="android.permission.INTERNET"/>
1、初始化Socket
初始化Socket时需要在一个Runnable中创建
Runnable runnable=new Runnable() {
@Override
public void run() {
try {
Socket socket1=new Socket("192.168.123.101",5000);
} catch (IOException e) {
e.printStackTrace();
}
}
};
2、运行Runnable对象
new Thread(runnable).start();
这时候已经初始化完毕,运行成功了
3、读取
此方法需配合线程使用,否则只会读取一次,建议创建一个新的线程继承类
InputStream inputStream = socket.getInputStream();
byte[] buffer = new byte[512];
inputStream.read(buffer);
str = new String(buffer,"UTF-8").trim();
4、读取完毕后将str传送给Message
Message自我介绍:Handler接收与处理的消息对象
Message message = Message.obtain();
message.obj=str;
handler.sendMessage(message);
5、显示(Handler+handleMessage内处理传入的消息)
Handler自我介绍:作用就是发送与处理信息
如果希望我正常工作,在当前线程中要有一个Looper对象
super.handleMessage(msg);
//(Textview对象).append("\n\n接收到的信息:"+(msg.obj).toString());
System.out.println((msg.obj).toString());
//提示
/*
Handler handler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
}
};
*/
6、发送
//初始化byte数组用于存放准备发送的信息
byte [] sendBuffer = "发送了一条信息".getBytes("UTF-8");
//获得socket的文件输出流
OutputStream outputStream = socket.getOutputStream();
//将sendBuffer写入输出流中,用于输出
outputStream.write(sendBuffer);
//发送成功!
7、最后一句,有话要说
所述的发送与连接一样,都需要线程支持,
所述的三个方法:连接,接收,发送都需要相关的Thread支持,
例如Recieve采用的是线程继承类,
而Send与Connect将都用于new Thread().start的启动
以上将为Socket实例,但不仅仅是最佳实例,可做参考,有更好的请自行更改
//最后,send的调用方法为
new Thread(Send).start();
4、具体实例
消息过长怎么办?
消息过长时考虑在xml的(消息框:Textview)中加入scrollbars
<TextView ... android:scrollbars="vertical"/>
MainActivity中设置MovemenrMethod:
(Textview对象).setMovementMethod(ScrollingMovementMethod.getInstance());
Manifest联网请求:
<uses-permission android:name="android.permission.INTERNET"/>
连接:
Runnable Connect=new Runnable() {
@Override
public void run() {
Socket socket = new Socket(serverIP,serverPort);
Boolean isConnect=true;
Boolean isReceive=true;
Receive receive = new Receive(socket);
receive.start();
System.out.println("----connected success----");
}
};
Handler:
Handler handler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
chatItem.append("\n\n接收到的信息:"+(msg.obj).toString());
System.out.println((msg.obj).toString());
}
};
构建接收函数(Receive):
private class Receive extends Thread{//继承线程类
private InputStream inputStream=null;
Receive(Socket socket){
//初始化输入流
inputStream = socket.getInputStream();
}
@Override
public void run() {
while (isReceive){
byte[] buffer = new byte[512];
inputStream.read(buffer);
String str = new String(buffer,"UTF-8").trim();
Message message = Message.obtain();
message.obj=str;
handler.sendMessage(message);
}
}
}
构建发送函数(Send):
Runnable Send=new Runnable() {
@Override
public void run() {
byte [] sendBuffer=null;
//可替代为接收输入框的字符串.getBytes
sendBuffer="发送了一条信息".getBytes("UTF-8");
OutputStream outputStream = socket.getOutputStream();
outputStream.write(sendBuffer);
}
};
开始方法:
//连接方法
new Thread(Connect).start();
//发送方法
//btn.setOnClickListener:new Thread(Send).start();
new Thread(Send).start();