Java中的多线程聊天应用程序| Set 1(服务器端编程)
先决条件:在套接字编程中引入线程
在上面的文章中,创建了一个简单的日期时间服务器,它使用线程同时处理多个用户请求。它解释了网络编程中线程的基本概念。只需稍加修改即可使用相同的概念来扩展上述想法并创建类似于 facebook messenger、whatsapp 等的聊天应用程序。
以下文章涵盖了此类应用程序的实现,并详细说明、限制及其解决方案。
在本集中,我们将讨论服务器端编程(Java),客户端编程(Java)在集2中讨论。
服务器端编程(Java)
1、服务器类:主要的服务器实现很简单,和上一篇类似。以下几点将有助于理解服务器实现:
- 服务器运行一个无限循环来继续接受传入的请求。
- 当请求到来时,它会分配一个新线程来处理通信部分。
- 服务器还将客户端名称存储到向量中,以跟踪连接的设备。向量存储当前请求对应的线程对象。帮助程序类使用此向量来查找要向其传递消息的收件人的名称。由于这个向量包含所有流,处理程序类可以使用它成功地将消息传递给特定的客户端。
- 调用 start() 方法。
2、ClientHandler类:和上一篇文章类似,我们创建了一个帮助类来处理各种请求。这一次,与套接字和流一起,我们引入了一个名称变量。这将保存连接到服务器的客户端的名称。以下几点将有助于理解 ClientHandler 的实现:
- 每当处理程序收到任何字符串时,它都会将其分解为消息和收件人部分。为此,它使用 Stringtokenizer 并以 '#' 作为分隔符。这里假设字符串始终采用以下格式:
message # recipient
- 然后它在连接的客户端列表中搜索收件人的名称,该列表作为向量存储在服务器中。如果它在客户端列表中找到收件人名称,它会在其输出流上转发消息,并在消息前加上发件人的名称。
Java
// Java implementation of Server side
// It contains two classes : Server and ClientHandler
// Save file as Server.java
import java.io.*;
import java.util.*;
import java.net.*;
// Server class
public class Server
{
// Vector to store active clients
static Vector ar = new Vector<>();
// counter for clients
static int i = 0;
public static void main(String[] args) throws IOException
{
// server is listening on port 1234
ServerSocket ss = new ServerSocket(1234);
Socket s;
// running infinite loop for getting
// client request
while (true)
{
// Accept the incoming request
s = ss.accept();
System.out.println("New client request received : " + s);
// obtain input and output streams
DataInputStream dis = new DataInputStream(s.getInputStream());
DataOutputStream dos = new DataOutputStream(s.getOutputStream());
System.out.println("Creating a new handler for this client...");
// Create a new handler object for handling this request.
ClientHandler mtch = new ClientHandler(s,"client " + i, dis, dos);
// Create a new Thread with this object.
Thread t = new Thread(mtch);
System.out.println("Adding this client to active client list");
// add this client to active clients list
ar.add(mtch);
// start the thread.
t.start();
// increment i for new client.
// i is used for naming only, and can be replaced
// by any naming scheme
i++;
}
}
}
// ClientHandler class
class ClientHandler implements Runnable
{
Scanner scn = new Scanner(System.in);
private String name;
final DataInputStream dis;
final DataOutputStream dos;
Socket s;
boolean isloggedin;
// constructor
public ClientHandler(Socket s, String name,
DataInputStream dis, DataOutputStream dos) {
this.dis = dis;
this.dos = dos;
this.name = name;
this.s = s;
this.isloggedin=true;
}
@Override
public void run() {
String received;
while (true)
{
try
{
// receive the string
received = dis.readUTF();
System.out.println(received);
if(received.equals("logout")){
this.isloggedin=false;
this.s.close();
break;
}
// break the string into message and recipient part
StringTokenizer st = new StringTokenizer(received, "#");
String MsgToSend = st.nextToken();
String recipient = st.nextToken();
// search for the recipient in the connected devices list.
// ar is the vector storing client of active users
for (ClientHandler mc : Server.ar)
{
// if the recipient is found, write on its
// output stream
if (mc.name.equals(recipient) && mc.isloggedin==true)
{
mc.dos.writeUTF(this.name+" : "+MsgToSend);
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
try
{
// closing resources
this.dis.close();
this.dos.close();
}catch(IOException e){
e.printStackTrace();
}
}
}
输出:
New client request received : Socket[addr=/127.0.0.1,port=61818,localport=1234]
Creating a new handler for this client...
Adding this client to active client list
New client request received : Socket[addr=/127.0.0.1,port=61819,localport=1234]
Creating a new handler for this client...
Adding this client to active client list
限制:
尽管上面的服务器实现设法处理了大多数场景,但上面定义的方法存在一些缺点。
- 上述程序的一个明显观察结果是,如果客户端数量增加,则处理程序类中的搜索时间会增加。为了避免这种增加,可以使用两个哈希映射。一种以名称为键,以活动列表中的索引为值。另一个以索引为键,关联的处理程序对象为值。这样,我们可以快速查找两个 hashmap 以匹配收件人。留给读者来实现这个hack以提高实现的效率。
- 需要注意的另一件事是,当用户断开与服务器的连接时,此实现效果不佳。由于在此实现中未处理断开连接,因此会引发很多错误。它可以像前面的基本 TCP 示例一样轻松实现。还留给读者在程序中实现此功能。
客户端程序(Java)与之前的文章有很大的不同,所以将在本系列的Set 2中讨论。
相关文章:多线程聊天应用 |设置 2