项目名称:
利用TCP协议,做一个带有登录,注册的无界面,控制版的多人聊天室。
知识点:
循环,判断,集合,IO,多线程,网络编程等
需求描述:
1.开始界面
==============欢迎来到聊天室===============
1.登陆
2.注册
请输入您的选择:
登陆界面输入自己的选项进入下一步
2.登陆界面
==============欢迎来到聊天室===============
1.登陆
2.注册
请输入您的选择:
1
请输入用户名:
zhangsan
请输入密码:
123
向服务器发送用户数据进行判断。
① 当客户端启动之后,要求让用户选择是登录操作还是注册操作,需要循环。
如果是登录操作,就输入用户名和密码,以下面的格式发送给服务端
username=zhangsan&password=123如果是注册操作,就输入用户名和密码,以下面的格式发送给服务端
username=zhangsan&password=123
登陆成功,服务器会向客户端返回登陆成功的状态码
登陆失败,服务器则会向客户端返回用户名不存在或者密码错误的状态码
客户端会识别状态码并在控制台输出状态码的信息
①.登陆成功
连接服务端成功!
==============欢迎来到聊天室===============
1.登陆
2.注册
请输入您的选择:
1
请输入用户名:
zhangsan
请输入密码:
123
登陆成功!请开始聊天
请输入聊天内容:
②.用户名不存在
连接服务端成功!
==============欢迎来到聊天室===============
1.登陆
2.注册
请输入您的选择:
1
请输入用户名:
dasdadas
请输入密码:
123456
用户名不存在!
③.密码错误
==============欢迎来到聊天室===============
1.登陆
2.注册
请输入您的选择:
1
请输入用户名:
zhangsan
请输入密码:
56468
密码错误,登陆失败!
3.注册界面
注册操作,客户端会先判断用户输入的用户名密码是否符合规范
用户名和密码要求:
要求1:用户名要唯一,长度:6~18位,纯字母,不能有数字或其他符号。
要求2:密码长度3~8位。第一位必须是小写或者大小的字母,后面必须是纯数字。
利用正则表达式进行判断
符合规范则将数据传输给服务器进行用户名的唯一性判断
如果用户名是唯一的,则将用户信息利用io流写入服务器端的配置文件中,并更新配置文件
==============欢迎来到聊天室===============
1.登陆
2.注册
请输入您的选择:
2
您正在进行注册操作
请输入用户名:
zhangsangggg
请输入密码:
a12348注册成功
用户名不唯一,服务器会向客户端返回 用户名存在
==============欢迎来到聊天室===============
1.登陆
2.注册
请输入您的选择:
2
您正在进行注册操作
请输入用户名:
zhangsan
请输入密码:
a123456
用户名存在,请重新操作!
4.聊天界面
如果成功登录,就可以开始聊天,此时的聊天是群聊,一个人发消息给服务端,服务端接收到之后需要群发给所有人
将每个人发送向服务器的消息转发给所有的客户端
服务器转发的主要逻辑:
服务器判断用户登陆成功之后,立即将该用户的客户端与服务器通信的socket对象存储在一个static关键字修饰的Arraylist集合中,这样每个服务器线程都可以共享这个集合,可以从这个集合中获取每个连接上服务器的客户端的socket对象,并利用这些socket对象中的io流进行消息的转发
界面展示
wangwu用户登陆进行聊天
==============欢迎来到聊天室===============
1.登陆
2.注册
请输入您的选择:
1
请输入用户名:
wangwu
请输入密码:
123456789
登陆成功!请开始聊天
请输入聊天内容:
你好
请输入聊天内容:
wangwu:你好
lisi:wangwu你好
zhangsan:你们好
晚上一起去吃饭吗?
请输入聊天内容:
wangwu:晚上一起去吃饭吗?
lisi:好的,一起去吧
zhangsan:好的一起去吧
lisi用户登陆进行聊天
连接服务端成功!
==============欢迎来到聊天室===============
1.登陆
2.注册
请输入您的选择:
1
请输入用户名:
lisi
请输入密码:
1234
登陆成功!请开始聊天
请输入聊天内容:
wangwu:你好
wangwu你好
请输入聊天内容:
lisi:wangwu你好
zhangsan:你们好
wangwu:晚上一起去吃饭吗?
好的,一起去吧
请输入聊天内容:
lisi:好的,一起去吧
zhangsan:好的一起去吧
zhangsan用户登陆进行聊天
连接服务端成功!
==============欢迎来到聊天室===============
1.登陆
2.注册
请输入您的选择:
1
请输入用户名:
zhangsan
请输入密码:
123
登陆成功!请开始聊天
请输入聊天内容:
wangwu:你好
lisi:wangwu你好
你们好
请输入聊天内容:
zhangsan:你们好
wangwu:晚上一起去吃饭吗?
lisi:好的,一起去吧
好的一起去吧
请输入聊天内容:
zhangsan:好的一起去吧
服务器端主要逻辑
服务端:
① 先读取本地文件中所有的正确用户信息。
② 当有客户端来链接的时候,就开启一条线程。
③ 在线程里面判断当前用户是登录操作还是注册操作。
④ 登录,校验用户名和密码是否正确
⑤ 注册,校验用户名是否唯一,校验用户名和密码的格式是否正确
⑥ 如果登录成功,开始聊天
⑦ 如果注册成功,将用户信息写到本地,开始聊天
主要代码:
客户端:
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
public class client {
public static void main(String[] args) throws IOException {
//连接服务端
Socket socket = new Socket("127.0.0.1",10000);
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);
String choose = sc.nextLine();
switch (choose){
case "1"-> login(socket);
case "2"-> register(socket);
default -> System.out.println("没有这个选择");
}
}
}
//登陆操作
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 name = sc.nextLine();
System.out.println("请输入密码:");
String password = sc.nextLine();
//向服务器传递状态码
output(bw,"1");
//将用户名和密码拼接成指定形式
StringBuilder sb = new StringBuilder();
sb.append("username=").append(name).append("&password=").append(password);
//把登陆数据写出到服务端
output(bw,sb.toString());
//获取服务器发回的登陆信息
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String loginCode = br.readLine();
if(loginCode.equals("1")){
System.out.println("登陆成功!请开始聊天");
//创建一个新的线程用于接受消息
new Thread(new clientThread(socket)).start();
//开始聊天
chat2All(bw);
} else if (loginCode.equals("2")) {
System.out.println("密码错误,登陆失败!");
} else if (loginCode.equals("3")) {
System.out.println("用户名不存在!");
}
}
//聊天操作
public static void chat2All(BufferedWriter bw) throws IOException {
while (true){
System.out.println("请输入聊天内容:");
Scanner sc = new Scanner(System.in);
String massage = sc.nextLine();
output(bw,massage);
}
}
//注册操作
public static boolean register(Socket socket) throws IOException {
//输入要注册的用户名和密码
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
Scanner sc = new Scanner(System.in);
System.out.println("您正在进行注册操作");
System.out.println("请输入用户名:");
String name = sc.nextLine();
System.out.println("请输入密码:");
String password = sc.nextLine();
//向服务器传递状态码
output(bw,"2");
//对用户名密码进行判断是否判断符合规则
boolean nameBool = name.matches("[a-zA-Z]{6,18}");
boolean passwordBool = password.matches("[a-zA-Z]\\d{2,7}");
if(nameBool){
if(passwordBool){
//符合规范则将用户名密码传递给服务器
StringBuilder sb = new StringBuilder();
sb.append("username=").append(name).append("&password=").append(password);
output(bw,sb.toString());
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String registerCode = br.readLine();
if (registerCode.equals("4")) {
System.out.println("用户名存在,请重新操作!");
} else if (registerCode.equals("5")) {
System.out.println("注册成功");
return true;
}
}else {
System.out.println("密码不符合规范,密码长度3~8位。第一位必须是小写或者大小的字母,后面必须是纯数字。");
}
}else{
System.out.println("用户名不符合规范,长度:6~18位,纯字母,不能有数字或其他符号。");
}
return false;
}
public static void output(BufferedWriter bw , String massage) throws IOException {
bw.write(massage);
bw.newLine();
bw.flush();
}
}
class clientThread implements Runnable{
Socket socket;
public clientThread(Socket socket){
this.socket = socket;
}
@Override
public void run() {
try {
while(true){
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String massage = br.readLine();
System.out.println(massage);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
服务器端:
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Properties;
public class server {
static ArrayList<Socket> list = new ArrayList<>();
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(10000);
Properties properties = new Properties();
properties.load(new FileReader("src\\userdata.txt"));
while(true){
Socket socket = ss.accept();
System.out.println("已连接客户端!");
Thread thread = new Thread(new thread(socket,properties));
thread.start();
}
}
}
class thread implements Runnable{
Socket socket;
Properties properties;
public thread(Socket socket,Properties properties){
this.socket = socket;
this.properties = properties;
}
@Override
public void run() {
try {
//读取客户端传递来的数据
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while(true){
String choose = br.readLine();
//对客户端传递的状态码进行判断
switch (choose) {
case "1" -> login();
case "2" -> register();
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
//注册
private void register() throws IOException {
System.out.println("用户进行注册操作");
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String userData = br.readLine();
//对传递过来的用户数据进行处理
String[] user = userData.split("&");
String name = user[0].split("=")[1];
String password = user[1].split("=")[1];
//对用户名进行判断是否唯一
if (properties.containsKey(name)) {
//用户名存在
output("4");
}else {
//用户名不存在则将用户数据写入userdata.txt中
properties.put(name,password);
properties.store(new FileWriter("src\\userdata.txt"),"test");
output("5");
}
}
//登陆
private void login() throws IOException {
System.out.println("用户正在进行登陆操作");
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String userData = br.readLine();
//处理接受的数据
String[] split = userData.split("&");
String name = split[0].split("=")[1];
String password = split[1].split("=")[1];
//读取本地文件验证用户名和密码是否正确
if (properties.containsKey(name)) {
//用户名存在
//判断密码是否正确
if (properties.get(name).equals(password)) {
//1.密码正确,登陆成功
output("1");
//登陆成功后将本线程的socket对象保存在list集合中
server.list.add(socket);
//进入聊天操作
chat2All(br,name);
}else{
//2.密码错误,登陆失败
output("2");
}
}else{
//用户名不存在
//输出提示或状态码
output("3");
}
}
private void chat2All(BufferedReader br,String name) throws IOException {
while (true){
//获取消息
String massage = br.readLine();
System.out.println(name+":"+massage);
//将消息转发
for (Socket s : server.list) {
output(s,name+":"+massage);
}
}
}
//向客户端输出提示码
private void output(String string) throws IOException {
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bw.write(string);
bw.newLine();
bw.flush();
}
private void output(Socket socket,String string) throws IOException {
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bw.write(string);
bw.newLine();
bw.flush();
}
}
基于黑马程序员相关课程编写,课程地址:黑马程序员Java零基础视频教程_下部(Java入门,含斯坦福大学练习题+力扣算法题和大厂java面试题)_哔哩哔哩_bilibili