简单的聊天室制作

简单的聊天室制作

一个简单的聊天室,主要是就两个部分,一部分就是我们进行的时候那个聊天窗口,另外一个就是背后的服务器,我们要写一个聊天窗口,也要写一个相对应的服务器。

做一个项目过程中,写一个代码很简单,但是把逻辑分析清楚,将制作的过程中所有的逻辑关系分析清楚是项目的最重要的环节。

下面的一步一步,将这个聊天室的制作过程一步一步制作出来。

第一步:

第二步:

第三步:

第四步:

第五步:

第六步:

第七步:

第八步:

第九步:

第十步:

第十一步:

这就是简单的聊天室的制作的过程。这样我们来看一下实现后的代码:

客户端的的代码:

  1 import java.awt.BorderLayout;
  2 import java.awt.Frame;
  3 import java.awt.TextArea;
  4 import java.awt.TextField;
  5 import java.awt.event.ActionEvent;
  6 import java.awt.event.ActionListener;
  7 import java.awt.event.WindowAdapter;
  8 import java.awt.event.WindowEvent;
  9 import java.io.DataInputStream;
 10 import java.io.DataOutputStream;
 11 import java.io.EOFException;
 12 import java.io.IOException;
 13 import java.net.Socket;
 14 import java.net.SocketException;
 24 //该类继承了Frame,所以本质就是一个Fame,具有图新界面的一切特征
 25 public class ChatClient extends Frame {
 26     //声明一个文本框,用用于客户的消息输入
 27     TextField tfTxt = new TextField();
 28     //显示客户的输入,包括别的客户端通过服务器转发过来的消息
 29     TextArea taContent = new TextArea();
 30     //声明一个空的socket链接,用于和服务器进行信息交互,下面合适的时候,会进行赋值(变量声明的三个步骤)
 31     Socket soc = null;
 32     //声明一个boolean,判断和服务器端是否链接上了,特指socket
 33     private boolean bConnected = false;
 34     //接受从服务器端发送过来的信息流
 35     DataInputStream dis = null;
 36     //向服务器端发送客户输入消息
 37     DataOutputStream dos = null;
 38     //声明一个线程变量,专门用于接受服务器端发过来的消息.死循环,阻塞接受
 39     Thread tRecv = new Thread(new RecvThread());
 40
 41     //main程序的入口,用于启动客户端,怎么启动的?静态和成员的区别
 42     public static void main(String[] args) {
 43         new ChatClient().launFrame();
 44     }
 45
 46     //客户端的启动方法,启动了客户端界面,链接服务器,启动接受线程
 47     public void launFrame() {
 48         //设置界面的位置宽高
 49         setBounds(1000, 300, 300, 300);
 50         //设置界面的布局.
 51         this.add(tfTxt, BorderLayout.SOUTH);
 52         this.add(taContent, BorderLayout.NORTH);
 53         //把Frame里面的控件打包然后按照默认设计好的方式显示
 54         this.pack();
 55         // 窗口关闭按钮,事件监听,用的是匿名内部类,关闭窗口的同时关闭了和服务器端链接,然后退出系统,线程不用管.都关机了
 56         this.addWindowListener(new WindowAdapter() {
 57             public void windowClosing(WindowEvent e) {
 58                 disConnect();
 59                 System.exit(0);
 60             }
 61         });
 62
 63         // 添加文本框监听事件,是一个成员内部类,目的是为了获得客户输入的消息,默认触发回车触发
 64         tfTxt.addActionListener(new TxtListener());
 65         //让整个界面可见
 66         setVisible(true);
 67
 68         // 链接服务器方法调用了,怎么就叫链接了.soc,dis,dos都链接上了才叫链接
 69         connect();
 70         //全部链接上以后,才启动消息接收线程
 71         tRecv.start();
 72     }
 73
 74     // 添加客户端链接服务器的方法
 75     public void connect() {
 76         try {
 77             //给声明好的Socket指定链接的IP和端口,是个成员变量??
 78             soc = new Socket("127.0.0.1", 9999);
 79             //dos通过Socket向服务器端发送消息,用的什么方法?
 80             dos = new DataOutputStream(soc.getOutputStream());
 81             //dis通过Socket接收服务器端发过来的消息,什么方法?
 82             dis = new DataInputStream(soc.getInputStream());
 83             //System.out.println(dis);
 84             //上面都都准备好,才叫连接上,所以,把链接成员变量设置为真
 85             bConnected = true;
 86         } catch (IOException e) {
 87             e.printStackTrace();
 88         }
 89     }
 90
 91     //断开服务器连接,把打开的那三个链接全部关闭掉
 92     public void disConnect() {
 93         try {
 94             dos.close();
 95             dis.close();
 96             soc.close();
 97         } catch (IOException e) {
 98             e.printStackTrace();
 99         }
100
101         /*try{
102         bConnected = false;
103         tRecv.join();   //合并线程
104         }catch(InterruptedException e){
105             e.printStackTrace();
106         }finally{
107             try{
108                 dos.close();
109                 dis.close();
110                 soc.close();
111             }catch(IOException e){
112                 e.printStackTrace();
113             }
114     }*/
115     }
116
117
118     // 创建一个内部类,实现控件本身的事件监听,不同的控件有自己默认的事件,有三种/四种来设置监听:匿名内部类,成员内部类,外部类
119     private class TxtListener implements ActionListener {
120         @Override
121         public void actionPerformed(ActionEvent e) {//获得触发事件的那个最初控件对象
122             //经过e获得了事件源对象控件,但是我们没用他,因为我们把事件源对象设置成了成员变量,我们内部类可以直接访问成员变量
123             String s = tfTxt.getText().trim();
124             // System.out.println(s);
125             // 把客户往输入文本框的消息,取到然后输出文本显示框,这里也是为了避免和服务器端发送回来的消息叠加,所以注释掉
126             //taContent.setText(s);
127             //同时把输入框中客户输入的值清空.要不然会叠加
128             tfTxt.setText("");
129
130             // 把数据发送到服务器
131             try {
132                 //DataOutputStream,会把数据的数据类型也保存进去,发送出去,什么样的顺序写,就什么样的顺序.
133                 //也需要按照对应的数据类型来读取
134                 dos.writeUTF(s);
135                 //只要是输出流,都需要flush,要不然会把数据缓存下来,而输出不出去
136                 dos.flush();
137             } catch (IOException e1) {
138                 e1.printStackTrace();
139             }
140         }
141     }
142
143     //阻塞是读取服务器端发送过来的消息
144     private class RecvThread implements Runnable {
145         //当线程类启动该线程的时候,调用start(),然后jvm默认会调用run()方法开启新线程
146         public void run() {
147             try {
148                 //如果链接全准备好了,我们就不间断的等待读取服务器端发送过来的消息.
149                 while (bConnected) {
150                     //通过dis的readUTF()读取
151                     String str = dis.readUTF();
152                     //然后把获取到的消息,和输出到文本显示框中原本的消息一起,加上换行符
153                     taContent.setText(taContent.getText() + str + ‘\n‘);
154                 }
155             } catch (SocketException e) {
156                 System.out.println("客户端已经关闭");
157             }catch (EOFException e) {
158                 System.out.println("读取完毕");
159             }catch (IOException e) {
160                 System.out.println("IO异常");
161             }
162         }
163
164     }
165 }

服务器端代码:

  1 import java.io.DataInputStream;
  2 import java.io.DataOutputStream;
  3 import java.io.EOFException;
  4 import java.io.IOException;
  5 import java.net.ServerSocket;
  6 import java.net.Socket;
  7 import java.util.ArrayList;
  8 import java.util.Iterator;
  9 import java.util.List;
 10
 11 import javax.xml.stream.events.StartDocument;
 12
 13 /**
 14  * 思路: 1 开启服务器,接受客户端链接,接受客户端数据 start():方法,负责服务器的开启 client():只负责管道的链接
 15  * run():负责数据的读取
 16  *
 17  * @author Admin
 18  *
 19  */
 20
 21 public class ChatServer {
 22     // 判断服务器是否启动,启动以后就不间断的接受客户端链接,就是while(true)接受链接,接受链接,不是消息
 23     boolean started = false;
 24     //声明一个ServerSocket变量,用于转载启动服务器对象
 25     ServerSocket ss = null;
 26     //声明一个Client集合,用于装在所有连接到服务器的客户端
 27     List<Client> clients = new ArrayList<>();
 28
 29     //main()方法,调用方法启动服务器,start(),自定义的
 30     public static void main(String[] args) {
 31         new ChatServer().start();
 32     }
 33
 34     // 负责启动服务器方法声明
 35     public void start() {
 36         try {
 37             //实例化一个服务器对象,然后赋值给上面的服务器变量
 38             ss = new ServerSocket(9999);
 39             //启动以后赋值给判断变量
 40             started = true;
 41             //如果    启动了,就不间断声明socket接收客户端的链接
 42             while (started) {
 43                 // 声明socket
 44                 Socket soc = ss.accept();
 45                 //每个Socket对应一个Client对象,然后把Socket传递到对象的内部,Socket和客户端就一一对应了
 46                 Client c = new Client(soc);
 47                 //然后根据客户端开启这个客户端对应的消息收发线程.这样线程,客户端,Socket就一一对应了.
 48                 new Thread(c).start();
 49                 //然后把客户端的引用保存到集合中,这样,客户单就有了顺序,相当于有了一个唯一的识别符号
 50                 clients.add(c);
 51                 // 上一行如果没有链接上会报错,到达这里就证明链接上了
 52
 53             }
 54         } catch (IOException e) {
 55             // e.printStackTrace();
 56             System.out.println("客户端关闭,服务器端也会关闭");
 57         } finally {
 58             try {
 59                 if (ss != null)
 60                     ss.close();
 61             } catch (Exception e2) {
 62                 e2.printStackTrace();
 63             }
 64         }
 65     }
 66
 67     class Client implements Runnable {
 68         //每个客户端对象,都肯定具备以下属性
 69         //对应的Socket
 70         private Socket soc;
 71         //对应的输入属性
 72         private DataInputStream dis = null;
 73         //对应的输出
 74         private DataOutputStream dos = null;
 75         //对应的链接属性,是否链接好了
 76         private boolean bConnected = false;
 77
 78         // 构造方法,只是成功的创建这个类,构造方法用于创建这个类
 79         //同时需要输入对应的Socket,还同时实例化输入和输出的对应的流及其方法,和Socket绑定
 80         public Client(Socket s) {
 81             this.soc = s;
 82             try {
 83                 dis = new DataInputStream(s.getInputStream());//s是绑定
 84                 dos = new DataOutputStream(s.getOutputStream());
 85             } catch (IOException e) {
 86                 e.printStackTrace();
 87             }
 88             bConnected = true;
 89         }
 90
 91         @Override
 92         public void run() {
 93             try {
 94                 //如果链接成功的话,这个线程就不间断的读取客户端的消息
 95                 while (bConnected) {
 96                     //读取客户端发过来的消息
 97                     String str = dis.readUTF();
 98                     System.out.println(str);
 99                     //同时遍历集合中的每一个对象,然后挨个发送收到消息
100                     for (int i = 0; i < clients.size(); ++i) {
101                         //i是Client,Socket,对应的下标
102                         Client c = clients.get(i);
103                         //发送方法
104                         c.send(str);
105                     }
106                 }
107             } catch (EOFException e) {
108                 //如果客户端断开,就需要移除这个客户端.输入移除和输出移除都一样.因为都是不间断的监听
109                 clients.remove(this);
110                 System.out.println("客户端已经断开连接,所以从集合中移除该客户端");
111             } catch (IOException e) {
112                 e.printStackTrace();
113             } finally {
114                 try {
115                     if (dis != null) {
116                         dis.close();
117                     }
118                     if (dos != null) {
119                         dos.close();
120                     }
121                     if (soc != null) {
122                         soc.close();
123                     }
124                 } catch (IOException e2) {
125                     e2.printStackTrace();
126                 }
127             }
128         }
129         public void send(String str) {
130             try {
131                 //真正向客户端发送消息的方法
132                 dos.writeUTF(str);
133             } catch (IOException e) {
134                 e.printStackTrace();
135             }
136         }
137     }
138
139 }

代码完成了,让我们来看看我们的聊天室做好以后的效果:

在我敲击回车后,观看一下结果:

我们这个小的聊天室就完成了!

时间: 2024-08-03 14:46:27

简单的聊天室制作的相关文章

网页聊天室制作步骤分享

结合网页与Asp来实现一个简单的网页聊天室制作案例,有在线聊天.在线人员名单.在线时间等功能.在这篇教程的最后,还比较详细地介绍了Flash与Asp结合使用的平台环境和常用的调试手段. 一.原理 主要的流程是先在网页端发送一个请求到服务器端,然后等待从服务器端的返回值,当值返回网页端后,根据相应的值做相应的操作.在网页部 分,跟前面的教程一样,用到的主要都是loadVariables()函数和循环等待的技巧.在Asp部分,主要用到了global.asa文件和 application()属性,先定

玩转Node.js(四)-搭建简单的聊天室

玩转Node.js(四)-搭建简单的聊天室 Nodejs好久没有跟进了,最近想用它搞一个聊天室,然后便偶遇了socket.io这个东东,说是可以用它来简单的实现实时双向的基于事件的通讯机制.我便看了一些个教程使用它来搭建一个超级简单的聊天室. 初始化项目 在电脑里新建一个文件夹,叫做“chatroom”,然后使用npm进行初始化: $ npm init 然后根据提示以及相关信息一步一步输入,当然也可以一路回车下去,之后会在项目里生成一个package.json文件,里面的信息如下: 1 $ ca

Java网络编程 - 基于UDP协议 实现简单的聊天室程序

最近比较闲,一直在抽空回顾一些Java方面的技术应用. 今天没什么事做,基于UDP协议,写了一个非常简单的聊天室程序. 现在的工作,很少用到socket,也算是对Java网络编程方面的一个简单回忆. 先看一下效果: 实现的效果可以说是非常非常简单,但还是可以简单的看到一个实现原理. "聊天室001"的用户,小红和小绿相互聊了两句,"聊天室002"的小黑无人理会,在一旁寂寞着. 看一下代码实现: 1.首先是消息服务器的实现,功能很简单: 将客户端的信息(进入了哪一个聊

Android开发之简单的聊天室(客户端与服务器进行通信)

1.使用ServerSocket创建TCP服务器端 Java中能接收其他通信实体连接请求的类是ServerSocket, ServerSocket对象用于监听来 自客户端的Socket连接,如果没有连接,它将一直处于等待状态.ServerSocket包含一个监听来自客户端连接请求的方法. 1) Socket accept():如果接收到一个客户端Socket的连接请求,该方法将返回一个与连接客户端Socket对应的Socket;否则该方法将一直处于等待状态,线程也被阻塞. 创建ServerSoc

WebSocket介绍和一个简单的聊天室

WebSocket是什么呢? WebSocket一种在单个 TCP 连接上进行全双工通讯的协议.WebSocket通信协议于2011年被IETF定为标准RFC 6455,并被RFC7936所补充规范,WebSocketAPI被W3C定为标准. WebSocket 是独立的.创建在 TCP 上的协议,和 HTTP 的唯一关联是使用 HTTP 协议的101状态码进行协议切换,使用的 TCP 端口是80,可以用于绕过大多数防火墙的限制. WebSocket 使得客户端和服务器之间的数据交换变得更加简单

基于Server-Sent Event的简单在线聊天室

一.Web即时通信 所谓Web即时通信,就是说我们可以通过一种机制在网页上立即通知用户一件事情的发生,是不需要用户刷新网页的.Web即时通信的用途有很多,比如实时聊天,即时推送等.如当我们在登陆浏览知乎时如果有人回答了我们的问题,知乎就会即时提醒我们,再比如现在电子商务的在线客服功能.这些能大大提高用户体验的功能都是基于Web即时通信实现的. 普通HTTP流程 客户端从服务器端请求网页 服务器作出相应的反应 服务器返回相应到客户端 而由于HTTP请求是无状态的,也就是说每次请求完成后,HTTP链

使用Service Bus Topic 实现简单的聊天室

创建Service Bus能够參照: https://azure.microsoft.com/en-gb/documentation/articles/service-bus-dotnet-how-to-use-topics-subscriptions/ Azure Service Bus做广播和消息通知功能非常合适,而且能够订阅不同的Topic(不同的消息类型或不同的聊天室). 1. 首先须要安装Azure Service Bus的nuget package: 2. 建议安装Azure Ser

node实现一个简单的聊天室(认识一下socket)

边学边理解node的高深,今天写了一个聊天室的demo,很简单,认识一下socket node服务端代码 var express = require('express'); var app = express();//session固定写法 var session = require('express-session'); app.use(session({ secret: 'keyboard cat', resave: false, saveUninitialized: true })); /

独家解析聊天室制作教程一

一位精通程式设计的大师,在每开发一个项目的时候,往往要经过非常严密的计划,十分严格的求证,否则,辛辛苦苦开发的程序一旦中途发现问题,那往往都是前功尽弃,从头开始.特别是开发一个大型项目,那计划与求证过程比编写程序过程更为重要. 然而,如果我们是一位程式设计的初学者,要让我们开发一个例程,我们的做法又往往与上面的做法相反.往往从最简单的实例开始,实现最初步的功能,然后在简单的基础上不断对程序加以改进.完善,使其功能不断增加,发展,强大. 我自己有一个习惯:在设计一些中小程式时,我喜欢花更大的精力去