一步一步写简易版飞鸽传书(四)

实现私聊

一、前言

本来,实现私聊和公聊是差不多的,只要再添加一个消息处理方法即可,可是这里为什么要把它单独拿出来成一篇呢?因为,我在这个过程中发现了两个重要的问题。是什么问题呢?我们先不说,先在上一篇的基础上实现私聊功能之后再慢慢揭露。


二、用户地址绑定错误问题

首先,ChatFrame 的按钮点击事件SendAction 的代码改成如下所示:


//定义发送消息按钮点击事件
		Action sendAction = new AbstractAction(){

			@Override
			public void actionPerformed(ActionEvent e) {
				// TODO 自动生成的方法存根
				
				SocketAddress destAddress = user.getAddress();
				System.out.println("ChatFrame_destAddress="+destAddress);
				//公聊“用户”,其address是null
				if(destAddress == null){
					System.out.println("destAddress == null");
					LoginFrame.comUtil.broadMsg(chatField.getText());
				}else{
					LoginFrame.comUtil.sendMsg(chatField.getText(), destAddress);
				}
				
				
			}	
		};

当我们发现是私聊消息时,就调用 LoginFrame.comUtil.sendMsg  方法。然后在消息处理类 MsgProcessor中增加一个私聊消息处理方法:

/**
	 * 处理私聊消息
	 * @param broaInPacket
	 * @param friendListFrame
	 */
	public void processSingleMsg(DatagramPacket broaInPacket, FriendListFrame friendListFrame){
		
		try {
			String msg = new String(broaInPacket.getData(), 0, broaInPacket.getLength(),ComUtil.CHARSET);
			SocketAddress destAddress = broaInPacket.getSocketAddress();
			System.out.println("processSingleMsg_destAddress"+destAddress);
			User toUser = friendListFrame.findUserByAddress(destAddress);
			ChatFrame toChatFrame = toUser.getChatFrame();
			if(toChatFrame == null){
				toChatFrame = new ChatFrame(toUser); 
				toUser.setChatFrame(toChatFrame);
			}
			if(!toChatFrame.isShowing()){
				toChatFrame.setVisible(true);
			}
			System.out.println("processSingleMsg_msg="+msg);
			toChatFrame.addMsg(toUser.getName()+" : "+msg);
		} catch (UnsupportedEncodingException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
	}

最后,在通信类 ComUtil 中的 ReadSingle 线程方法加入

msgProcessor.processSingleMsg(singleInPacket, LoginFrame.friendListFrame);

看起来是没有问题的。我们运行一下看看:

本来,我们打开了一个私聊窗口,输入了 abc ,点击发送信息,可是这条信息却出现在公聊窗口中去了。这是为什么呢?我们在ChatFrame 的 sendAction 中打了日志:

 

ChatFrame_destAddress=/192.168.48.1:30000

接收到公聊消息=abc

 

问题就出在端口号 30000 上,用户私聊占用端口应该是 30001。这条私聊消息被发送到多点广播端口上去了,结果被处理公聊消息的线程处理了。那么,这个destAddress是怎么来的?SocketAddress destAddress = user.getAddress();也就是说,用户登录后绑定地址时绑定错了。这个过程在 MsgProcessor 中的processBroadMsg 方法处理用户上线时的逻辑里。

User user = new User(userInfo[0], userInfo[1], broaInPacket.getSocketAddress(),0);

这里是有问题的,broaInPacket.getSocketAddress() 得到的端口号是广播端口号30000 ,按照ComUtil中的约定,应该加1才对。修改之后:

InetSocketAddress srcAddress = (InetSocketAddress) broaInPacket.getSocketAddress();
			System.out.println("srcAddress="+srcAddress);
				SocketAddress theUserAddress = new InetSocketAddress(srcAddress.getHostString(), srcAddress.getPort()+1);
				User user = new User(userInfo[0], userInfo[1], theUserAddress,0);

这样应该就可以了。

三、多网络接口的问题

到目前为止,我们的程序已经可以完成收发消息的功能了。可是,当我将它打包放到局域网上另一台机器上运行的时候,它却并不能“发现”局域网上的其它用户。这时我想起来我用ipconfig命令在我本机查看时有多个ip地址。《疯狂java讲义》P803页也说过“在某些系统中,可能有多个网络接口。这可能会给多点广播带来问题,这时候程序需要在一个指定的网络接口上监听,通过调用setInterface() 方法可以强制MulticastSocket 使用特定的网络接口。”那么,问题就出在这里了。既然如此,那么我们干脆把所有ip列举出来,让用户自己选择。

 

新增一个 IPFinder 工具类:

package com.myipmsg.util;

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


/**
 * 查询所有ip
 * @author ThinkPad
 *
 */
public class IPFinder {

	
	public static Vector<String> find(){
		 
		Vector<String> ipList = new Vector<>();
		 Enumeration<NetworkInterface> netInterfaces = null;  
		    try {  
		        netInterfaces = NetworkInterface.getNetworkInterfaces();  
		        while (netInterfaces.hasMoreElements()) {  
		            NetworkInterface ni = netInterfaces.nextElement();   
		            Enumeration<InetAddress> ips = ni.getInetAddresses();  
		            while (ips.hasMoreElements()) {  
 
		                String ip = ips.nextElement().getHostAddress();
		                if(!ip.contains(":") && !ip.equals("127.0.0.1")){
		                	ipList.add(ip);
		                }
		            }  
		        }  
		    } catch (Exception e) {  
		        e.printStackTrace();  
		    }  
		    System.out.println(ipList);
		    return ipList;
	}
	
	
	
	public static void main(String[] args) {
		// TODO 自动生成的方法存根
		find();
	}

}

工程中其它地方的修改这里就不一一赘述了,有兴趣的可以去查看附件。修改之后的登录窗口:

好友列表中把用户的ip也一并显示出来了:


注:本来还有第五篇,写传输文件的,因为之前在自己局域网中测试发送文字信息时老是不成功,备受打击,所以索性没写了。那就不写了吧。代码在附件中都有。

所有代码可在此处下载:http://download.csdn.net/detail/zhutulang/9207885

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值