如何实现基于网络通信的QQ聊天室
目录
- 认识网络通信
- 实现QQ聊天室
- 项目总结
一.认识网络通信
1.前言
作为一名大一的学生,虽然马上就要奔大二啦,但是由于今年下学期学习都是在线上进行的,对课程学习的积极性也不是很高,课程也是学的一知半解的,对于学习Java编程就更没有什么较大的进展,自己就特别内疚,想一想暑假一定要做点什么。
期末考试结束之后,这几天一直在学习网络通信这一块。之前对于网络通信也有相关的了解,学习一些基本的输入输出流和一些基础的框架,但是这些都非常表面和零散,于是花了4天时间把网络通信深入学习了一下,把一个简单的QQ聊天室给实现了。虽然刚开始看到一堆代码头晕眼花的,但是结果还是不错的。ヾ(•ω•`)o
这里简单说一下网络通信,简单理解,网络通信顾名思义就是指通过网络进行通信。那网络是什么大家一定非常清楚了吧,你使用的手机等一些电子设备都需要联网才能外界进行通信(相互联系),这样你应该就知道网络通信的大致一些内容啦。所谓通信官方地说就是:人与人之间通过某种媒体进行信息交流 。而这种媒体就是互联网。
网络通信是通过网络将各个孤立的设备进行连接,通过信息的交互实现人与人,人与计算机,计算机与计算机的通信。而网络通信最基础最重要的就是网络通信协议。说到网络通信协议,现在的网络协议有很多,有关于局域网的ISO协议、TCP/IP协议,还有关于工控网和广域网的一堆协议,这里我就不举例了。废话不多说,今天着重带大家了解一下TCP/IP协议。
2.TCP/IP协议
虽然我也不想用QQ作为网络通信技术的代言人,虽然我不是很喜欢它,但是它对于我们肯定是再熟悉不过了!
但你在QQ消息框输入一段文字按下发送键之后,这段文字就会出现在另一台电脑或者手机设备上的QQ中--------这是如何实现的?是不是感到很神奇?如下图所示:
首先,你要想一想:为啥我发出的信息会到我指定的好友的机器(手机或电脑)上,而不是别人的手机上。这个很重要,因为你至少要知道,每一台设备都有一个独立的IP地址,包括手机和电脑及一些与外界通信的设备。IP地址在网络中标识了一台机器的位置,以便另一台机器可以找到它,所以当你发送信息给好友的时候,你发出不仅仅只有你说的话,其中还包括好友的IP地址,其实这其中还包括很多东西,具体的后面我还会说的。
其实仅仅知道IP地址是不够的,用QQ发送信息要到你的好友的电脑上,必须要指明哪一个端口,就像你要到某栋楼的好友家里做客,你光是知道是那一栋楼是不够的,你还必须知道朋友家的门牌号。在一台计算机中用端口号这个数字来标识机器上需要同通信的某一个程序。所以你发送信息指定的端口号应该是QQ通信模块所对应的端口号。
对于端口号,一定要知道它不是硬件端口,它只是TCP/UDP上的一个逻辑号码。这里我们在窗口命令行输入netstat -an即可知道自己的计算机哪些端口号是被使用的。如下图:
这里大家可以自己打开自己的命令行窗口去看看!!!!!
IP地址和端口号是密不可分的。对于IP地址和端口号我建议大家可以详细了解一下,我只就简单地说明它们的大致解释:
IP地址:IP地址是一个32位的二进制数,是由4个字节组成,我们都知道每个字节是一个8位二进制数。IP地址通常被表示为(a.b.c.d)的形式,如本机IP(127.0.0.1),IP地址分成五个类别,具体这里就不细说了,我们通常用的是B类地址。整体来说:如果把个人电脑比作一台电话的话,那么IP地址就相当于电话号码。
Port:端口号:每台机器都有0~65535个端口号,其中每一个数字可供一个程序通信使用,而且通常情况下0-1024的端口号我们一定要避免去使用它,我们一般称为它们为知名端口,就是一般网页和系统已经分配好的端口,例如打开网页的时候,则会连接服务器的80端口,在地址栏不需要输入这个端口号,因为它是默认的。
如果要实现与对方通信,我们可以在窗口命令行中用telnet命令(可能有些人该命令是用不了,因为没有电脑打开该功能,这里大家可以去网上搜搜如何打开的)输入对方的地址和对应的端口号,这里我就拿taobao.com来举例吧,如果我要连接淘宝网对应的服务器,因为上面说了它默认它是80端口,如下图:
按下回车键后出现如下界面:
虽然我们已经连上了淘宝网页的服务器,但是如果要发送字节的时候,它会切断与你的连接,因为你发送的东西并不符合其服务器制定的规范,如果在七八十年代,很多黑客可能会通过这种方式向服务器发送大量的字节,如果数据量太大,服务器就不能处理别的客户发来的信息,从而实现网络攻击服务器的现象,但是现在网络通信制定一系列的规范来达到管理大家的网络通信的安全。这里我来简单说说TCP/IP协议:
TCP/IP协议:
TCP/IP协议在一定程度上参考了OSI的体系结构。建议先了解一下OSI模型的一个层次划分,它从下到上分为:物理层、数据链路层、网络层、运输层、会话层、表示层和应用层。而TCP/IP协议被简化为四个层次。
TCP和IP协议分别位于传输层和网络层,其实TCP/IP协议不仅仅指的是TCP和IP协议,它指的是一个由FTP\SMTP\TCP\UDP\IP等协议构成的协议簇,只是由于TCP\IP协议最具有代表性,所以被称为TCP\IP协议。
那么TCP\IP到底有啥呢?你咋不说清楚。下面就说说它的作用:
TCP/IP协议:它其实就是一种规则,它是方便去将所有的不同类型的计算机连接在一起的一套规范,但如果由设备要传输文件或信息的时候,它就要去遵循这套规范,它可以规定发送信息的组成和传输的方式。
总之:它就是一种机器之间交流的规则,规定通信双方的通信流程和数据格式的规则,要参与通信的双方都只有按照这个规则发送数据才可以正常通信,否则就不能进行通信,就像人与人之间要用同一种语言交流一样。
3.认识Java中的Socket类
对于该类的作用,大家想必已经知道了,它是Java负责网络通信的类,先来看看的API文档对该类的介绍:
由上面API文档的解释可知它是java网络编程中的一个类,而且它说它是客户端的一个套接字,只是两台机器通信的一个端点。这里的另一个端点就是服务器所在端点,它是ServerSocket:负责创建服务器并且等待客户的连接。其中一些详细的方法就不过多的说了。既然现在已经了解地差不多了,接下来就直接上代码了。
二.实现QQ聊天室
首先说一说为什么我们在编写代码前要进行一个框架的搭建,其实如果是一些简单的项目就不要这么大费周章,但是如果你要做的项目涉及到的类比较多,光代码就要1000多行的话,这可能就要进行一波框架的设计。
好处:
- 验证编程项目的可行性
- 避免代码的高耦合问题
- 加快我们编写代码的速度
1.框架搭建
- 服务器端框架
- UerInformation.java:用户数据模型类,功能:每个UserInformation类的对象保存一个用户的账号和身份信息。
- DaoTools.java:数据访问和验证类,功能:负责生成模拟的用户的信息,并在客户登入时验证账号和密码。
- UserModel.java:用户列表类,功能:在服务器管理界面上见在线用户以列表的形式展现,便于对用户进行处理。
- UserModelRender.java:用户列表渲染类,功能:对用户列表进行一些外观的设置和事件的处理。
- SocketServer.java:服务器线程类,功能:创建服务器并启动,等待连接,将进入的连接交给线程对象去处理,以防连接客户端的阻塞影响其他客户的连接和其他程序的运行。
- ServerThread.java:处理Socket对象的线程类,功能:每进入一个连接,就需要创建一个该类的对象,在run方法中负责对该连接的客户进行交互,对于服务器而言:一个ServerThread对象就对应了一个客户端。
- ServerThreadTools.java:服务器端的辅助类,功能:负责将客户机发来的消息转发给其他的客户机(群发),这里要创建一个队列的对象来存放服务器生成的每一个ServerThread对象,该类可以对客户机进行一些处理并且返回客户机的一些信息。
- MainServerUI.java:服务器界面管理程序类,功能:主要实现界面展示,事件处理的能力(1.启停服务器 2.发布公告消息 3.显示在线用户信息 4.踢人 5.对某一个人发消息)。
- 客户端框架
- MainNetUI.java:客户端登录和聊天界面类,功能:产生登录界面,验证通过后显示聊天界面,进行与服务器的通信。
- NetClientThread.java:客户端连接服务器通信线程类,功能:连接服务器并且收发消息类的封装。
2.实现框架中的每一部分
- 服务器端框架
- UerInformation.java:用户数据模型类
/**
* 用户数据模型类
* @author 梦想少年
* 功能:每个UserInformation类的对象保存一个用户的账号和身份信息
*/
public class UserInformation {
//构造器
public UserInformation(){
}
public UserInformation(ImageIcon icon,String name,String password){
this.icon=icon;
this.name=name;
this.password=password;
}
public UserInformation(String name,String password){
this.name=name;
this.password=password;
}
//用户头像
private ImageIcon icon;
//用户名
private String name;
//用户的密码
private String password;
//用户的登陆时的时间
private String loginTime;
//用户的地址
private String address;
//注册设置的方法
public void setIcon(ImageIcon icon){
this.icon=icon;
}
public void setName(String name){
this.name=name;
}
public void setPassWord(String password){
this.password=password;
}
public void setLoginTime(String loginTime){
this.loginTime=loginTime;
}
public void setAddress(String address){
this.address=address;
}
//取得用户信息的方法
public ImageIcon getIcon(){
return icon;
}
public String getName(){
return name;
}
public String getPassWord(){
return password;
}
public String getLoginTime(){
return loginTime;
}
public String getAddress(){
return address;
}
}
- DaoTools.java:数据访问和验证类
/**
* 数据访问和验证类
* @author 梦想少年
* 功能:负责生成模拟的用户的信息,并在客户登入时验证账号和密码
*/
public class DaoTools {
@SuppressWarnings({
"unchecked", "rawtypes" })
//用户列表
public static List<UserInformation> userList=new ArrayList();
//生成模拟的用户信息
@SuppressWarnings({
"rawtypes", "unchecked" })
private static Map<String,UserInformation> userDataBase=new HashMap();
//在程序启动时加载用户数据
static {
for(int i=0;i<10;i++){
UserInformation user=new UserInformation();
user.setIcon(new
ImageIcon("D:\\常用\\矢量图标库\\listUser\\"+(i+1)+".png"));
user.setName("梦想少年"+i);
user.setPassWord("123456789");
userDataBase.put(user.getName(),user);
}
}
//登陆时验证用户信息
public static boolean checkLogin(UserInformation user){
if(userDataBase.containsKey(user.getName())){
if(userDataBase.containsKey(user.getPassWord())){
return true;
}else{
System.out.println("用户密码错误!"+user.getPassWord());
return false;
}
}else{
System.out.println("用户名不存在!"+user.getName());
return false;
}
}
}
- UserModel.java:用户列表类
/**
* 用户列表类
* @author 梦想少年
* 功能:在服务器管理界面上见在线用户以列表的形式展现,便于对用户进行处理
* @param <E>
*/
public class UserModel<E> implements ListModel<E> {
List<E> allUserThread;
//构造器
public UserModel(List<E> allUserThread) {
// TODO Auto-generated constructor stub
this.allUserThread=allUserThread;
}
@Override
public int getSize() {
// TODO Auto-generated method stub
return allUserThread.size();
}
@Override
public E getElementAt(int index) {
// TODO Auto-generated method stub
return allUserThread.get(index);
}
@Override
public void addListDataListener(ListDataListener l) {
// TODO Auto-generated method stub
}
@Override
public void removeListDataListener(ListDataListener l) {
// TODO Auto-generated method stub
}
}
- ModelRender.java:用户列表渲染类
/**
* 用户列表渲染类
* @author 梦想少年
* 功能:对用户列表进行一些外观的设置和事件的处理
* @param <E>
*/
public class ModelRender<E> implements ListCellRenderer<E> {
//容器
JPanel container=new JPanel();
//头像标签
JLabel iconLable=new JLabel();
//用户名标签
JLabel nameLable=new JLabel();
//密码标签
JLabel passwordLable=new JLabel();
//初始化
public ModelRender() {
// TODO Auto-generated constructor stub
container.setLayout(new FlowLayout());
container.setPreferredSize(new Dimension(450,20));
container.add(iconLable,BorderLayout.WEST);
container.add(nameLable,BorderLayout.CENTER);
container.add(passwordLable,BorderLayout.EAST);
}
@Override
public Component getListCellRendererComponent(JList<? extends E> list,
E value, int index, boolean isSelected,
boolean cellHasFocus) {
// TODO Auto-generated method stub
//获取当前用户
UserInformation user=(UserInformation)value;
//设置组件要显示的内容
iconLable