实现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("发送成功");}}

可能会遇到的问题

客户端

  1. 安卓中子线程无法修改控件的值,所以需要使用handler处理机制
  2. 将接收消息的方法放到线程中,才不会导致主线程阻塞

服务器端

  1. 接收消息的方法也要放在子线程中调用并执行,否则尽管提示发送成功,但实际上是阻塞的,会影响消息的发送
  2. 消息的发送类似于Client端消息的发送,目标IP地址用request.getAddress()获取
  3. 创建发送数据报包时的目标端口号,需要和客户端用于接收时所指定的目标端口号一致,而并非简单的从request.getPort()得到的值作为目标端口号,因为客户端用于发送和接收的端口可能不是同一端口

本来放假了想偷懒,但是时间一久,有些细节果然记得不清晰了,还是得及时记录呀!
接下来打算制定协议,在现有的基础下传输一个自定义类的对象。留着第三篇来写吧!
面向UDP的Android——PC双向通信(三):在Android客户端和PC服务器端之间传输自定义对象

更多相关文章

  1. Android的消息机制
  2. Android异步消息框架
  3. Android异步处理三:Handler+Looper+MessageQueue深入详解
  4. Android异步处理三:Handler+Looper+MessageQueue深入详解
  5. Android(安卓)* HandleMessage的模型
  6. 大话Android的消息机制(Handler、Looper、Message...)
  7. Android的消息推送系列之消息推送原理
  8. Android(安卓)的消息队列模型
  9. Android的消息机制源码分析

随机推荐

  1. android语音即时通讯之录音、播放功能实
  2. Android9.0万年历毕业设计H5小应用webvie
  3. 杂记-Android(安卓)Studio 2.2 新功能
  4. TTF字体库系列文章1 —— Android使用ttf
  5. 这篇 Widget 秘籍,让你的 iPhone 下拉通知
  6. 解密:为什么Android机没有iPhone流畅呢?
  7. Android常见的八种导致 APP 内存泄漏的问
  8. Android蓝牙完全学习手册
  9. Android监听ScrollView的滚动事件
  10. Android(安卓)阅读 Office 文档,所有你能