多客户端之间的通信
服务器可以与多个客户端实现通信了,那我们真正的目的是要实现多个客户端之间的通信,使用TCP协议实现的方案是:
客户端的数据包通过服务器中转,发送到另一个客户端,如下图所示:
这个数据包 包含的内容
客户端A要发送消息给客户端B
首先发送给服务器中处理客户端A的线程A1
A1检索到A要发送给B 所以A1联系B1 通过B1回传给客户端B 完成通信
然后吧 这个服务器中的线程A1 B1就有了联系 所以不能再单独放了
把他们统一的管理起来 用 集合 的方式 Vector
MessageType
package com.vince.communication;
/**
* Created by vince on 2017/6/7.
*/
public final class MessageType {
public static final int TYPE_LOGIN = 0x1;//登录消息类型
public static final int TYPE_SEND = 0x2;//发送消息的类型
}
消息包 Message
一会我们要发送这个消息对象 怎么发送 这里待会我们使用流发送 所以对象要序列化
package com.vince.communication;
import java.io.Serializable;
/**
* Created by vince on 2017/6/7.
* 消息包
*/
public class Message implements Serializable{
private String from;//发送者
private String to;//接收者
private int type;//消息类型
private String info;//消息
public String getFrom() {
return from;
}
public void setFrom(String from) {
this.from = from;
}
public String getTo() {
return to;
}
public void setTo(String to) {
this.to = to;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
public Message() {
}
public Message(String from, String to, int type, String info) {
this.from = from;
this.to = to;
this.type = type;
this.info = info;
}
@Override
public String toString() {
return "Message{" +
"from='" + from + '\'' +
", to='" + to + '\'' +
", type=" + type +
", info='" + info + '\'' +
'}';
}
}
服务器端
package com.vince.communication;
import javax.security.sasl.SaslClient;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
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;
/**
* Created by vince on 2017/6/7.
* 服务器端
*/
public class Server {
public static void main(String[] args) {
//保存客户端处理的线程
Vector<UserThread> vector = new Vector<>();
ExecutorService es = Executors.newFixedThreadPool(5);
//创建服务器端的Socket
try {
ServerSocket server = new ServerSocket(8888);
System.out.println("服务器已启动,正在等待连接...");
while(true){
Socket socket = server.accept();
UserThread user = new UserThread(socket,vector);
es.execute(user);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 客户端处理的线程
*/
class UserThread implements Runnable{
private String name;//客户端的用户名称(唯一)
private Socket socket;
private Vector<UserThread> vector;//客户端处理线程的集合
private ObjectInputStream ois;
private ObjectOutputStream oos;
private boolean flag = true;
public UserThread(Socket socket,Vector<UserThread> vector){
this.socket = socket;
this.vector = vector;
vector.add(this);
}
@Override
public void run() {
try{
System.out.println("客户端"+socket.getInetAddress().getHostAddress()+"已连接");
ois = new ObjectInputStream(socket.getInputStream());
oos = new ObjectOutputStream(socket.getOutputStream());
while (flag){
//读取消息对象
Message msg = (Message)ois.readObject();
int type = msg.getType();
switch (type){
case MessageType.TYPE_SEND:
String to = msg.getTo();
UserThread ut;
int size = vector.size();
for (int i = 0; i < size; i++) {
ut = vector.get(i);
if (to.equals(ut.name) && ut!=this){
ut.oos.writeObject(msg);
break;
}
}
break;
case MessageType.TYPE_LOGIN:
name = msg.getFrom();
msg.setInfo("欢迎你:");
oos.writeObject(msg);
break;
}
}
ois.close();
oos.close();
}catch (IOException | ClassNotFoundException e){
e.printStackTrace();
}
}
}
客户端
package com.vince.communication;
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;
/**
* Created by vince on 2017/6/7.
*/
public class Client {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
ExecutorService es = Executors.newSingleThreadExecutor();
try {
Socket socket = new Socket("localhost", 8888);
System.out.println("服务器连接成功");
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
//向服务器发送登录信息
System.out.println("请输入名称:");
String name = input.nextLine();
Message msg = new Message(name, null, MessageType.TYPE_LOGIN, null);
oos.writeObject(msg);
msg = (Message) ois.readObject();
System.out.println(msg.getInfo() + msg.getFrom());
//启动读取消息的线程
es.execute(new ReadInfoThread(ois));
//使用主线程来实现发送消息
boolean flag = true;
while(flag){
msg = new Message();
System.out.println("To:");
msg.setTo(input.nextLine());
msg.setFrom(name);
msg.setType(MessageType.TYPE_SEND);
System.out.println("Info:");
msg.setInfo(input.nextLine());
oos.writeObject(msg);
}
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
//读取消息
class ReadInfoThread implements Runnable {
private ObjectInputStream in;
public ReadInfoThread(ObjectInputStream in){
this.in = in;
}
private boolean flag = true;
public void setFlag(boolean flag) {
this.flag = flag;
}
@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();
}
}
}
先启动服务器
再启动两个客户端
第一个客户端 起名字·bin
第二个客户端叫vince
第一个客户端 bin 给vince发消息
说 good good study day day up
然后第二个客户端vince收到消息
然后给bin回消息
bin就收到了
启动客户端后 向服务器发送登录信息
第二步 启动接收线程 专门用来接收消息的线程
接下来使用主线程实现循环发送消息的过程
对服务器来说 服务器启动后 要读取
使用Vector来保存服务器当中的所有的用户线程任务Runnable(UserThread线程 )
每对应一个socket 即客户端的连接 就启动一个UserThread线程 每次都添加到Vector里面去
(这个添加的方法是 把对象vector添加到UserThread里面去 所以在构造方法 把UserThread自己添加进Vector里面)
然后开始启动线程 es.execute(user);
然后启动线程的第一步 run方法里面的
第一步 先把相关的流先构造完
然后就是循环 不断读取消息对象
读取的消息对象 取出类型进行判断
如果是登录信息 就返回一个欢迎你的消息
客户端就读取到欢迎信息 并输出出来 这是服务器端和客户端第一次交互
如果是发送消息的话
就取出要发送的对象
在集合Vector里面循环去找
找出所有的线程 判断要发送的人的名字跟线程保存的名字相不相同 (因为登陆的时候就把名字保存到线程了 所以他找的也是已经登陆的用户) 并且不是自己 毕竟这个循环会找到自己
找到了 就写到线程里面去 线程就会把消息发送出去