Android Socket通信(四) -- UDP与TCP结合传输数据
系列文章:Android Socket通信(一)– 初识与相遇Android Socket通信(二) --UDP,单播,广播和多播(组播)Android Socket通信(三) – TCP 配置和传递基础数据本系列代码链接:https://github.com/LillteZheng/SocketDemo在前面几章中,我们已经学习了 tcp 和 udp 基础的方法;...
系列文章:
Android Socket 系列更新计划
Android Socket通信(一) – 初识与相遇
Android Socket通信(二) --UDP,单播,广播和多播(组播)
Android Socket通信(三) – TCP 配置和传递基础数据
Android Socket通信(四) – UDP与TCP结合传输数据
Android Socket通信(五) – 实现一个多人聊天室
工程连接 : https://github.com/LillteZheng/SocketDemo
在这篇文章中,你将学习到:
- 学习通过 UDP 获取不同设备的ip和端口
- 通过 TCP 进行相互通信
- 实践一个案例
今天要实现的效果:
一、案例分析
在前面几章中,我们已经学习了 tcp 和 udp 基础的方法;这一章中,我们对它进行一个总结,设想一个案例,即我想与B设备相互通信;
但是并不知道B设备的ip和端口,但我们可以通过 udp 发送广播,找到设备 ip 和端口,再进行 tcp 通信;大概流程如下:
客户端端通过 udp 广播 搜索设备,如果A或者B设备对广播的协议有响应,则发送自己的ip和端口给客户端,客户端拿到之后,再与之进行双向通信。
二、进行UDP通过获取IP
在第二章已经学习了 UDP 广播的用法,代码改动不大,发送广播那里,我们进行一个数据包装,采用 ByteBuffer,毕竟我们不想让每个人都与之通信。
客户端中发送广播:
/**
* 发送广播
* @throws IOException
*/
public static void sendBroadcast() throws IOException {
DatagramSocket socket = new DatagramSocket();
ByteBuffer byteBuffer = ByteBuffer.allocate(128);
//发送特定数据
byteBuffer.putInt(Constans.CMD_BROAD);
byteBuffer.putInt(Constans.BROADCAST_PORT);
DatagramPacket packet = new DatagramPacket(byteBuffer.array(),
byteBuffer.position(),
InetAddress.getByName(Constans.BROADCAST_IP),
Constans.PORT);
socket.send(packet);
socket.close();
}
通过广播,发送 cmd 和 要回送的 port ;接着,在服务端中,解析相应数据:
ByteBuffer byteBuffer = ByteBuffer.wrap(bytes,0,length);
int cmd = byteBuffer.getInt();
int responsePort = byteBuffer.getInt();
System.out.println("客户端: " + ip + "\tport: " + port +"\t回送接口: "+responsePort);
/**
* 给客户端发送消息 cmd 必须匹配
*/
if (Constans.CMD_BROAD == cmd) {
ByteBuffer buffer = ByteBuffer.allocate(256);
buffer.putInt(Constans.CMD_BRO_RESPONSE);
buffer.putInt(Constans.TCP_PORT);
buffer.put(("我是设备: "+ UUID.randomUUID().toString()).getBytes());
System.out.println(buffer.position());
DatagramPacket receivePacket = new DatagramPacket(buffer.array(),
buffer.position(),
packet.getAddress(), //目标地址
responsePort); //广播端口
socket.send(receivePacket);
}
当 cmd 是 CMD_BROAD 才回送数据,然后把 tcp 的端口也回送回去;然后回到 client 中的 listener 去监听:
ByteBuffer buffer = ByteBuffer.wrap(bytes,0,length);
int cmd = buffer.getInt();
if (Constans.CMD_BRO_RESPONSE == cmd) {
int tcpPort = buffer.getInt();
int pos = buffer.position();
String msg = new String(bytes,pos,length - pos);
System.out.println("监听到: " + ip + "\ttcpPort: " + tcpPort + "\t信息: " + msg+" "+length+" "+pos);
if (msg.length() > 0) {
DeviceInfo device = new DeviceInfo(ip, tcpPort, msg);
devices.add(device);
}
//成功收取到一份
searchLatch.countDown();
}
打印如下:
三、进行 TCP 双向通信
拿到 TCP 的设备之后,就可以进行通信了,代码基于第一章。
首先,我们先改动 客户端,让发送和接收分开:
发送
/**
* 发送数据
* @param socket
*/
static void sendData(Socket socket){
try {
//终端输入流
BufferedReader osReader = new BufferedReader(new InputStreamReader(System.in));
PrintStream ps = new PrintStream(socket.getOutputStream());
boolean isFinish = false;
do {
String msg = osReader.readLine();
ps.println(msg);
if ("bye".equalsIgnoreCase(msg)){
isFinish = true;
}
}while (!isFinish);
osReader.close();
ps.close();
} catch (IOException e) {
e.printStackTrace();
}
}
接收
/**
* 监听数据
*/
static class ReaderListener extends Thread{
Socket socket;
boolean isFinish = false;
public ReaderListener(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
super.run();
try {
BufferedReader responseReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
do {
String response = responseReader.readLine();
//当服务器关闭连接了,则 IO 不再阻塞,会返回null
if (response == null){
System.out.println("连接断开");
break;
}else {
System.out.println(response);
}
}while (!isFinish);
responseReader.close();
} catch (IOException e) {
// e.printStackTrace();
}finally {
exit();
}
}
public void exit(){
isFinish =true;
if (socket != null){
try {
socket.close();
socket = null;
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
这样,我们就能监听服务端的信息了,接着,我们需要改变一下服务端的;当终端也输入时,发送给其他客户端。
这里,我们也把发送和接收分开:
为了能并发,我们这里都是采用线程去处理:
发送
/**
* 发送数据
*/
class writerHandle {
private PrintStream ps ;
private ExecutorService executorService ;
private boolean isFinish = false;
public writerHandle(OutputStream os) {
ps = new PrintStream(os);
executorService = Executors.newSingleThreadExecutor();
}
public void exit(){
isFinish = true;
ps.close();
executorService.shutdown();
}
public void sendMsg(String msg){
executorService.execute(new sendSync(msg));
}
class sendSync implements Runnable{
String str;
public sendSync(String str) {
this.str = str;
}
@Override
public void run() {
if (writerHandle.this.isFinish){
return;
}
ps.println(str);
}
}
}
接收:
/**
* 数据读取监听类
*/
class ReaderListener extends Thread{
InputStream inputStream;
boolean isFinish = false;
public ReaderListener(InputStream inputStream) {
this.inputStream = inputStream;
}
@Override
public void run() {
super.run();
try {
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
do {
String msg = br.readLine();
if (msg != null){
System.out.println("client: "+msg);
}else{
System.out.println("连接已断开");
break;
}
} while (!isFinish);
} catch (IOException e) {
// e.printStackTrace();
}finally {
exit();
}
}
public void exit(){
isFinish = true;
if (inputStream != null){
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
这样就完成了双向通信了。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)