面向UDP的Android——PC双向通信(二):实现Android客户端和PC服务器端的双向通信
16lz
2021-01-23
实现Android客户端和PC服务器端的双向通信
- 导语
- Android客户端
- Java服务器端
- 示例代码
- 客户端代码
- 服务器端代码
- 可能会遇到的问题
- 客户端
- 服务器端
导语
上篇博客中,面向UDP的Android——PC双向通信(一):实现Android客户端和PC服务器端的单向通信,我们介绍了如何实现Android客户端向Java服务器端发送消息,这次我们尝试实现两者之间的双向通信。
我们需要实现的是,对于Android客户端,增加接收信息的方法,对于Java服务器端,增加发送消息的方法。
Android客户端
我们需要增加的是接收消息的方法,接收到消息后,将消息显示在页面上。
接收消息的代码与上篇博客Java服务器端接收消息的代码类似:
public DatagramSocket socket;public void receiveMsg(){try{//创建socket对象socket = new DatagramSocket(9999);while(true){byte data[] = new byte[1000];//创建数据报包DatagramPacket request = new DatagramPacket(data,data.length);//接收数据报包socket.receive(request);String s = new String(data,"GBK");/*将数据显示在页面上*/......}} catch(Exception e){e.printStackTrace();}}
由于接收消息是在子线程中进行的,但组件只能在主线程中进行修改,所以需要用到Android特有的消息处理机制handler机制。
先创建Handler对象,重写handleMessage(Message msg)方法:
private Handler handler = new Handler(){public void handlerMessage(Message msg){String s = (String) msg.obj;TextReceive.setText(s);}}
再在接收消息的方法中增加发送消息到主线程消息队列的代码即可:
Message message = handler.obtainMessage();message.obj = s;handler.sendMessage(message);
Java服务器端
Java服务器端需要增加发送消息的代码,和Android客户端增加消息的代码类似。
/** * 发送消息 * @throws Exception */public void sendMsg() throws Exception{System.out.println("准备发送消息");String Msg = MsgSend.getText();System.out.println("要发送的消息是: "+Msg);byte data[] = Msg.getBytes("GBK");DatagramPacket request = new DatagramPacket(data,data.length,ClientAddress,9999);socket.send(request);System.out.println("发送成功");}
示例代码
客户端代码
package com.myscelyn.udpclient;import android.os.Handler;import android.os.Message;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.util.Log;import android.view.View;import android.widget.Button;import android.widget.EditText;import android.widget.TextView;import android.widget.Toast;import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.InetAddress;public class MainActivity extends AppCompatActivity { public DatagramSocket socket; public EditText TextSend; public TextView TextReceive; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button BtnSend = this.findViewById(R.id.BtnSend); TextSend = this.findViewById(R.id.TextSend); TextReceive = this.findViewById(R.id.TextReceive); try{ socket = new DatagramSocket(9999); }catch(Exception e){ e.printStackTrace(); } BtnSend.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { final String msg = TextSend.getText().toString(); new Thread(){ public void run(){ sendMsg(msg); } }.start(); } }); new Thread(){ public void run(){ receiveMsg(); } }.start(); } public void receiveMsg(){ try{ while(true){ byte data[] = new byte[1000]; DatagramPacket request = new DatagramPacket(data,data.length); socket.receive(request); String s = new String(data,"GBK"); Message message=handler.obtainMessage(); message.obj=s; handler.sendMessage(message); } } catch(Exception e){ e.printStackTrace(); } } private Handler handler=new Handler(){ public void handleMessage(Message msg) { String s=(String)msg.obj; TextReceive.setText(s); } }; public void sendMsg(String msg){ Log.v("Client send:",msg); Toast.makeText(MainActivity.this,msg,Toast.LENGTH_LONG); try{ byte data[] = msg.getBytes("GBK"); InetAddress ServerAddress = InetAddress.getByName("192.168.31.233"); DatagramPacket request = new DatagramPacket(data,data.length,ServerAddress,9999); socket.send(request); } catch(Exception e){ e.printStackTrace(); } }}
服务器端代码
import java.awt.Dimension;import java.awt.FlowLayout;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.io.IOException;import java.io.UnsupportedEncodingException;import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.InetAddress;import javax.swing.JButton;import javax.swing.JFrame;import javax.swing.JLabel;import javax.swing.JTextArea;public class UDPServer {public JFrame frame;public JLabel IPShow;public JLabel PortShow;public JTextArea MsgReceive;public JTextArea MsgSend;public JButton SendBtn;public InetAddress ClientAddress;public String AddressStr;public int ClientPort;public DatagramSocket socket;public static void main(String[] args) throws Exception{UDPServer server = new UDPServer();server.showUI();server.receiveMsg();}public void showUI(){frame = new JFrame("UDP Server");frame.setSize(800, 700);frame.setLocationRelativeTo(null);frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setLayout(new FlowLayout());JLabel IPLabel = new JLabel(" 源IP:");IPLabel.setPreferredSize(new Dimension(80,30));IPShow = new JLabel("__________________");IPShow.setPreferredSize(new Dimension(300,30));JLabel PortLabel = new JLabel("端口:");PortLabel.setPreferredSize(new Dimension(80,30));PortShow = new JLabel("__________________");PortShow.setPreferredSize(new Dimension(300,30));JLabel RecvLabel = new JLabel("接收到消息:");RecvLabel.setPreferredSize(new Dimension(700,30));MsgReceive = new JTextArea();MsgReceive.setPreferredSize(new Dimension(750,200));JLabel SendLabel = new JLabel("待发送消息:");RecvLabel.setPreferredSize(new Dimension(700,30));MsgSend = new JTextArea();MsgSend.setPreferredSize(new Dimension(750,200));SendBtn = new JButton("发送");SendBtn.setPreferredSize(new Dimension(80,60));SendBtn.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {try {sendMsg();//发送消息} catch (Exception e1) {e1.printStackTrace();}}});frame.add(IPLabel);//源IPframe.add(IPShow);frame.add(PortLabel);//端口frame.add(PortShow);frame.add(RecvLabel);//接收到消息:frame.add(MsgReceive);frame.add(SendLabel);//待发送消息:frame.add(MsgSend);frame.add(SendBtn);//发送按钮frame.setVisible(true);}/** * 接收消息 * @throws Exception */public void receiveMsg() throws Exception{System.out.println("UDPServer start...");socket = new DatagramSocket(9999);new Thread(){public void run(){while(true){byte[] data = new byte[52];DatagramPacket request = new DatagramPacket(data, 52);System.out.println("准备接收消息");try {socket.receive(request);System.out.println("消息接收完毕");ClientAddress=request.getAddress();IPShow.setText(ClientAddress.toString());System.out.println("客户机IP地址:"+IPShow.getText());ClientPort=request.getPort();PortShow.setText(""+ClientPort);System.out.println("客户机端口:"+PortShow.getText());String s = new String(data,"GBK");System.out.println("收到新消息:"+s+"\n"+s.length());MsgReceive.setText(s);} catch (IOException e) {e.printStackTrace();}}}}.start();}/** * 发送消息 * @throws Exception */public void sendMsg() throws Exception{System.out.println("准备发送消息");String Msg = MsgSend.getText();System.out.println("要发送的消息是: "+Msg);byte data[] = Msg.getBytes("GBK");DatagramPacket request = new DatagramPacket(data,data.length,ClientAddress,9999);socket.send(request);System.out.println("发送成功");}}
可能会遇到的问题
客户端
- 安卓中子线程无法修改控件的值,所以需要使用handler处理机制
- 将接收消息的方法放到线程中,才不会导致主线程阻塞
服务器端
- 接收消息的方法也要放在子线程中调用并执行,否则尽管提示发送成功,但实际上是阻塞的,会影响消息的发送
- 消息的发送类似于Client端消息的发送,目标IP地址用request.getAddress()获取
- 创建发送数据报包时的目标端口号,需要和客户端用于接收时所指定的目标端口号一致,而并非简单的从request.getPort()得到的值作为目标端口号,因为客户端用于发送和接收的端口可能不是同一端口
本来放假了想偷懒,但是时间一久,有些细节果然记得不清晰了,还是得及时记录呀!
接下来打算制定协议,在现有的基础下传输一个自定义类的对象。留着第三篇来写吧!
面向UDP的Android——PC双向通信(三):在Android客户端和PC服务器端之间传输自定义对象
更多相关文章
- Android客户端程序员的一些思考
- Android的消息机制
- Android’s HTTP Clients(Android的HTTP客户端接口)
- Android异步消息框架
- 大话Android的消息机制(Handler、Looper、Message...)
- Android的消息机制源码分析
- Android的消息推送系列之消息推送原理
- Android 源码分析 - 消息处理机制