本人Java菜鸟问几个关于图形化界面聊天软件的问题,望高手指点迷津!

axr1985lazy 2014-07-21 09:49:03
先上代码:

package test10;

import java.io.*;
import java.net.*;
import java.awt.*;
import java.awt.event.*;

class ChatFrame
{
//定义图形化界面的各个组件
private Frame f;
private TextArea write, show;
private Button but;

//定义了两个线程,分别控制发送端和接收端
private Thread send, rece;

//定义了连个Socket服务,分别负责发送和接收
private DatagramSocket sendSocket, receSocket;

//接收端端口
private int recePort = 9980;

//构造函数
ChatFrame()
{
//进行各种初始化的方法
initial();
}

//初始化函数
public void initial()
{
//定义主窗体
f = new Frame("MyChat");
f.setBounds(300, 100, 400, 600);
f.setLayout(new FlowLayout());

//发送按钮
but = new Button("发送");

//输入文本区域和显示文本区域
write = new TextArea(15, 50);
show = new TextArea(15, 50);

//向窗体中添加组件
f.add(show);
f.add(write);
f.add(but);

try
{
//创建两个Socket服务,分别负责发送信息和接收信息
sendSocket = new DatagramSocket();
receSocket = new DatagramSocket(recePort);//传入接收端端口
}
catch(Exception e)
{
throw new RuntimeException("Socket服务创建失败!");
}

//开启事件监听
chatEvent();

//创建两个线程,分别控制发送端和接收端
send = new Thread(new Send(sendSocket, write, show));//参数分别为发送Socket服务,主窗体中的输入文本区和显示文本区
rece = new Thread(new Receive(receSocket, show));//参数分别为接收Socket服务,主窗体为显示文本区

//开启接收端线程
//send.start();
rece.start();

//显示主窗体
f.setVisible(true);
}

//为主窗体定义事件监听机制
public void chatEvent()
{
//为发送按钮添加事件监听器
but.addActionListener(new ActionListener()
{
//当按钮被按下时,再次创建一个发送端线程,参数同上,并执行该线程
public void actionPerformed(ActionEvent e)
{
send = new Thread(new Send(sendSocket, write, show));
send.start();
}
});

//为显示文本区添加事件监听器
show.addKeyListener(new KeyAdapter()
{
//不允许对显示文本区进行任何键盘操作
public void keyPressed(KeyEvent e)
{
e.consume();
}
});

//关闭主窗体监听器
f.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
}
}

//定义发送端线程
class Send implements Runnable
{
//定义Socket服务,输入和显示文本区
private DatagramSocket ds;
private TextArea write, show;

//构造函数
Send(DatagramSocket ds, TextArea write, TextArea show)
{
this.ds = ds;
this.write = write;
this.show = show;
}

//执行体
public void run()
{
String msg = null;
DatagramPacket dp = null;
byte[] buf = null;

//发送目标设备端口
int targetPort = 21129;

try
{
//获取输入文本的文本,并发送至目标设备
msg = write.getText();

//仅在文本区不为空时执行
if(!("".equals(msg)))
{
//将字符串转换为字节数组
buf = msg.getBytes();
//将文本(字符串)打包成数据报包
dp = new DatagramPacket(buf, buf.length, InetAddress.getByName("192.168.1.106"), targetPort);

ds.send(dp);

if("over".equals(msg))
return;

//在显示文本区,显示所发送的内容
show.append("我说:"+msg+"\n");
//清空输入文本区
write.setText("");
}
}
catch (Exception e)
{
throw new RuntimeException("数据发送失败!");
}
}
}

class Receive implements Runnable
{
//定义了Socket服务和显示文本区
private DatagramSocket ds;
private TextArea show;
//构造函数
Receive(DatagramSocket ds, TextArea show)
{
this.ds = ds;
this.show = show;
}
//执行体
public void run()
{
byte[] buf = new byte[1024*64];
DatagramPacket dp = null;
String ip = null, data = null;

try
{
while(true)
{
//将字节缓存打包成数据报包,用于接收数据
dp = new DatagramPacket(buf, buf.length);
//接收数据
ds.receive(dp);

//获取对方设备IP地址和数据
ip = dp.getAddress().getHostAddress();
data = new String(dp.getData(), 0, dp.getLength());

if("over".equals(data))
{
System.out.println(ip+"离开了聊天室。。。");
break;
}

//在显示文本区显示接收到的信息
show.append(ip+"::"+data+"\n");
}
}
catch(Exception e)
{
throw new RuntimeException("数据接收失败!");
}
}
}

class MyChat
{
public static void main(String[] args)
{
new ChatFrame();
}
}


注:因本人刚学Java,代码比较粗糙,没有优化,使用比较麻烦,需要在运行第二个程序(运行两个程序用于观看效果)前将接收端端口(第22行)和发送数据报包的目的端口(135行)对调一下。

问题一:第66行
必须在ChatFrame类中创建发送端线程(不必执行),程序运行才正常。
如果仅仅是当“发送”按钮活动时(被按下)才创建发送端线程并执行线程(第84~87行),就会发生如下现象:第一个打开的程序会接收自己发送的信息,而第二个(目标)程序却收不到,但是第二个程序发送给第一个的信心却可以正常显示,这是为什么?
现象如下图:


正常运行如下图:


问题二:第143行
如果我写成if(msg != null),那么这个判断语句就起不到作用,即使输入文本区没有内容,只要按下“发送”按钮,还是会发送“我说:”,这是为什么?

希望大家帮帮忙,非常感谢!
...全文
191 3 打赏 收藏 转发到动态 举报
写回复
用AI写文章
3 条回复
切换为时间正序
请发表友善的回复…
发表回复
axr1985lazy 2014-07-22
  • 打赏
  • 举报
回复
非常感谢二楼,实在太耐心了,谢谢~
highnewrain 2014-07-21
  • 打赏
  • 举报
回复
1、第一个问题我也没搞清楚,但我把程序简单改了下之后(接收端口和发送端口通过构造函数传递,调试也方便了),这个问题就没出现了(那个chatframe类的initial函数中创建发送线程的那个代码我也注释掉了),
这句
InetAddress.getByName("192.168.1.106")
我改成
InetAddress.getByName("localhost")
,这样换台机子调试也方便
2、第二个问题比较简单:msg=write.getText(),在默认情况下,也就是你write中什么都没输入的情况下,getText()方法返回的是一个空字符串即“”,空字符串跟null不是一个概念;所以你用msg!=null来判断肯定不行,msg肯定不等于null
比如:
String msg=null,则说明msg当前是个空引用,没有指向任何有效的String对象
而String msg=“”,则意思是msg当前指向一个有效的String对象,只不过这个String对象的值是“”而已
你查下api文档了解下TextArea类的getText方法,以及区分下null和“”就明白啦
3、修改后的代码及结果
package lesson10;
import java.io.*;
import java.net.*;
import java.awt.*;
import java.awt.event.*;

class ChatFrame
{
//定义图形化界面的各个组件
private Frame f;
private TextArea write, show;
private Button but;

//定义了两个线程,分别控制发送端和接收端
private Thread send, rece;

//定义了连个Socket服务,分别负责发送和接收
private DatagramSocket sendSocket, receSocket;

//接收端端口
private int recePort = 6000;
private int targetPort=6001;
private String name;
//构造函数
ChatFrame(String name,int recePort,int targetPort)
{
this.name=name;
this.recePort=recePort;
this.targetPort=targetPort;
//进行各种初始化的方法
initial();
}

//初始化函数
public void initial()
{
//定义主窗体
f = new Frame(name);
f.setBounds(300, 100, 400, 600);
f.setLayout(new FlowLayout());

//发送按钮
but = new Button("发送");

//输入文本区域和显示文本区域
write = new TextArea(15, 50);
show = new TextArea(15, 50);

//向窗体中添加组件
f.add(show);
f.add(write);
f.add(but);

try
{
//创建两个Socket服务,分别负责发送信息和接收信息
sendSocket = new DatagramSocket();
receSocket = new DatagramSocket(recePort);//传入接收端端口
}
catch(Exception e)
{
throw new RuntimeException("Socket服务创建失败!");
}

//开启事件监听
chatEvent();

//创建两个线程,分别控制发送端和接收端
//send = new Thread(new Send(sendSocket, write, show));//参数分别为发送Socket服务,主窗体中的输入文本区和显示文本区
rece = new Thread(new Receive(receSocket, show));//参数分别为接收Socket服务,主窗体为显示文本区

//开启接收端线程
//send.start();
rece.start();

//显示主窗体
f.setVisible(true);
}

//为主窗体定义事件监听机制
public void chatEvent()
{
//为发送按钮添加事件监听器
but.addActionListener(new ActionListener()
{
//当按钮被按下时,再次创建一个发送端线程,参数同上,并执行该线程
public void actionPerformed(ActionEvent e)
{
send = new Thread(new Send(sendSocket, write, show,targetPort));
send.start();
}
});

//为显示文本区添加事件监听器
show.addKeyListener(new KeyAdapter()
{
//不允许对显示文本区进行任何键盘操作
public void keyPressed(KeyEvent e)
{
e.consume();
}
});

//关闭主窗体监听器
f.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
}
}

//定义发送端线程
class Send implements Runnable
{
//定义Socket服务,输入和显示文本区
private DatagramSocket ds;
private TextArea write, show;
//发送目标设备端口
private int targetPort = 6001;

//构造函数
Send(DatagramSocket ds, TextArea write, TextArea show,int targetPort)
{
this.ds = ds;
this.write = write;
this.show = show;
this.targetPort = targetPort;
}

//执行体
public void run()
{
String msg = null;
DatagramPacket dp = null;
byte[] buf = null;
try
{
//获取输入文本的文本,并发送至目标设备
msg = write.getText();

//仅在文本区不为空时执行
if(!("".equals(msg)))
{
//将字符串转换为字节数组
buf = msg.getBytes();
//将文本(字符串)打包成数据报包
dp = new DatagramPacket(buf, buf.length, InetAddress.getByName("localhost"), targetPort);

ds.send(dp);

if("over".equals(msg))
return;

//在显示文本区,显示所发送的内容
show.append("我说:"+msg+"\n");
//清空输入文本区
write.setText("");
}
}
catch (Exception e)
{
throw new RuntimeException("数据发送失败!");
}
}
}

class Receive implements Runnable
{
//定义了Socket服务和显示文本区
private DatagramSocket ds;
private TextArea show;
//构造函数
Receive(DatagramSocket ds, TextArea show)
{
this.ds = ds;
this.show = show;
}
//执行体
public void run()
{
byte[] buf = new byte[1024*64];
DatagramPacket dp = null;
String ip = null, data = null;

try
{
while(true)
{
//将字节缓存打包成数据报包,用于接收数据
dp = new DatagramPacket(buf, buf.length);
//接收数据
ds.receive(dp);

//获取对方设备IP地址和数据
ip = dp.getAddress().getHostAddress();
data = new String(dp.getData(), 0, dp.getLength());

if("over".equals(data))
{
System.out.println(ip+"离开了聊天室。。。");
break;
}

//在显示文本区显示接收到的信息
show.append(ip+"::"+data+"\n");
}
}
catch(Exception e)
{
throw new RuntimeException("数据接收失败!");
}
}
}

public class Demo7
{
public static void main(String[] args)
{
//new ChatFrame("李四",6000,6001);
new ChatFrame("王五",6001,6000);
}
}


我是先启动李四的,后启动王五的
以夕阳落款 2014-07-21
  • 打赏
  • 举报
回复
问题一不是很明白,问题二改成
if(!msg.equals(""))
试试
前导课程:      《Java工程师系列课程》前4部课程内容:       本课程是《Java工程师系列课程》的第7部分,主要讲解Java实际开发过程中常用类,特别是各种工具类的使用。熟练掌握这些类的使用方法,能够大幅度提高编程效率。同时还在课程中穿插讲解了很多Java语言的设计思想和理念,深刻理解并掌握这些编程思想能够从根本上提高编程水平和解决的能力。在课程的末尾还安排了国际化软件实战项目。       本课程涉及的主要内容如下表所示:课程说明:      在开发Java程序的过程中,无论做什么类型的项目,都会用到各种工具类来解决一些实际。这些工具类被广泛的应用与各种行业软件的开发。比如我们经常会用BigDecimal类来的完成浮点数的精确计算,也会用Formatter类完成文本、数字、日期的格式化操作等等。      为了能够让学员迅速掌握这些工具类的使用,我们特意开发了这门视频课程,视频中详细讲解了20余种常用工具类的使用规则。此外,在课程中,还讲解了很多Java语言的设计思想和理念,可以让学员在听课过程中能够对Java语言有更加深刻的理解和认识。      最后,本课程还安排了一个小型国际化软件的实战项目,通过实战让学员掌握国际化软件的开发理念和实际操作流程预期效果:      认真学习完本课程,学员可以掌握20种Java实战开发中常用类的使用方法,实实在在提高实战水平,完成从菜鸟高手的华丽转变。配套福利:中英文双语版本自助购物软件的完整源码环境配置要求:      学习本课程需安装JDK13或更高版本的JDK,以便程序能正确运行,建议使用IntelliJ IDEA 2019.1.2或更高版本的开发工具。     因有合作协议约束,《穆哥学堂》只提供PDF版本的课件!

62,614

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧