UDP通信简单 小结

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UDP通信简单 小结相关的知识,希望对你有一定的参考价值。

android手机版和电脑版 效果图: 通过WiFi局域网 电脑和手机连接通信. 电脑版本和手机版本使用了相同的消息发送头协议, 可以相互接收消息. 若有做的不好的地方还希望大家指导一下.

 

1. 手机版

添加权限 AndroidManifest.xml

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
View Code

页面 activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:orientation="vertical" >

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content" >

            <EditText
                android:id="@+id/editText1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:ems="10" >

                <requestFocus />
            </EditText>

            <Button
                android:id="@+id/button1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="连接" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content" >

            <EditText
                android:id="@+id/editText2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:ems="10" />

            <Button
                android:id="@+id/button2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="发送" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content" >

            <TextView
                android:id="@+id/textView1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Large Text"
                android:textAppearance="?android:attr/textAppearanceLarge" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical" >

            <ScrollView
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:fadingEdge="vertical"
                android:scrollbars="vertical" >

                <TextView
                    android:id="@+id/textView2"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:text="Large Text"/>
            </ScrollView>
        </LinearLayout>
    </LinearLayout>

</RelativeLayout>
View Code

后台代码 MainActivity.java

package com.example.udppacket;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.view.Window;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import com.java.net.IPUtils;
import com.java.net.MessageEntity;
import com.java.net.UdpEntitiy;
import com.java.net.UdpEntitiy.ReceivesListener;

public class MainActivity extends ActionBarActivity {

    EditText ip;
    EditText send;
    TextView txt;
    TextView msgs;

    UdpEntitiy udp;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        this.requestWindowFeature(Window.FEATURE_NO_TITLE);

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ip = (EditText) findViewById(R.id.editText1);
        send = (EditText) findViewById(R.id.editText2);
        txt = (TextView) findViewById(R.id.textView1);
        msgs = (TextView) findViewById(R.id.textView2);

        send.setText("Hello Word!");
        txt.setText("");
        msgs.setText("");
        ip.setText(IPUtils.getHostIP());

        udp = UdpEntitiy.getUdpEntitiy();

        /**
         * 监听消息
         * */
        udp.onReceiveMessage(new ReceivesListener() {

            @Override
            public void onReceiveMessage(MessageEntity messageEntity) {
                // TODO Auto-generated method stub

                Message msg = new Message();
                msg.obj = messageEntity;
                msgHd.sendMessage(msg);
            }
        });
        // 连接
        findViewById(R.id.button1).setOnClickListener(
                new View.OnClickListener() {

                    @Override
                    public void onClick(View v) {
                        // TODO Auto-generated method stub
                        if (!udp.isReceive()) {
                            udp.startReceive();
                            Toast.makeText(MainActivity.this, "已连接!", Toast.LENGTH_SHORT).show();
                        }else {
                            udp.stopReceive();
                            Toast.makeText(MainActivity.this, "已关闭!", Toast.LENGTH_SHORT).show();
                        }
                    }
                });
        // 发送
        findViewById(R.id.button2).setOnClickListener(
                new View.OnClickListener() {

                    @Override
                    public void onClick(View v) {
                        // TODO Auto-generated method stub
                        if (!send.getText().toString().equals("")) {
                            udp.send(ip.getText().toString(), send.getText().toString());
                            msgs.append("我 : " + send.getText().toString() + "\\n");
                            send.setText("");
                        }else {
                            Toast.makeText(MainActivity.this, "请输入消息!", Toast.LENGTH_SHORT).show();
                        }
                    }
                });
    }

    Handler msgHd = new Handler() {

        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            MessageEntity me = (MessageEntity) msg.obj;
            if (me.type.equals("00")) {// 文字
                msgs.append("来自 [ " + me.sendIP + " ] :" + me.getString() + "\\n");

            }
        };
    };

}
View Code

以下是类库代码:

获取IP地址 IPUtils.java 可要可不要

package com.java.net;

import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;

import android.content.Context;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;

public class IPUtils {

    /***
     * 
     * <uses-permission android:name="android.permission.INTERNET"/>
        <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
     * /
    /**
     * 获取ip地址
     * @return
     */
    public static String getHostIP() {

        String hostIp = null;
        try {
            Enumeration nis = NetworkInterface.getNetworkInterfaces();
            InetAddress ia = null;
            while (nis.hasMoreElements()) {
                NetworkInterface ni = (NetworkInterface) nis.nextElement();
                Enumeration<InetAddress> ias = ni.getInetAddresses();
                while (ias.hasMoreElements()) {
                    ia = ias.nextElement();
                    if (ia instanceof Inet6Address) {
                        continue;// skip ipv6
                    }
                    String ip = ia.getHostAddress();
                    if (!"127.0.0.1".equals(ip)) {
                        hostIp = ia.getHostAddress();
                        break;
                    }
                }
            }
        } catch (SocketException e) {
            e.printStackTrace();
        }
        return hostIp;

    }


    /*
     **
     * 获取本机IPv4地址
     *
     * @param context
     * @return 本机IPv4地址;null:无网络连接
     */
    public static String getIpAddress(Context context) {
        // 获取WiFi服务
        WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
        // 判断WiFi是否开启
        if (wifiManager.isWifiEnabled()) {
            // 已经开启了WiFi
            WifiInfo wifiInfo = wifiManager.getConnectionInfo();
            int ipAddress = wifiInfo.getIpAddress();
            String ip = intToIp(ipAddress);
            return ip;
        } else {
            // 未开启WiFi
            return getIpAddress();
        }
    }

    private static String intToIp(int ipAddress) {
        return (ipAddress & 0xFF) + "." +
                ((ipAddress >> 8) & 0xFF) + "." +
                ((ipAddress >> 16) & 0xFF) + "." +
                (ipAddress >> 24 & 0xFF);
    }

    /**
     * 获取本机IPv4地址
     *
     * @return 本机IPv4地址;null:无网络连接
     */
    private static String getIpAddress() {
        try {
            NetworkInterface networkInterface;
            InetAddress inetAddress;
            for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements(); ) {
                networkInterface = en.nextElement();
                for (Enumeration<InetAddress> enumIpAddr = networkInterface.getInetAddresses(); enumIpAddr.hasMoreElements(); ) {
                    inetAddress = enumIpAddr.nextElement();
                    if (!inetAddress.isLoopbackAddress() && !inetAddress.isLinkLocalAddress()) {
                        return inetAddress.getHostAddress();
                    }
                }
            }
            return null;
        } catch (SocketException ex) {
            ex.printStackTrace();
            return null;
        }
    }

}
View Code

发送接收消息包装类库 :MessageEntity.java

package com.java.net;

import java.io.ByteArrayOutputStream;
import java.io.File;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;

/**
 * 标识 000000, 6位 类型00... , 00 :字符串, 01 :图片, 02 :文件 ... 2位(不足使用字母代替) 是否接收确认0 - 1
 * , 0 :不确认 ,1 :确认 1位 默认不确认0 是否为最后一个 0 - 1, 0 :不是最后一个, 1 :最后一个 1位 默认最后一个1
 * 数据byte[]
 * */
public class MessageEntity {
    // 初始化
    private void init() {
        flagNo = getRandom();// 6 位随机数
        type = "00";
        isPass = "0";
        isLast = "1";
    }

    public MessageEntity() {
        init();
    }
    public MessageEntity(String s){
        init();
        type = "00";//字符串
        datas = s.getBytes();
    }
    public MessageEntity(File s){
        init();
        type = "02";//文件
        //文件流转换
    }
    public MessageEntity(Bitmap s){
        init();
        type = "01";//Bitmap  图片
        datas = Bitmap2Bytes(s);
    }
    //根据字节获取图片
    public Bitmap getBitmap(){
        if (datas != null && datas.length > 0) {
            return Bytes2Bimap(datas);
        }
        return null;
    }
    //根据字节获取字符串
    public String getString(){
        if (datas != null && datas.length > 0) {
            return new String(datas, 0, datas.length);
        }
        return null;
    }
    //根据字节获取文件
    public String getFile(){
        if (datas != null && datas.length > 0) {
            //代码
            return null;
        }
        return null;
    }
    //获取头字节
    public byte[] getHeadToByte(){
        return (getFlagNo() + "," + type + "," + isPass + "," + isLast).getBytes();
    }
    //根据字节 重置头部参数
    public MessageEntity resetParameter(byte[] b, MessageEntity m){
        if (m == null) {
            m = new MessageEntity();
        }
        if (b != null && b.length > 0) {
            String s = new String(b, 0, b.length);
            resetParameter(s,m);
        }
        return m;
    }
    //根据字节 重置头部参数
    public MessageEntity resetParameter(String b, MessageEntity m){
        String[] ss = b.split(",");
        if (ss.length >= 4) {
            m.flagNo = ss[0];
            m.type = ss[1];
            m.isPass = ss[2];
            m.isLast = ss[3];
        }
        return m;
    }
    private String flagNo;// 消息编号
    public String getFlagNo(){
        return flagNo;
    }
    public String type;// 消息类型
    public String isPass;// 是否确认
    public String isLast;// 是否为最后一条
    public boolean isReader;//消息是否已读
    public byte[] datas;// 数据字节
    public String sendIP;// 发送方 IP
    public int sendPort;// 发送方 端口
    // 字节转为为图片
    private Bitmap Bytes2Bimap(byte[] b) {
        if (b.length != 0) {
            return BitmapFactory.decodeByteArray(b, 0, b.length);
        } else {
            return null;
        }
    }
    // 将图片转为字节
    private byte[] Bitmap2Bytes(Bitmap bm) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        bm.compress(Bitmap.CompressFormat.PNG, 100, baos);
        return baos.toByteArray();
    }
    //产生随机数并凑够6位
    private String getRandom() {
        int r = (int) (Math.random() * 1000000);
        String a = Integer.toString(r);
        return getFormatString(a, 6);
    }
    private String getFormatString(String s, int n) {
        n = n - s.length();
        for (int i = 0; i < n; i++) {
            s = "0" + s;// 为了凑够6位数
        }
        return s;
    }
}
View Code

管理消息类(主要是针对发送消息过大,分批发送使用) : MessageManage.java

package com.java.net;

import java.util.ArrayList;
import java.util.List;

/**
 * 消息管理
 * */
public class MessageManage {

    private MessageManage(){
        list = new ArrayList<MessageEntity>();
    }
    private static MessageManage msgm = new MessageManage();
    public static MessageManage getMessageManage(){
        return msgm;
    }
    private static List<MessageEntity> list;
    //添加
    public void add(MessageEntity me){
        if (list.size() > 0) {
            for (int i = 0; i < list.size(); i++) {
                MessageEntity dd = list.get(i);
                if (dd.getFlagNo().equals(me.getFlagNo())) {
                    dd.datas = addBytes(dd.datas,me.datas);
                    return;
                }
            }
        }
        //新添加时, 如果数量大于 max则移除所有
        if (size()>MaxSize) {
            for (int i = 0; i < list.size(); i++) {
                MessageEntity dd = list.get(i);
                if (dd.isReader) {
                    list.remove(i);//移除已读消息
                }
            }
        }
        list.add(me);
    }
    final int MaxSize = 500;//如果大于500条则自动清理
    //读取消息
    public MessageEntity reader(MessageEntity me){
        for (int i = 0; i < list.size(); i++) {
            MessageEntity dd = list.get(i);
            if (dd.getFlagNo().equals(me.getFlagNo())) {
                //设置为已读
                dd.isReader = true;
                return dd;
            }
        }
        return null;
    }
    public static int size(){
        return list.size();
    }
    //合并两个字节数组
    public static byte[] addBytes(byte[] data1, byte[] data2) {
        byte[] data3 = new byte[data1.length + data2.length];
        System.arraycopy(data1, 0, data3, 0, data1.length);
        System.arraycopy(data2, 0, data3, data1.length, data2.length);
        return data3;

    }
}
View Code

UDP端口发送监听类 UdpEntitiy.java 用于发送消息和监听消息

package com.java.net;

import java.io.File;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EventListener;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import android.graphics.Bitmap;
import android.os.Message;


public class UdpEntitiy{

    private UdpEntitiy(){
    }
    private static UdpEntitiy udp = new UdpEntitiy();
    public static UdpEntitiy getUdpEntitiy(){
        return udp;
    }
    private int port = 12345;//监听端口
    private int getPortSend() {//临时端口 发送时使用
        return (port + 1);
    }
    public int getPort() {
        return port;
    }
    public void setPort(int port) {
        this.port = port;
    }
    private boolean isReceive;//是否监听
    public boolean isReceive() {
        return isReceive;
    }
    public void startReceive(){
        if (!isReceive) {//如果没有监听
            new Thread(){
                public void run() {
                    receiveMessage();
                };
            }.start();
        }
    }
    //发送消息
    public void send(final String ip,final MessageEntity ms){
        new Thread(){
            public void run() {
                sendMessage(ip,ms);
            };
        }.start();
    }
    //发送文字
    public void send(final String ip,String s){
        final MessageEntity ms = new MessageEntity(s);
        send(ip,ms);
    }
    //发送图片
    public void send(final String ip,Bitmap s){
        final MessageEntity ms = new MessageEntity(s);
        send(ip,ms);
    }
    //发送文件
    public void send(final String ip,File s){
        final MessageEntity ms = new MessageEntity(s);
        send(ip,ms);
    }
    public void stopReceive(){
        if (isReceive) {//如果监听
            socket.close();
        }
    }
    //接收消息接口
    public interface ReceivesListener extends EventListener{
        //当接收到消息时
        public void onReceiveMessage(MessageEntity messageEntity);
    }
    private Collection listeners = new HashSet();
    public void onReceiveMessage(ReceivesListener receiveListener){
        if (listeners == null) {
            listeners = new HashSet();
        }
        listeners.add(receiveListener);
    }
    private void notifyListener(MessageEntity s){
        if (listeners != null) {
            Iterator iter = listeners.iterator();
            while (iter.hasNext()) {
                ReceivesListener r = (ReceivesListener) iter.next();
                r.onReceiveMessage(s);
            }
        }
    }
    
    DatagramSocket socket = null;//声明 数据报套接字
    final int VALUE_MAX = 2048;//接收发送最大字节 2kb
    LinkedList flags = new LinkedList();
    MessageManage msgm = MessageManage.getMessageManage();
    //监听数据接收
    private void receiveMessage(){
        try {
            socket = new  DatagramSocket(port);//初始化 数据报套接字
            isReceive = true;//设置状态 - 已经在监听
            while (true) {
                byte data[] = new byte[VALUE_MAX];//接收存放的字节
                DatagramPacket packet = new DatagramPacket(data, data.length);//声明初始化接收的数据包
                //阻塞监听
                socket.receive(packet);
                //接收到端口数据消息
                MessageEntity me = new MessageEntity();
                //截获返回符
                if (packet.getLength() == me.getFlagNo().length()) {
                    flags.add(new String(packet.getData(),packet.getOffset(),packet.getLength()));
                    continue;
                }
                me.sendIP = packet.getAddress().getHostAddress();
                me.sendPort = packet.getPort();
                //设置头部信息
                int headSize = me.getHeadToByte().length;
                me.resetParameter(new String(packet.getData(),packet.getOffset(),headSize), me);
                //获取有效字节
                byte ndata[] = new byte[packet.getLength()-headSize];
                System.arraycopy(packet.getData(),headSize,ndata,0,ndata.length);
                me.datas = ndata;//赋值
                //添加到消息中
                msgm.add(me);
                if (me.isLast.equals("1")) {//最后一个
                    me = msgm.reader(me);
                    notifyListener(me);//通知
                }
            }
        } catch (SocketException e) {
            //socket 关闭是会抛异常, 才可以停止消息阻塞
        } catch (IOException e) {
        }finally{
            isReceive = false;
            if (socket!=null

以上是关于UDP通信简单 小结的主要内容,如果未能解决你的问题,请参考以下文章

qt在windows下的udp通信(最简单)

QT下UDP套接字通信——QUdpSocket 简单使用

170411java Socket通信的简单例子(UDP)

linux网络编程之用socket实现简单客户端和服务端的通信(基于UDP)

与某些接口的 UDP 通信(使用 UDP Echo 示例代码,main.m 有问题)

Node.js权威指南 - 实现基于TCP与UDP的数据通信