【專案原始碼】- 【區域網聊天】android實現區域網聊天 - UDP實現

溫訊春風發表於2016-05-03

總結下自己前階段學習的區域網聊天,鞏固下知識。這個乃是作者的開山之作,大家隨便看看就好。


補上效果圖:


其中主要採用的UDP協議,其中涉及的知識點主要有,資料庫的操作:SQLite,SharedPreference ,Udp協議,字串資料流的寫入讀取,廣播等


不過這個實現的功能比較少,只能文字聊天,由於區域網聊天的侷限性,所以作者就沒有打算繼續深入的研究,大概瞭解下實現的原理。


知道UPD協議的,就不難理解其中的知識。


過程是這樣的:

在開啟程式的時候就先弄一個接收的服務,在整個程式的執行當中,這個服務就一直開著,這樣就可以一直接收訊息。由於只實現的是文字的傳送接受,所以我們只需要解析成字串就可以達成目的。


但聊天得有聊天聯絡人的列表,那怎樣才能實現聊天聯絡人列表呢,這裡主要靠192.168.1.255這個ip地址來實現,因為這個地址是群發的地址,在路由器上的所有主機都能接受所以我們只要往這個地址上發訊息,那麼接受到訊息的人自然就知道這個發訊息的人是線上的。


知道這個原理,那麼我們就想到了弄一個執行緒,隔斷的時間就群發訊息,用來判別是否線上。


但作者在聊天介面上有一個群聊的功能卻會與這個產生衝突,那怎麼解決這個衝突呢,很簡單,在判斷線上的執行緒裡傳送的訊息用一些不常用的隨便字串就能輕鬆解決問題,但接收訊息的時候判別下接收的字串,如果是的話自然就不用載入到聊天內容當中,自然就解決了。


另外,如果要實現聲音 和 圖片的傳輸,由於作者的能力有限,暫時想到的辦法是在開另外的埠來實現,這樣就不會與接受文字的埠產生衝突。


接受服務部分的程式碼:(可能程式碼比較亂,由於作者旨在學習知識點所以,花在註釋上的時間就少了點)

package com.example.administrator.canchatdemo.util;


import android.content.Context;
import android.content.Intent;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;

import com.example.administrator.canchatdemo.R;
import com.example.administrator.canchatdemo.activity.MainActivity;

import java.io.*;
import java.net.*;
import java.sql.Connection;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by Administrator on 2016/4/12.
 * 接受服務
 */

public class CanChatUdpReceiver extends Thread {

    //儲存聊天記錄
    public static List<MessageInfo> messageInfos = new ArrayList<MessageInfo>();

    public static List<ListContactInfo> listContactInfos = new ArrayList<ListContactInfo>();

    //儲存聊天資料庫
    private MsgSQLiteOpenHelper helper;
    //資料庫表message操作例項化
    private MessageDao messageDao;

    private int port;
    //停止標誌
    private boolean flag = true;

    private DatagramSocket da = null;

    private Context context;

    public CanChatUdpReceiver(Context context,int port){
        this.context = context;
        this.port = port;

        //context.deleteDatabase("message.db");//刪除資料庫初始化
        //建立資料庫
        helper = new MsgSQLiteOpenHelper(context);
        //資料庫表message操作例項化
        messageDao =new MessageDao(helper);

        List<MessageInfo> msgInfos = messageDao.findAll();
        if(msgInfos.size() > 50) {//提取一部分資料庫內容
            for (int i = msgInfos.size()-40; i < msgInfos.size(); i++) {
                messageInfos.add(msgInfos.get(i));
            }
        }else{
            for (int i = 0; i < msgInfos.size(); i++) {
                messageInfos.add(msgInfos.get(i));
            }
        }

        //初始化列表中的群聊資料
        ListContactInfo listContactInfo = new ListContactInfo();

        Calendar calendar = Calendar.getInstance();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm");
        String time =simpleDateFormat.format(calendar.getTime());

        listContactInfo.setListName("區域網群聊");
        listContactInfo.setListImage(R.drawable.dml);
        listContactInfo.setListIp(MainActivity.ipToAll);
        listContactInfo.setListTime(time);
        listContactInfo.setListUnRead("1");

        if(listContactInfos.size()< 1)
            listContactInfos.add(listContactInfo);
    }

    public void run(){
        try{
            if(da == null)
                da = new DatagramSocket(port);
            while (flag){
                byte[] buf = new byte[1024];
                DatagramPacket dp = new DatagramPacket(buf,buf.length);
                da.receive(dp);
                String ip = dp.getAddress().getHostAddress();
                String data = new String(dp.getData(),0,dp.getLength());

                //資料分析開始 判斷資料型別

                if(data.contains(Udp.CHECKED_CODE)){
                    //判斷線上人數部分//判斷線上人數部分
                    //取出 剔除判斷碼部分 取出列表資訊
                    String listInfoData = data.substring(Udp.CHECKED_CODE.length(),data.length());
                    //資料解析提取
                    boolean isAdd = true;
                    ListContactInfo newListContactInfo = getListContactInfo(listInfoData);
                    for (int i =0;i<listContactInfos.size();i++){
                        ListContactInfo listContactInfo = listContactInfos.get(i);
                        if(listContactInfo.getListIp().equals(ip)){

                            listContactInfo.setListName(newListContactInfo.getListName());
                            listContactInfo.setListName(newListContactInfo.getListName());
                            listContactInfo.setListImage(newListContactInfo.getListImage());

                            isAdd =false;
                            break;
                        }
                    }

                    //聯絡人列表資料加入
                    if(isAdd) {
                        newListContactInfo.setListIp(ip);
                        listContactInfos.add(newListContactInfo);
                        Log.e("列印提示資料:", "列表統計數量:" + listContactInfos.size()+" ip:" + ip);
                    }

                }else{
                    //訊息資料解析部分
                    MessageInfo messageInfo =getMessageInfo(data);
                    messageInfo.setUserIp(ip);
                    messageInfos.add(messageInfo);
                    //傳送新訊息廣播
                    Intent intent = new Intent();
                    intent.putExtra("listName",messageInfo.getListName());
                    intent.putExtra("userName",messageInfo.getUserName());
                    intent.putExtra("userIp",messageInfo.getUserIp());
                    intent.putExtra("imageId",messageInfo.getImageId());
                    intent.putExtra("msgBody",messageInfo.getMsgBody());
                    intent.putExtra("receTime",messageInfo.getReceTime());
                    intent.setAction("com.ADD_VIEW");
                    context.sendBroadcast(intent);

                    //儲存資料到資料庫
                    saveMessageToSQL(messageInfo);
                }

            }
        }catch(Exception e){
            Log.i(e.getMessage(),"伺服器掛了");
        }finally {
            try{
                if(da !=null)
                    da.close();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }

    //訊息資料處理
    private MessageInfo getMessageInfo(String data){
        String[] datas = data.split("##");
        MessageInfo messageInfo = new MessageInfo();

        messageInfo.setListName(datas[0]);
        messageInfo.setUserName(datas[1]);
        messageInfo.setImageId(Integer.parseInt(datas[2]));
        messageInfo.setMsgBody(datas[3]);
        messageInfo.setReceTime(datas[4]);

        return messageInfo;
    }

    //聯絡人列表資料處理
    private ListContactInfo getListContactInfo(String data){
        //解析資料data
        String[] datas = data.split("##");

        //
        ListContactInfo listContactInfo = new ListContactInfo();
        Calendar calendar = Calendar.getInstance();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm");
        String time =simpleDateFormat.format(calendar.getTime());

        listContactInfo.setListTime(time);
        listContactInfo.setListUnRead("1");
        listContactInfo.setListName(datas[0]);
        listContactInfo.setListImage(Integer.parseInt(datas[1]));

        return listContactInfo;
    }



    //儲存訊息資料到資料庫
    private void saveMessageToSQL(MessageInfo messageInfo){
        try {
            String userName = messageInfo.getUserName();
            String listName = messageInfo.getListName();
            String userIp = messageInfo.getUserIp();
            int imageId = messageInfo.getImageId();
            String msgBody = messageInfo.getMsgBody();
            String receTime = messageInfo.getReceTime();

            messageDao.add(listName,userName,userIp,imageId,msgBody,receTime);
            Log.e("資料庫操作提示:", "儲存成功");
        }catch (Exception e){
            Log.d(e.getMessage(),"儲存失敗");
        }
    }
}

以上程式碼中有很多問題就是,大家瞭解下實現的方法就好,由於傳送的內容中包含文字資訊比較多,所以作者只用##來分割,所以當傳送訊息時含有##時就會傳送出錯。傳送的訊息內容主要包括使用者名稱,聊天列表名,使用者ip,頭像的地址,訊息的內容,和接受的時間。


這裡有個列表名和使用者名稱是不一樣的概念,由於作者的資料庫只有一個,所有的聊天資訊都在一個集合當中,這樣在提取資料的時候,由於聊天列表裡有個群聊,在區分分組時採用使用者名稱就會衝突,所以匯入資料時,按的是列表名來匯入,這樣就可以區分資訊。


以上程式碼中還用到了廣播,廣播的用途旨在提示新訊息,這樣可以通知主執行緒資料變化,更新檢視。


傳送訊息的程式碼如下:

package com.example.administrator.canchatdemo.util;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/**
 * Created by Administrator on 2016/4/13.
 *  * Upd傳送資料
 */



public class CanChatUdpSend implements Runnable {
    //傳送的資料文字
    private String data;

    private int port;

    private String ip;

    public CanChatUdpSend(String data,String ip,int port){
        this.data = data;
        this.port = port;
        this.ip = ip;
    }
    public void run() {
        DatagramSocket ds = null;
        try{
            ds = new DatagramSocket();
            byte[] buf = data.getBytes();
            DatagramPacket dp = new DatagramPacket(buf,buf.length, InetAddress.getByName(ip),port);
            ds.send(dp);
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            try{
                if(ds != null)
                    ds.close();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
}

傳送訊息的程式碼就簡單了,只要有ip地址,埠,和內容就好,只管傳送,不用對接受的內容進行處理。


在ip地址中有個問題就是在進入程式中時要判斷自身的ip地址,由於ip地址有可能是192.168.1.1也有可能是192.168.0.1,所以只有知道了自己的ip地址才能得到正確的ip。不過這裡有個小問題,在電腦上JAVA獲取ip地址的方法和手機上的獲取方法有點不同,用電腦上的獲取方法得到的ip地址為127.0.0.1,所以採用瞭如下的程式碼來獲取。

package com.example.administrator.canchatdemo.util;

import android.util.Log;
import android.widget.Toast;

import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.Enumeration;

/**
 * Created by Administrator on 2016/4/14.
 * 定義常量
 */
public class Udp {
    //定義單獨埠
    public static final int PORT_OWN = 52000;
    //定義群聊埠
    public static final int PORT_ALL = 51000;



    public static final String CHECKED_CODE = "check_code_123456789";


    //獲取255ip
    public static String getIpToAll(){
        try {
            String ip = getIp();
            if(ip == null)
                return  null;
            return getIp().substring(0, 10) + "255";
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    //獲取本地ip
    public static String getIp(){
        try{
            for(Enumeration<NetworkInterface> en= NetworkInterface.getNetworkInterfaces();en.hasMoreElements();){
                NetworkInterface intf = en.nextElement();
                for(Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses();enumIpAddr.hasMoreElements();){
                    InetAddress inetAddress = enumIpAddr.nextElement();
                    if(!inetAddress.isLoopbackAddress()&&inetAddress instanceof Inet4Address){
                        return inetAddress.getHostAddress().toString();
                    }
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }
}


剩下的就是一些佈局的操作之類的東西了吧。

在此貼上自己的原始碼,寫的不好勿噴,謝謝。

http://download.csdn.net/detail/wduj123/9508473



相關文章