局域网实时通讯工具

    目前,在互联网上比较常见的聊天软件通常都将程序分成两个部分使用,即客户机程序和服务器程序。客户机程序在网上发布供用户使用而服务器程序则只供服务器使用,一般不公开。采用这种模式可以方便管理员对所有用户进行管理,但它必须单独提供一台计算机作为服务器。而对于在局域网内使用的聊天软件,由于计算机的数量有限,而且计算机之间的服务关系经常变化或者根本没有服务关系,采用这种模式就显得力不从心了。
    本程序在服务关系上做了作了一个新的设计,不再区分所为的服务器程序与客户端程序。当程序启动时,启动广播在线信息的线程,同时启动监听消息的线程。即用户输入用户名并打开在线用户的列表时,启动广播线程每隔一段时间便向局域网中的所有用户广播在线信息,同时启动监听其他主机发来的消息的线程,若接收到在线广播则将该用户添加到在线用户列表中。在进行消息通信时,通过指定端口发送消息,线程监听到其他用户发来的信息便提示用户查看。
    本程序将所有的代码文件按功能分为user、utils、window三个个功能包,它们实现的功能分别为:
    user包:
    程序运行时需要的临时用户信息。
    utils包:
    程序的通信功能实现及程序中用到的常量。
    window包:
    构建本程序的主要窗口。
    该程序工程的大致结构如图所示:


    效果图如下:


    广播的实现

package utils;

import user.UserInfo;

/*
 * 通过指定端口广播在线信息
 * 只广播用户名
 */
public class BroadCast implements Runnable{

	@Override
	public void run() {
		System.out.println("广播进程已打开!");
		SendPacket request = new SendPacket();
		while(true){
			if(request.postRequest(SendPacket.ONLINE_INFO,UserInfo.userName,Constant.BROADCAST_IP, Constant.BROADCAST_PORT,Constant.MSG_REC_PORT)){
				System.out.println("在线信息广播成功!");
			}else{
				System.out.println("在线信息发送失败!");
			}
			try {
				Thread.sleep(5000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

}


    发送数据包的实现

package utils;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

import user.UserInfo;

/*
 * 消息格式
 * massage type#username#massage
 */
public class SendPacket {
	
	private DatagramSocket dSocket;
	private DatagramPacket dPacket;
	public static final int ONLINE_INFO = 0;	//发送上线信息的首部
	public static final int OFFLINE_INFO = 1;	//发送下线信息的首部
	public static final int CONNECT_INFO = 2;	//发送消息的首部
	
	public boolean postRequest(int type ,String str ,String ip ,int port ,int toPort){
		byte[] data = null;
		
		try{
			System.out.println("将---" + str + "---发送至<" + ip + ":" + toPort + ">");
			dSocket = new DatagramSocket(port);
			//将用户名转换为byte[]
			switch(type){
			case ONLINE_INFO:
				data = (Constant.ONLINE_INFO + "#" + UserInfo.userName + "#" + str).getBytes();
				break;
			case OFFLINE_INFO:
				data = (Constant.OFFLINE_INFO + "#" + UserInfo.userName + "#" + str).getBytes();
				break;
			case CONNECT_INFO:
				data = (Constant.CONNECT_INFO + "#" + UserInfo.userName + "#" + str).getBytes();
				break;
			}
			System.out.println("-------------------发送数据:" + data);
			try{
				while(true){
					//封装userInfo广播信息
					dPacket = new DatagramPacket(data, 
										data.length, 
										InetAddress.getByName(ip), 
										toPort);
					//广播在线信息
					dSocket.send(dPacket);
					System.out.println("成功将---" + str + "---发送到" + ip);
					return true;
				}
			}catch(Exception e){
				e.printStackTrace();
				System.out.println("-请求发送失败!");
				return false;
			}
		}catch(Exception e){
			e.printStackTrace();
			System.out.println("-打开端口失败!");
			return false;
		}finally{
			data = "".getBytes();
			dPacket = null;
			dSocket.close();
		}
	}
	
}

    监听指定端口信息的实现

package utils;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.util.StringTokenizer;

import javax.swing.JOptionPane;

import window.W_Chatting;
import window.W_UserList;

/*
 * 用于不断指定端口号接收其他在线主机发来的在线信息
 * 并将在线信息放入ArrayList
 * 同时更新在线用户列表
 */
public class MassageListener implements Runnable {

	private DatagramSocket dSocket;

	public void run() {
		/*
		 * isInOnlineUserList = 0 不在在线列表中 
		 * isInOnlineUserList = 1 在在线列表中
		 * isInOnlineUserList = 2 用户名更新
		 */
		int isInOnlineUserList;
		int i = 0;
		System.out.println("接收在线信息进程已打开!");
		try {
			dSocket = new DatagramSocket(Constant.MSG_REC_PORT);
			try {
				System.out.println("正在接收在线信息!");
				// 将获取的数据保存到dPacket
				while (true) {

					byte[] dataBuf = new byte[Constant.MAX_MSG_SIZE + 2];
					DatagramPacket dPacket = new DatagramPacket(dataBuf, dataBuf.length);
					
					dSocket.receive(dPacket);
					System.out.println("-------------------接收到数据:" + dataBuf);
					String ip = dPacket.getAddress().getHostAddress();
					int port = dPacket.getPort();
					
					String dataPack = new String(dPacket.getData()).trim();
					StringTokenizer token = new StringTokenizer(dataPack ,"#");
					//分离消息
					String head = token.nextToken();
					String userName = token.nextToken();
					String massage = dataPack.substring((head + "#" + userName + "#").length()).trim();
					System.out.println("拦截到的首部 :" + head);
					System.out.println("拦截到的消息:" + massage);
					if (head.equals(Constant.ONLINE_INFO)) {
						/*
						 * 接收到上线信息
						 */
						System.out.println();
						System.out.println("来自ip为 " + ip + " <对方端口>: " + port
								+ " 的上线信息!");
						System.out.println(userName);
						isInOnlineUserList = 0;
						if (W_UserList.onlineUserArrayList.size() != 0) { // ArrayList不为空
							for (i = 0; i < W_UserList.onlineUserArrayList
									.size(); i++) {
								// 已添加ip的用户
								if (W_UserList.onlineUserArrayList.get(i).ipEquals(ip)
										&& W_UserList.onlineUserArrayList.get(i).userNameEquals(userName)) {
									isInOnlineUserList = 1;
									System.out.println(userName + "<ip:" + ip
											+ ">已经在您的好友列表中!");
								}else if(W_UserList.onlineUserArrayList.get(i).ipEquals(ip)
										&& (W_UserList.onlineUserArrayList.get(i).userNameEquals(userName) == false)) {
									isInOnlineUserList = 2;
								}
							}
						}
						if (isInOnlineUserList == 0) {
							W_UserList.addOnlineUser(userName, ip);
							System.out.println("添加新的在线好友!");
						} else if (isInOnlineUserList == 2) {
							// ip已添加,但用户名更改
							String str = userName + "<ip:" + ip + ">";
							// 修改好友列表信息
							W_UserList.modOnlineUser(
									W_UserList.onlineUserArrayList.get(i)
											.getDmtnUser(), str);
							// 修改ArrayList中的username
							W_UserList.onlineUserArrayList.get(i).setUserName(
									userName);
							System.out.println("<ip:" + ip + ">用户名更改为:"
									+ userName);
						}
					} else if (head.equals(Constant.OFFLINE_INFO)) {
						/*
						 * 接收到下线信息
						 */
						if (W_UserList.onlineUserArrayList.size() != 0) { // ArrayList不为空
							for (i = 0; i < W_UserList.onlineUserArrayList
									.size(); i++) {
								if (W_UserList.onlineUserArrayList.get(i)
										.ipEquals(ip)) {
									W_UserList.delOnlineUser(i);
								}
							}
						}
					} else if (head.equals(Constant.CONNECT_INFO)) {
						/*
						 * 接收到会话信息
						 */
						boolean flag = false;
						int j = 0;
						//检查是否已打开会话窗口
						for (j = 0; j < W_UserList.chattingUserArrayList
								.size(); i++) {
							if (W_UserList.chattingUserArrayList.get(j)
									.getUserIP().equals(ip)) {
								flag = true;
								break;
							}
						}
						// 尚未打开会话窗口
						if (!flag) {
							int choice = JOptionPane.showConfirmDialog(null,
									"是否打开新消息?" + userName + ":" + ip	+ "的消息!",
									"新信息!", 
									JOptionPane.YES_NO_OPTION,
									JOptionPane.ERROR_MESSAGE);
							//打开新信息
							if (choice == JOptionPane.YES_OPTION) {
								try{
									W_Chatting w_Chatting = new W_Chatting(userName ,ip);
									//设置w_Chatting为可见
									w_Chatting.setVisible(true);
									W_UserList.addChattingUser(userName ,ip ,w_Chatting);
									System.out.println("会话窗口成功打开!");
									w_Chatting.appendMsg(massage);
									
								}catch(Exception e1){
									e1.printStackTrace();
									System.out.println("会话窗口创建失败!");
								}
							}
						} else {// 已打开会话窗口
							W_UserList.chattingUserArrayList.get(j).getJfChatting().appendMsg(massage);
						}
					}
					dPacket = null;
					dataBuf = null;
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		} catch (SocketException e1) {
			e1.printStackTrace();
			System.out.println("在线信息接收失败!");
		} finally {
			dSocket.close();
		}
	}

}

    其他为界面的构建与逻辑的处理。

完整工程在此:http://download.csdn.net/detail/weizi4332/5675915

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值