面向UDP的Android——PC双向通信(二):实现Android客户端和PC服务器端的双向通信

实现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() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
				try {
					
					sendMsg();		//发送消息
					
				} catch (Exception e1) {
					e1.printStackTrace();
				}
			}
		});
		
		frame.add(IPLabel);		//源IP
		frame.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服务器端之间传输自定义对象

你可能感兴趣的:(网络通信)