当前位置: 首页 > news >正文

利用TCP协议,创建一个多人聊天室

项目名称

利用TCP协议,做一个带有登录,注册的无界面,控制版的多人聊天室。

知识点

循环,判断,集合,IO,多线程,网络编程

准备内容

在当前模块下新建txt文件,在文件中保存正确的用户名和密码

zhangsan=123
lisi=1234
wangwu=12345

页面搭建

  1. 客户端连接服务器后,显示如下
服务器已经连接成功
==============欢迎来到小周聊天室================
1登录
2注册
请输入您的选择:

服务器

public class Server {public static void main(String[] args) throws IOException {ServerSocket serverSocket = new ServerSocket(10001);System.out.println("有客户端来连接");while (true) {}}
}

客户端

public class Client {public static void main(String[] args) throws IOException {Socket socket = new Socket("127.0.0.1",10001);System.out.println("服务器已经连接成功");//搭建页面框架while(true){System.out.println("=========欢迎来到小周聊天室=========");System.out.println("1.登录");System.out.println("2.注册");System.out.println("请选择选项:");Scanner sc = new Scanner(System.in);switch(sc.nextInt()){case 1 -> System.out.println("用户选择了登录");case 2 -> System.out.println("用户选择了注册");default -> System.out.println("没有这个选项");}}}
}

效果展示

服务器已经连接成功
=========欢迎来到多人聊天室=========
1.登录
2.注册
请选择选项:
1
用户选择了登录

登录操作

  1. 需要输入用户名和密码
  2. 回车,提交给服务器,进行验证,服务器会根据txt文件中的密码

根据不同的情况,服务器会写三种判断提示

服务器回写第一种提示:登录成功
服务器回写第二种提示:密码有误  
服务器回写第三种提示:用户名不存在

客户端会根据服务器回写的数据,根据三种情况进行不同的处理方案

  1. “登录成功”->开始聊天
  2. “密码错误”->需要重新输入
  3. “用户名不存在”->需要重新输入

如果登录成功,就可以开始聊天,此时的聊天,一个人发消息给服务端,服务端接收之后需要群发给所有人。

将登录操作抽成一个方法(养成一个习惯,一个方法里就实现一个功能)

首先我们要实现数据(用户名和密码)的传输

客户端

public static void login(Socket socket) throws IOException {//获取输出流,给服务器发送数据BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));Scanner sc = new Scanner(System.in);System.out.println("请输入用户名:");String username = sc.nextLine();System.out.println("请输入密码:");String  password= sc.nextLine();//拼接//username=zhangsan&password=123StringBuilder sb = new StringBuilder();sb.append("username=").append(username).append("&password=").append(password); //写入数据bw.write(sb.toString());bw.newLine();bw.flush();
}

服务器

public class Server {static ArrayList<Socket> list = new ArrayList<>();public static void main(String[] args) throws IOException {ServerSocket serverSocket = new ServerSocket(1000);//获取文件中正确的用户名及密码Properties prop = new Properties();FileInputStream fis = new FileInputStream("D:\\java\\basic-code\\online-chat-room\\客户数据.txt");prop.load(fis);//只要有一个客户端,就开一条线程处理while (true) {Socket socket = serverSocket.accept();System.out.println("有客户端来连接");new Thread(new MyRunnable(socket,prop)).start();}}
}
//线程的作用:传输工具
class MyRunnable implements Runnable {Socket socket;Properties prop;public MyRunnable(Socket socket, Properties prop) {//连接客户端this.socket = socket;this.prop = prop;}public void run() {try {BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));String line = br.readline();System.out.println(line);}} catch (IOException e) {throw new RuntimeException(e);}}
}

运行结果

//客户端
服务器已经连接成功
=========欢迎来到小周聊天室=========
1.登录
2.注册
请选择选项:
1
请输入用户名:
zhangsan
请输入密码:
123
//服务器
有客户端来连接
username=zhangsan&password=123

要使右边的服务器知道客户端进行的是登录操作还是注册操作,从而让服务器进行不同的操作,我们可以从客户端发送一条信息,告诉服务器,是登录操作还是注册操作。

所以我们要对上面的代码进行适当的改写。

客户端发送两次数据

//第一次写入执行登录操作(告诉服务器)
bw.write("login");
bw.newLine();
bw.flush();
//第二次写入数据
bw.write(sb.toString());
bw.newLine();
bw.flush();

服务端根据客户端发来的第一次数据进行判断,进行选择操作

String choose = br.readLine();
switch (choose) {case "login" -> System.out.println("用户选择了登录操作");case "register" -> System.out.println("用户选择了注册操作");
}            

服务器根据传来的信息(login),进行用户名和密码的校验

//该方法要获取输入的用户名和密码
//并进行判断public void login(BufferedReader br) throws IOException {System.out.println("用户选择了登录操作");String userinfo = br.readLine();//username=zhangsan&password=123//对搜到的用户信息进行拆分String[] userinfoArr = userinfo.split("&");String usernameInput = userinfoArr[0].split("=")[1];String passwordInput = userinfoArr[1].split("=")[1];System.out.println("用户名为:"+usernameInput);System.out.println("密码为:"+passwordInput);//判断if(prop.containsKey(usernameInput)){//用户名存在,则继续判断密码是否正确String rightpassword = prop.get(usernameInput)+"";//判断密码是否正确if(rightpassword.equals(passwordInput)){//提示用户登录成功writeMessage2Client("1");}else{//密码错误,直接回写writeMessage2Client("2");}}else{//用户名不存在,直接回写writeMessage2Client("3");}}

根据客户端发来的用户名和密码,与文件中的数据进行判断是否一致,判断完之后再回写给客户端因为回写的代码是通用的,内容不一样,所以可以把它抽成一个方法。

public void writeMessage2Client(String message) throws IOException {BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));bw.write(message);bw.newLine();bw.flush();}

当用户输错了密码,需要进行重新登录,所以要将读取客户端发来的数据的代码进行while(true)循环。

while (true) {String choose = br.readLine();switch (choose) {case "login" -> login(br);case "register" -> System.out.println("用户选择了注册操作");}}

运行结果

服务器已经连接成功
=========欢迎来到小周聊天室=========
1.登录
2.注册
请选择选项:
1
请输入用户名:
zhangsan
请输入密码:
12

输入完之后发现没有运行结果,聪明的你肯定发现了,是因为客户端还有写接收数据的代码。

//创建输入流BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));String s = br.readLine();//可以用状态码代表传输的文字,比较方便//1:登陆成功 2:密码错误 3:用户不存在if(s.equals("1")){System.out.println("登录成功,可以开始聊天");//写一个while(true)循环,表示开始聊天while(true){}}else if(s.equals("2")){System.out.println("密码有误,请重新登录");}else if(s.equals("3")){System.out.println("用户名不存在,请先注册");}

运行结果

服务器已经连接成功
=========欢迎来到小周聊天室=========
1.登录
2.注册
请选择选项:
1
请输入用户名:
zhangsan
请输入密码:
12
密码有误,请重新登录
=========欢迎来到小周聊天室=========
1.登录
2.注册
请选择选项:
1
请输入用户名:
wuyu
请输入密码:
123
用户名不存在,请先注册
=========欢迎来到小周聊天室=========
1.登录
2.注册
请选择选项:
1
请输入用户名:
zhangsan
请输入密码:
123
登录成功,可以开始聊天

接下来我们实现客户端和服务器之间的聊天连接

服务器

因为可能会有很多个客户端向服务器发送数据,所有传递的参数加一个usernameInput

//接收客户端发来的信息
talk2All(br,usernameInput);
private void talk2All(BufferedReader br, String usernameInput) throws IOException {while (true) {String message = br.readLine();System.out.println(usernameInput+"发送过来消息:"+message);}}}

客户端

//发出消息,显示在控制台
talk2All(bw);
private static void talk2All(BufferedWriter bw) throws IOException {Scanner sc = new Scanner(System.in);while(true){System.out.println("请输入你要说的话");String message = sc.nextLine();bw.write(message);bw.newLine();bw.flush();}}

群发的逻辑

服务器要向每一个客户端回写数据

功能实现:

服务端可以将所有用户的Socket对象存储到一个集合中

当需要群发消息时,可以遍历集合发给所有的用户

此时的服务端,相当于做了一个消息的转发

服务器群发逻辑

在服务器的成员位置定义一个静态集合

static ArrayList<Socket> list = new ArrayList<>();

有客户端来连接,就把它的socket保存起来

Server.list.add(socket);

然后服务器的talk2All除了打印客户端发来的数据,还要进行群发的操作,遍历集合,给每一个socket对象发送数据。

for(Socket s : Server.list){writeMessage2Client(s,usernameInput+"发送过来消息:"+message);}

因为原来的writeMessage2Client方法只能传递一个message回写的消息,不能传socket,所以我们需要对writeMessage2Client方法进行重载。

方法的重载

是指在同一个类中,方法名相同但参数列表不同的多个方法。这些方法可能会有不同的参数类型、参数数量或参数顺序。方法的重载允许我们根据不同的参数来调用同名的方法。

public void writeMessage2Client(Socket s, String message) throws IOException {BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));bw.write(message);bw.newLine();bw.flush();}

客户端群发逻辑

我们要思考我们在发送消息的时候,服务器可能会传一个消息过来,所以需要单独开一条线程,专门用来接收服务器发送过来的聊天记录,这条线程只负责接收并打印在控制台。

//单独开一条线程去接收服务器发送过来的聊天记录
new Thread(new ClientMyRunnable(socket)).start();
class ClientMyRunnable implements Runnable{Socket socket;public ClientMyRunnable(Socket socket){this.socket = socket;}@Overridepublic void run() {//循环,重复的接收while(true) {try {//接收服务器发来的聊天记录BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));String mag = br.readLine();System.out.println(mag);} catch (IOException e) {throw new RuntimeException(e);}}}
}

结束

我们来测验一下吧

//服务器
有客户端来连接
有客户端来连接
用户选择了登录操作
用户名为:zhangsan
密码为:123
用户选择了登录操作
用户名为:lisi
密码为:1234
zhangsan发送过来消息:你好呀,李四
lisi发送过来消息:你好呀,张三
//客服端1
服务器已经连接成功
=========欢迎来到小周聊天室=========
1.登录
2.注册
请选择选项:
1
请输入用户名:
zhangsan
请输入密码:
123
登录成功,可以开始聊天
请输入你要说的话
你好呀,李四
请输入你要说的话
zhangsan发送过来消息:你好呀,李四
lisi发送过来消息:你好呀,张三
//客户端2
服务器已经连接成功
=========欢迎来到小周聊天室=========
1.登录
2.注册
请选择选项:
1
请输入用户名:
lisi
请输入密码:
1234
登录成功,可以开始聊天
请输入你要说的话
zhangsan发送过来消息:你好呀,李四
你好呀,张三
请输入你要说的话
lisi发送过来消息:你好呀,张三

结束了,希望能为大家提供帮助,感谢关注!

http://www.dtcms.com/a/266590.html

相关文章:

  • 使用reactor-rabbitmq库监听Rabbitmq
  • Go中使用Google Authenticator
  • 东软8位MCU低功耗调试总结
  • 如何使用python识别出文件夹中全是图片合成的的PDF,并将其移动到指定文件夹
  • 【ASP.NET Core】REST与RESTful详解,从理论到实现
  • 当前主流AI智能代理框架对比分析报告
  • 分布式光伏监控系统防孤岛保护装置光功率预测
  • 【论文阅读】VARGPT-v1.1
  • Webpack构建工具
  • node.js下载教程
  • 机器学习数学基础与Python实现
  • 机器学习在智能建筑中的应用:能源管理与环境优化
  • 每日问题总结记录
  • 一、如何用MATLAB画一个三角形 代码
  • 基于AR和SLAM技术的商场智能导视系统技术原理详解
  • 京东小程序JS API仓颉改造实践
  • 深圳安锐科技发布国内首款4G 索力仪!让斜拉桥索力自动化监测更精准高效
  • 【centos8服务如何给服务器开发3306端口】
  • Python 中线程和进程在实际项目使用中的区别和联系
  • 解决HttpServletRequest无法获取@RequestBody修饰的参数
  • Java并发性能优化|读写锁与互斥锁解析
  • Python 中的可迭代对象与迭代器:原理与项目实战
  • 【Verilog】parameter、localparam和 `define的区别
  • Android View的绘制原理详解
  • 基于虚拟化技术的网闸安全交换:物理隔离时代的智能数据流通引擎
  • 最快实现的前端灰度方案
  • python打卡day58@浙大疏锦行
  • 算法19天|回溯算法:理论基础、组合、组合总和Ⅲ、电话号码的字母组合
  • 用原生 JS + Vue 实现一套可复用的前端错误监控系统
  • Python 机器学习核心入门与实战进阶 Day 2 - KNN(K-近邻算法)分类实战与调参