Java实战 | 网络聊天室 (二) | 实现管理员反馈信息给用户 | 引用多线程机制 | 添加注册登陆功能

本文介绍了如何使用Java实现一个网络聊天室,包括服务端接收消息的机制、多线程的应用、服务端反馈机制的建立以及添加注册登录功能。通过创建ServerReadThread和ServerReadyThread处理客户端连接和消息,使用DataUtils和Login类处理用户信息。文章还探讨了多线程的扩展性和安全性问题。
摘要由CSDN通过智能技术生成

笔者: unirithe
日期: 11/20/2021

上一篇 【Java实战】网络聊天室 (一) 简单实现用户发消息给后台管理员

运行环境


  • JDK8
  • IDEA

实现效果


在这里插入图片描述

1. 探索服务端接收消息的机制


在上一次的实践中,使用缓冲输入输出流通过套接字进行网络通信,但是目前只能实现使用客户端发送消息给服务端,服务端无法做出回应,这一次就对此问题进行优化。

由于是初次使用ServerSocket类,有一些机制需要自己探索,比如在Server中这段:


while(true){
   
Socket accept = serverSocket.accept();
BufferedReader r = new BufferedReader(new InputStreamReader(accept.getInputStream()));

String message;
while((message = r.readLine()) != null)
	System.out.println("收到: " + message);
}

最外层使用一个while循环不断执行,接收客户端套接字的程序,同时根据套接字的字节输入流而创建缓冲输入流,进而读取来自客户端的数据,那么服务端最终会执行多少次 while (true) 呢?
是否在客户端发送一次消息过后,服务端就结束一次while循环了呢?只需一个变量就可以解决这个疑问,测试代码如下:

int i = 0; 
while(true){
   
	// ... 前面部分和之前一样

	System.out.println("-----" + (++i));
}

接下来先后打开服务端、客户端,并在客户端发送三条测试消息,测试结果如下:
在这里插入图片描述

答案是:while (true) 只执行了一次,而且一直没有结束.。

这意味着服务端主线程一直在执行 while(message = r.rindLine() != null); 的语句。

现在把while 改成 if ,同时把socket的获取放在前面,否则会一直阻塞进程,测试效果如下:

Socket accept = serverSocket.accept();
while(true){
   
	//...
	if(message=r.rindLine())!=null){
   
		// ...
	}
}

在这里插入图片描述

成功解决了服务端接受一个客户端的线程阻塞问题,接下来测试两个客户端,通过修改端口类的连接IP实现。

在这里插入图片描述
很明显,由于 服务端获取socket的代码必须放在外面,否则无法一直执行while(true) 的所有内容(线程阻塞),但是在主线程,服务端必须要随时等待客户端发来的消息,此时也有其他客户端连接的可能,所以这里需要引用多线程的机制,使用Java.lang包下的Thread类实现该过程。

2 多线程机制的应用


实现多线程的方法主要有两种

方式一:实现Runnable接口,重写Run方法

class RunnableImpl implements Runnable{
   

	@Override
	public void run(){
   
		// ...
	}
}

// 使用通过 new Thread(new RunnableImpl()).start()

方式二:继承Thread类,重写Run方法

class myThread extends Thread{
   

	@Override
	public void run(){
   
		// ...
	}
}
// 使用通过 new myThread().start();

它们的区别是:

实现接口方式,还可以继承其他的类,具有更高的扩展性,但只有多线程的run方法。

继承的方式,具有原生Thread的方法,更加全面,但扩展性低,无法再继承第二个类。

当有多个客户端连接服务端时,即多个用户需要管理员进行管理,对于每个用户,管理员需要进行不同的约束,当然也有共性,在程序实现中,给每个用户分配一个线程,这样管理员就不用一一的对用户进行管理,提高了并发性。

现对服务端添加两个线程类,一是负责随时接待连接的客户端,二是针对响应每个客户端的消息。

ServerReadThread.java

该类负责处理一个客户端的消息发送,其中的socket对象是与服务端连接时创建的socket,所以可以根据该对象进行数据通信。

package App;

import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;

public class ServerReadThread extends Thread{
   
    private Socket socket;
    private DataInputStream in;
    public ServerReadThread(Socket socket){
   
        this.socket = socket;
        try {
   
            in = new DataInputStream(socket.getInputStream());
        } catch (IOException e) {
   
            System.out.println("获取套接字输入流对象失败");
        }
    }
    @Override
    public void run() {
   
        while (true) {
   
            String info;
            try {
   
                if ((info = in.readUTF()) != null)
                    System.out.println(info);
            } catch (IOException e) {
   
                e.printStackTrace();
            }
        }
    }
}

ServerReadyThread.java

该类负责处理每个客户端的连接,其中的serverSocket对象是创建服务端时产生的套接字,通过该对象可以调用accept()方法接收到连接的客户端套接字。

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerReadyThread extends Thread{
   
    private ServerSocket serverSocket;
    public ServerReadyThread(ServerSocket serverSocket){
   
        this.serverSocket = serverSocket;
    }
    @Override
    public void run() {
   
        while (true){
   
            try {
   
                Socket accept = serverSocket.accept();
                new ServerReadThread(accept).start();
                Server.list_socket.add(accept);
            } catch (IOException e) {
   
                e.printStackTrace();
            }

        }
    }
}

Server.java

该类是服务端的主要实现类,采用多线程过后,主线程可以不用while(true)循环,

其中的 list_socket 存储当前连接的所有客户端套接字,在客户端连接时会添加进来,这样方便主线程可以对客户端进行消息反馈。

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;

public class Server {
   
    private final static int port = 6666;
    public final static ArrayList<Socket> list_socket = new ArrayList<>(5);

    public static void main(String[] args) throws IOException {
   
        System.out.println("服务端启动");
        int i = 0;
        ServerSocket serverSocket = new ServerSocket(port);
        // 启动负责连接的线程
        new ServerReadyThread(serverSocket).start();
    }
}

3. 服务端的反馈机制


通过多线程机制,现在服务端的主线程已经可以继续执行其他的操作,比如再添加更多的多线程。

之前所有客户端套接字存储在了 list_sockets对象里,既然是相通的套接字,那么服务端同样可以发送消息给出去,只要客户端也做相应的接收即可。

再次使用多线程机制,这次在客户端类设计多线程负责接收来自服务端的消息,同时在服务端添加发送消息的代码。

Server.java

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.Scanner;

public class Server {
   
    private final static int port = 6666;
    public final static ArrayList<Socket> list_socket = new ArrayList<>(5);
    public static Scanner in = new Scanner(System.in);
    public static void main(String[] args) throws IOException {
   
        System.out.println("服务端启动");
        int i = 0;
        ServerSocket serverSocket = new ServerSocket(port);
        // 启动负责连接的线程
        new ServerReadyThread(serverSocket).start();
        tips();
        while (true) {
   
            String operation = in.nextLine();
            switch (operation){
   
                case "1":
                    sendMsgToClient();
                    break;
                default:
                    System.out.print(">> 不支持当前指令,请重新输入\n>> ");
            }
            tips();
        }
    }
    public static void tips(){
   
        System.out.println("==========欢迎访问服务端========");
        System.out.println("|      现在可进行的操作如下     |");
        System.out.println("|   1. 向指定客户端发送消息     |");
        System.out.println("================================");
        System.out.printf(">> ");
    }
    public static void sendMsgToClient(){
   
        showAllClient();
        System.out
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值