在本人上一篇博文中,曾讲到了网络编程的基础知识,并且在最后,实现了一个服务器处理两个客户端的情况,那么,本人在这篇博文中来讲解一下 我们该如何处理多个客户端的情况
因为在上一篇博文中《网络编程(1)——基础知识讲解》中,本人讲解了有关 网络编程 的基本知识点,那么,在这篇博文中,本人就不再对于较为简单的代码段进行过多的讲解了。
那么,回归主题。
首先,本人来给出一个抽象类:
package com.mec.network.common;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
public abstract class Communication implements Runnable {
private Socket socket;
private DataInputStream dis;
private DataOutputStream dos;
private volatile boolean goon;
private String ip;
public Communication(Socket socket) { //带参构造中,我们主要是对各成员的赋初值
this.socket = socket;
this.ip = socket.getInetAddress().getHostAddress(); //获取客户端的拨号ip
try {
this.dis = new DataInputStream(socket.getInputStream());
this.dos = new DataOutputStream(socket.getOutputStream());
goon = true;
new Thread(this).start(); //这里就用到了本人同专题之前的博文 《线程编程 小例》的知识,这一句是用来“启动”线程的
} catch (IOException e) {
e.printStackTrace();
}
}
public String getIp() {
return ip;
}
public void send(String message) { //这个方法的主要功能是“发送信息”
try {
dos.writeUTF(message);
} catch (IOException e) {
close();
}
}
public abstract void dealNetMessage(String message); //由于我们不知道掉用这个类的是服务器的类还是客户端的类,所以我们对于如何处理接收到的信息的操作还是未知的
public abstract void peerAbnormalDrop(); //这个方法的功能是:处理对端异常掉线
@Override
public void run() {
String message = null;
while (goon) {
// 侦听对端消息的发送
try {
message = dis.readUTF();
dealNetMessage(message);
} catch (IOException e) {
if (goon == true) {
// 处理异常掉线!
peerAbnormalDrop();
}
close(); //这里调用close()的目的主要是:使得goon为false,从而结束循环
}
}
close(); //这里调用close()的目的是:将dis、dos 和 socket都关闭掉,以保证调用这个类的服务器(或客户端)能够 正常下线
}
public void close() {
goon = false;
try {
if (dis != null) {
dis.close();
}
} catch (IOException e) {
} finally {
dis = null;
}
try {
if (dos != null) {
dos.close();
}
} catch (IOException e) {
} finally {
dos = null;
}
try {
if (socket != null && !socket.isClosed()) {
socket.close();
}
} catch (IOException e) {
} finally {
socket = null;
}
}
}
现在,本人再来给出两个接口来处理消息的传递:
首先是消息发送者的接口:
package com.mec.network.common;
public interface ISpeaker {
void addListener(IListener listener); // 增加客户端
void removeListener(IListener listener); // 删除客户端
}
下面则是消息接收者的接口:
package com.mec.network.common;
public interface IListener {
void processMessage(String message);
}
那么,有了上一篇博文的经验,我们就再来编写一个处理配置的接口:
package com.mec.network.common;
public interface INetConfig {
int DEFAULT_PORT = 54188;
}
本人现在来给出一个处理多客户端情况的服务器的类:
package com.mec.network.server;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import com.mec.network.common.Communication;
import com.mec.network.common.IListener;
import com.mec.network.common.INetConfig;
import com.mec.network.common.ISpeaker;
public class MulServer implements Runnable, ISpeaker {
private int port;
private ServerSocket server;
private boolean goon;
private List<IListener> listenerList;
public MulServer() {
this.listenerList = new ArrayList<>();
this.port = INetConfig.DEFAULT_PORT;
}
public void startup() throws IOException {
if (goon == true) {
speakOut("服务器已启动!");
return;
}
speakOut("启动服务器,请稍后……");
server = new ServerSocket(port);
speakOut("服务器启动成功!");
goon = true;
new Thread(this).start();
}
public void shutdown() {
if (goon == false) {
speakOut("服务器未启动!");
return;
}
close();
speakOut("服务器已宕机!");
}
public boolean isStartup() {
return goon;
}
private void close() {
goon = false;
try {
if (server != null && !server.isClosed()) {
server.close();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
server = null;
}
}
class InnerCommunication extends Communication {
public InnerCommunication(Socket socket) {
super(socket);
}
@Override
public void dealNetMessage(String message) {
System.out.println("来自客户端[" + getIp() + "]的消息:" + message);
if (message.equalsIgnoreCase("byebye")) {
speakOut("客户端[" + getIp() + "]下线!");
close();
return;
}
send("[" + message + "]");
}
@Override
public void peerAbnormalDrop() {
speakOut("客户端[" + getIp() + "]异常掉线!");
}
}
@Override
public void run() {
speakOut("开始侦听客户端连接请求……");
while (goon) { //这里的设定是:当goon为true时,一直增添要侦听的客户端
try {
Socket socket = server.accept();
String clientIp = socket.getInetAddress().getHostAddress();
speakOut("接收到客户端[" + clientIp + "]连接请求!"); // 发送消息
new InnerCommunication(socket);
} catch (IOException e) {
close();
}
}
close();
}
private void speakOut(String message) { // 给每一个“听众”(即:客户端)发送消息
for (IListener listener : listenerList) {
listener.processMessage(message); // 实际上是调用每一个听众的必须实现的processMessage()
}
}
@Override
public void addListener(IListener listener) { //这里是实现IListener这个接口所要完成的类
if (listenerList.contains(listener)) {
return;
}
listenerList.add(listener);
}
@Override
public void removeListener(IListener listener) { //这里是实现IListener这个接口所要完成的类
if (!listenerList.contains(listener)) {
return;
}
listenerList.remove(listener);
}
}
现在,相应地,本人来给出以一个Test类:
package com.mec.network.server.test;
import java.io.IOException;
import java.util.Scanner;
import com.mec.network.common.IListener;
import com.mec.network.server.MulServer;
public class ServerTest implements IListener {
public ServerTest() {
}
@Override
public void processMessage(String message) { // “阅读”消息
System.out.println(message);
}
public static void main(String[] args) {
MulServer server = new MulServer();
server.addListener(new ServerTest()); // “订阅”:增加客户端
Scanner in = new Scanner(System.in);
String command = "";
boolean finished = false;
while (!finished) {
command = in.next();
if (command.equalsIgnoreCase("startup")) { //本人设置:当在命令行输入字符串"startup"后,开始创建服务器
try {
server.startup();
} catch (IOException e) {
e.printStackTrace();
}
} else if (command.equalsIgnoreCase("shutdown")) { //本人设置:当在命令行输入字符串"shutdown"后,开始关闭服务器
server.shutdown();
} else if (command.equalsIgnoreCase("exit")) { //本人设置:当在命令行输入字符串"shutdown"后,退出程序
if (!server.isStartup()) {
finished = true;
} else {
System.out.println("服务器尚未宕机!");
}
}
}
in.close();
}
}
现在,本人来给出客户端的类:
package com.mec.network.client;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import com.mec.network.common.Communication;
import com.mec.network.common.INetConfig;
public class MulClient {
private String ip;
private int port;
private Socket socket;
private Communication communication;
public MulClient() {
this.ip = "localhost";
this.port = INetConfig.DEFAULT_PORT;
}
public void setIp(String ip) {
this.ip = ip;
}
public void setPort(int port) {
this.port = port;
}
public void send(String message) {
if (communication == null) {
return;
}
communication.send(message);
}
public void close() {
if (communication == null) {
return;
}
communication.close();
}
public void connectToServer() throws UnknownHostException, IOException {
this.socket = new Socket(ip, port);
communication = new Communication(this.socket) {
@Override
public void peerAbnormalDrop() {
System.out.println("服务器异常宕机,服务停止!");
}
@Override
public void dealNetMessage(String message) {
System.out.println("来自服务器的消息:" + message);
}
};
}
}
那么,接下来就是客户端的Test类:
package com.mec.network.client.test;
import java.io.IOException;
import java.net.UnknownHostException;
import java.util.Scanner;
import com.mec.network.client.MulClient;
public class ClientTest {
public static void main(String[] args) {
MulClient client = new MulClient();
try {
client.connectToServer();
Scanner in = new Scanner(System.in);
String command = "";
boolean finished = false;
while (!finished) {
command = in.next();
client.send(command);
if (command.equalsIgnoreCase("byebye")) { //我们设置:当客户端输入字符串"byebye"时,关闭此客户端,并在服务器显示该客户端下线
client.close();
finished = true;
}
}
in.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
那么,现在我们来看一下运行结果:
可以看到:服务器可以正常地启动和关闭。
可以看到:服务器可有连接多个客户端
可以看到:服务器可以区分客户端的正常和异常下线。
那么,可能有同学问了:那我们的那两个接口有什么用呢?
答曰:可以使得我们的服务器与客户端应付多种需求。例如:窗口编程…
所以我们编程也是要放长一点眼光的啊!
那么,有关网络编程的多客户端 现象的处理 知识点,在这里就讲解完毕了。
若是对上述知识点或代码有任何疑惑、意见或者建议,请在下方评论区提出,本人将尽早予以答复,觉得有所帮助的同学请留下小赞赞,谢谢!!!