Socket
概念
客户端多对多的实现的原理是把服务器作为一个中转站,中转站会会根据客户端的连接数量开辟用户线程,对接连接进来的客户端,客户端首先发送数据包给对接的用户线程, 数据包的内容包括 :
数据名 | Value |
---|---|
from | 信息起点 |
to | 信息终点 |
info | 发送信息 |
type | 发送信息类型 |
把数据包包装成一个java类如下 Message.java:
package chat;
import java.io.Serializable;
public class Message implements Serializable {
private String from;
private String to;
private String info;
private int type;
@Override
public String toString() {
return "Message{" +
"from='" + from + '\'' +
", to='" + to + '\'' +
", info='" + info + '\'' +
", type=" + type +
'}';
}
public Message(String from, String to, String info, int type) {
this.from = from;
this.to = to;
this.info = info;
this.type = type;
}
public void setFrom(String from) {
this.from = from;
}
public void setTo(String to) {
this.to = to;
}
public void setInfo(String info) {
this.info = info;
}
public void setType(int type) {
this.type = type;
}
public String getFrom() {
return from;
}
public String getTo() {
return to;
}
public String getInfo() {
return info;
}
public int getType() {
return type;
}
}
服务器端收到数据包后,从to中获取要传送到的客户端,并且把数据包发送给与其对接的服务器端线程,最后服务器端线程会将信息发送给对应的客户端
原理图示
问题待解决
运行服务器和两个客户端时命令台输出如下,我添加了一个输出语句在客户线程接收到输入流后输出 Message包内容,和一个在客户端传送包内容之前输出Message包内容
传入登录数据包,服务器端和输出:
Message{from=‘bin’, to=‘null’, info=‘null’, type=1}
这里成功传入from
和 type
第一次传输信息,info设置为服务器端输出:
Message{from=‘bin’, to=‘lili’, info=‘123’, type=2}
源代码
ChatServer.java:
package chat;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Vector;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ChatServer {
public static void main(String[] args) {
ExecutorService ex = Executors.newFixedThreadPool(5); //创建线程池,用于启动线程
Vector<ClientThread> vectors = new Vector<>(); //创建集合,把线程放入集合用于储存和遍历所有线程
try{
ServerSocket server = new ServerSocket(6666);
System.out.println("服务器已创建,等待连接...");
while (true) {
Socket socket = server.accept(); //等待客户端连接
System.out.println(socket.getInetAddress().getHostAddress()+"已连接"); //获取并输出客户端IP地址
ClientThread ct = new ClientThread(socket,vectors); //创建客户线程,用于对接客户端
ex.execute(ct); //启动客户线程 下跳ClientThread
}
}catch (IOException e){
e.printStackTrace();
}
}
}
class ClientThread implements Runnable{
private String name; //等于 message.getFrom
private Socket socket;
private Vector<ClientThread> vectors;
private ObjectOutputStream oos;
private ObjectInputStream ois;
private boolean flag = true;
public ClientThread(Socket socket,Vector<ClientThread> vectors){
this.socket = socket;
this.vectors = vectors;
vectors.add(this);
}
@Override
public void run() {
try {
oos = new ObjectOutputStream(socket.getOutputStream());
ois = new ObjectInputStream(socket.getInputStream());
while (flag){
Message message = (Message) ois.readObject();
System.out.println(message.toString());
int type = message.getType();
/*
判断传送过来的数据包的类型
是MessageType.TYPE_LOGIN则为登录
是MessageType.TYPE_SEND则为信息
*/
switch (type){
case MessageType.TYPE_LOGIN:
name = message.getFrom();
message.setInfo("欢迎");
oos.writeObject(message);
break;
case MessageType.TYPE_SEND:
String to = message.getTo();
// ClientThread ct;
// int size = vectors.size();
// for(int i = 0 ; i<size ; i++){
// ct = vectors.get(i);
// if(to.equals(ct.name) && ct != this){
// ct.oos.writeObject(message);
// break;
// }
// }
for(ClientThread ct :vectors){
if(to.equals(ct.name) && ct != this){
ct.oos.writeObject(message);
break;
}
}
break;
}
}
ois.close();
oos.close();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
ChatClient.java
package chat;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ChatClient {
public static void main(String[] args) {
ExecutorService es = Executors.newSingleThreadExecutor(); //创建单线程池
Scanner in = new Scanner(System.in);
Boolean flag = true;
try {
Socket socket = new Socket("localhost", 6666);
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
System.out.println("请输入您的昵称");
String name = in.nextLine();
Message message = new Message(name,null,null,MessageType.TYPE_LOGIN);
oos.writeObject(message);
message = (Message) ois.readObject();
System.out.println(message.getInfo()+":"+message.getFrom());
es.execute(new ReadInfo(ois));
message.setType(MessageType.TYPE_SEND);
System.out.println("[To Who ?]:");
message.setTo(in.nextLine());
System.out.println("成功建立与" + message.getTo() + "的连接...请开始会话(输入quit退出)");
while (flag) {
message.setInfo(in.nextLine());
System.out.println(message.toString());
oos.flush();
oos.writeObject(message);
}
// while (flag) {
// System.out.println("To:");
// message.setTo(in.nextLine());
// message.setType(MessageType.TYPE_SEND);
// System.out.println("info:");
// message.setInfo(in.nextLine());
// oos.writeObject(message);
// }
}catch (IOException |ClassNotFoundException e){
e.printStackTrace();
}
}
}
class ReadInfo implements Runnable{
private ObjectInputStream in;
private boolean flag = true;
public void setFlag(boolean flag){
this.flag = flag;
}
public ReadInfo(ObjectInputStream in){
this.in = in;
}
@Override
public void run() {
try {
while (flag){
Message message = (Message) in.readObject();
System.out.println("["+message.getFrom()+"] :"+message.getInfo());
}
if(in!=null){
in.close();
}
}catch (IOException | ClassNotFoundException e){
e.printStackTrace();
}
}
}