Java实现多对多网络通讯的流程
基本流程
客户端发送信息(指定目标客户端)至固定的一个服务端,服务端接收信息进行处理后发送至相应的客户端
通讯核心类
Socket类与流相辅相成,完成通讯。在accept方法返回了一个Socket对象后,获取socket的输入输出流,就可以接收信息或发送信息了,以一对一为例:
服务端 :
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) throws IOException {
// 创建服务端套接字并指定端口
ServerSocket server = new ServerSocket(88);
// 接收创建建立,返回连接创建好后服务器的socket对象
Socket socket = server.accept();
InputStreamReader reader = new InputStreamReader(socket.getInputStream());
BufferedReader bufferedReader = new BufferedReader(reader);
// 获取请求
String request = bufferedReader.readLine();
System.out.println("client say:" + request);
// 写到输出流传递给客户端
PrintWriter writer = new PrintWriter(socket.getOutputStream());
String line = "hello too";
writer.println(line);
writer.flush();
// 关闭处理流的工具、socket套接字、服务套接字
writer.close();
bufferedReader.close();
socket.close();
server.close();
}
}
客户端 :
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class Client {
public static void main(String[] args) throws IOException {
// 创建socket连接,指明其地址和端口
Socket socket = new Socket("127.0.0.1", 88);
// 获取套接字的输出流,输出hello
PrintWriter writer = new PrintWriter(socket.getOutputStream());
String readLine = "Hello";
writer.println(readLine);
writer.flush();
// 从套接字的输入流中获取信息
InputStreamReader reader = new InputStreamReader(socket.getInputStream());
BufferedReader bufferedReader = new BufferedReader(reader);
String respond = bufferedReader.readLine();
System.out.println("server say:" + respond);
bufferedReader.close();
writer.close();
socket.close();
}
}
运行结果:
需要注意的是accept方法在没有连接的时候会阻塞,而导致后面的代码无法执行,在接下来的多对多通讯中需要依靠多线程来解决这个问题。
多对多代码实现
为了方便服务端和客户端对信息的处理,解析。首先定义一个消息类,定义属性分别为端口的本地地址,发送的消息内容,发送的目标地址。定义静态方法:将字符串解析为该类实例,处理消息的收发:
import com.alibaba.fastjson.JSON;
import java.io.Serializable;
import com.alibaba.fastjson.JSON;
import java.io.*;
import java.net.Socket;
public class SocketMessage implements Serializable {
private String key;
private String to;
private String content;
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getTo() {
return to;
}
public void setTo(String to) {
this.to = to;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public static void writeTargetMessage(SocketMessage message, Socket socket) throws IOException {
PrintWriter writer = new PrintWriter(socket.getOutputStream());
// 统一字符串标准,以便于服务端解析
writer.println(JSON.toJSONString(message));
writer.flush();
}
public static SocketMessage parseSocket(InputStream is) throws Exception {
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String info = reader.readLine();
return parseSocketByStr(info);
}
public static SocketMessage parseSocketByStr(String str) {
SocketMessage socketMessage = null;
try {
socketMessage = JSON.parseObject(str, SocketMessage.class);
} catch (Exception ex) {
throw new RuntimeException("socket之间通讯不能不使用SocketMessage");
}
return socketMessage;
}
@Override
public String toString() {
// 通过 阿里巴巴 的FastJson 库,将一个对象转换为 字符串 ,统一标准,以便于将字符串解析为该类
return JSON.toJSONString(this);
}
}
再单独定义一个服务端的消息处理类,该类用于发送消息至特定的客户端,所以定义两个属性,1.发送的消息,2.目标客户端的套接字:
import java.net.Socket;
public class SocketMessageHandler {
SocketMessage sm;
Socket targetSocket;
public SocketMessageHandler(SocketMessage sm,Socket targetSocket) {
this.sm = sm;
this.targetSocket = targetSocket;
}
public void setSm(SocketMessage sm) {
this.sm = sm;
}
public void send() {
if (this.sm == null) {
return;
}
try {
System.out.println(sm.getContent());
// 发送
SocketMessage.writeTargetMessage(sm, this.targetSocket);
} catch ( Exception ex) {
ex.printStackTrace();
}
}
}
接下来进行服务端的定义,我们的服务端需要处理多个客户端的消息,所以要定义一个容器存放客户端地址,在此之前我们已经定义了处理服务端消息的SocketMessageHandler类,因为我们的最终目的是为了处理信息,所以可以直接将SocketMessageHandler类存放至容器。我们用map来存储,而key就是客户端的地址:
import com.issac.task_05.task.msg.SocketMessage;
import com.issac.task_05.task.msg.SocketMessageHandler;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
public class SocketServer {
// 存放消息处理器
private static final Map<String, SocketMessageHandler> clientContainer = new HashMap<>();
public static void main(String[] args) {
try {
ServerSocket ss = new ServerSocket(8888);
Socket accept;
while (true) {
accept = ss.accept();
SocketMessage msg = SocketMessage.parseSocket(accept.getInputStream()); // 获取信息
System.out.println("客户端建立连接:" + msg.getKey());
// 建立连接后将客户端地址存入容器
clientContainer.put(msg.getKey(), new SocketMessageHandler(msg, accept));
new ServerThread(accept, clientContainer).start();
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
在这里需要注意ServerSocket类的accept方法,在没有新连接的时候,该方法会阻塞,而之后的代码就无法执行了。我们在客户端与服务端连接成功之后进行消息收发的时候是没有新连接产生的,此时的阻塞导致无法进行通讯,于是乎我们需要再开辟一个线程,进行消息处理。那么我们定义一个继承Thread的消息处理类,将每次连接成功返回的套接字接收,进行信息处理。如此一来,只要有消息的传递该线程就可以进行获取:
import com.issac.task_05.task.msg.SocketMessage;
import com.issac.task_05.task.msg.SocketMessageHandler;
import java.io.InputStream;
import java.net.Socket;
import java.util.Map;
public class ServerThread extends Thread{
private Socket socket;
InputStream inputStream;
Map<String, SocketMessageHandler> clientContainer;
public ServerThread(Socket socket,Map<String, SocketMessageHandler> clientContainer){
this.socket = socket;
this.clientContainer = clientContainer;
}
public void run(){
try{
while (true){
// 将输入流中的数据解析为SocketMessage对象
inputStream = socket.getInputStream();
SocketMessage msg = SocketMessage.parseSocket(inputStream);
System.out.println(msg);
// 在容器中获取目标地址
SocketMessageHandler socketMessageHandler = clientContainer.get(msg.getTo());
// 设置需要传输的信息
socketMessageHandler.setSm(msg);
// 传输信息
socketMessageHandler.send();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
最后就是客户端了,每个客户端所对应的服务端都相同,在客户端写一个简易的菜单,选择接收或发送消息即可:
import com.issac.task_05.task.msg.SocketMessage;
import java.net.Socket;
import java.util.Scanner;
public class Client {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
Socket s = null;
try {
s = new Socket("localhost", 8888);
// 第一次启动,创建socket,向服务器发送我是谁
SocketMessage initMsg = getSocketMsg(s.getLocalSocketAddress().toString(), null, null);
System.out.println("开始与服务器建立连接: " + initMsg.toString());
SocketMessage.writeTargetMessage(initMsg, s);
// 开始 循环等待
while (true) {
System.out.println("===================menu=====================");
System.out.println("1:发送消息");
System.out.println("2:接收消息");
int choice = scanner.nextInt();
switch (choice){
case 1: // 发送消息
String target = input("请输入您要发给谁:");
String content = input("请输入您要发送的内容:");
System.out.println();
SocketMessage afterMsg = getSocketMsg(s.getLocalSocketAddress().toString(), target, content);
SocketMessage.writeTargetMessage(afterMsg, s);
break;
case 2: // 接收并打印消息
showRequiredMsg(s);
break;
default:
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
public static String input(String tip) {
Scanner input = new Scanner(System.in);
System.out.println(tip);
return input.next();
}
public static SocketMessage getSocketMsg(String localSocketAddress, String to, String content) {
SocketMessage socketMessage = new SocketMessage();
// to 为null的时候,说明只是对服务器的初始
socketMessage.setKey(localSocketAddress.replaceAll("\\/", ""));
socketMessage.setTo(to);
socketMessage.setContent(content);
return socketMessage;
}
public static void showRequiredMsg(Socket socket) throws Exception {
SocketMessage socketMessage = SocketMessage.parseSocket(socket.getInputStream());
String source = socketMessage.getKey();
String content = socketMessage.getContent();
System.out.println("接收到来自《"+source+"》的信息:"+content+"\n");
}
}
运行结果:
到此这篇关于Java实现多对多网络通讯的流程的文章就介绍到这了,更多相关Java多对多网络通讯内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341