总结下自己前阶段学习的局域网聊天,巩固下知识。这个乃是作者的开山之作,大家随便看看就好。
补上效果图:
其中主要采用的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