创建NetSocketIoChat聊天系统
使用技术关键字有 Socket,Io流,Thread
step1:首先创建客户端与服务端以及实体类
Message实体类的创建
public class Message implements Serializable {
//序列化
private static final long serialVersionUID = 4497214422911483808L;
//操作类型
private Integer type;
//消息
private String content;
//用来登录
private String username;
private String password;
//给朋友发消息时,用于指定用户
private String friendUsername;
public void setType(Integer type) {
this.type = type;
}
public void setContent(String content) {
this.content = content;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public void setFriendUsername(String friendUsername) {
this.friendUsername = friendUsername;
}
public Integer getType() {
return type;
}
public String getContent() {
return content;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
public String getFriendUsername() {
return friendUsername;
}
//登录构造器
public Message(String username, String password,Integer type) {
this.username = username;
this.password = password;
this.type = type;
}
//登录,向服务器发消息
public Message(Integer type, String content, String username) {
this.type = type;
this.content = content;
this.username = username;
}
//向在线用户发消息
public Message(Integer type, String content, String username, String friendUsername) {
this.type = type;
this.content = content;
this.username = username;
this.friendUsername = friendUsername;
}
public Message() {
}
@Override
public String toString() {
return "Message{" +
"type=" + type +
", content='" + content + '\'' +
", username='" + username + '\'' +
", password='" + password + '\'' +
", friendUsername='" + friendUsername + '\'' +
'}';
}
}
常量进行打包存放
public class Constant {
public static final String SUCCESS = "SUCCESS";
public static final String FAIL = "FAIL";
public static final String SERVER_NAME = "server";
public static final String OK= "ok";
public static final String DEFAULT_PASSWORD = "123";
//保存在线的用户
public static final Map<String, Socket> ONLINE_USERS = new ConcurrentHashMap<>(8);
}
//操作类型
public class MessageType {
public static final int TO_SERVER = 2;
public static final int TO_FRIEND = 3;
public static final int TO_ALL = 4;
public static final int LOGIN = 1;
public static final int FORM_SERVER = 6;
public static final int RECEIVER = 5;
}
读写工具类打包
public class MsgUtils {
//读操作
public static Optional<Message> readMsg(InputStream inputStream) {
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(inputStream);
//封装成一个optional,将来使用的时候就可以避免空指针
return Optional.ofNullable((Message) ois.readObject());
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
return Optional.empty();
}
//向输出流写数据
public static void writeMsg(OutputStream outputStream,Message message) {
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(outputStream);
//封装成一个optional,将来使用的时候就可以避免空指针
oos.writeObject(message);
oos.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
ScannerUtil打包为后续操作提供便利
public class ScannerUtil {
private static final Scanner scanner = new Scanner(System.in);
public static String input(){
return scanner.next();
}
}
服务端与客户端创建
public class Server {
public static void main(String[] args){
try (
//创建端口服务
ServerSocket serverSocket = new ServerSocket()
){
//2.随便绑定端口
serverSocket.bind(new InetSocketAddress(8888));
System.out.println("服务器已经启动,在8888端口!");
//开始监听
//建立一个连接
while (true){
Socket accept = serverSocket.accept();
new ServerThread(accept).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
//客户端
Socket socket = new Socket();
//绑定服务端的端口
socket.connect(new InetSocketAddress(8888));
//给服务端发消息
//发消息
OutputStream outputStream = socket.getOutputStream();
InputStream inputStream = socket.getInputStream();
//一切就绪,开始进行登录操作
String username = null;
public static void main(String[] args) throws IOException {
Socket socket = new Socket();
//绑定服务端的端口
socket.connect(new InetSocketAddress(8888));
//给服务端发消息
//发消息
OutputStream outputStream = socket.getOutputStream();
InputStream inputStream = socket.getInputStream();
//开始登录工作
String username = null;
//表示当前登录的用户
while (true) {
if (username == null) {
username = login(outputStream, inputStream);
} else {
//打印菜单
printOrder();
String input = ScannerUtil.input();
//把scanner内容转为int类型
switch (Integer.parseInt(input)) {
case MessageType.LOGIN:
login(outputStream, inputStream);
case MessageType.TO_SERVER:
sendToServer(username, outputStream, inputStream);
break;
case MessageType.TO_FRIEND:
sendToFriend(username, outputStream, inputStream);
break;
case MessageType.TO_ALL:
sendToAll(username, outputStream, inputStream);
break;
case MessageType.RECEIVER:
receiveMsg(inputStream);
break;
default:
break;
}
}
}
}
step2:开始进行各项模块的验证
登录模块
//登录模块 客户端
//登录的方法
private static String login(OutputStream outputStream, InputStream inputStream) {
//登录
System.out.println("请您输入用户名:");
String name = ScannerUtil.input();
System.out.println("请输入密码");
String pwd = ScannerUtil.input();
Message message = new Message();
message.setType(MessageType.LOGIN);
message.setUsername(name);
message.setPassword(pwd);
//发送给服务器
MsgUtils.writeMsg(outputStream, message);
//接收来自服务的的消息
Optional<Message> msg = MsgUtils.readMsg(inputStream);
if (msg.isPresent() && Constant.SUCCESS.equals(msg.get().getContent())) {
return name;
}
return null;
}
//服务端验证
private void loginHandler(InputStream inputStream, OutputStream outputStream, Message message, Socket accept) {
//判断登录的逻辑
//1.message 不存在
if (message.getUsername() == null || !Constant.DEFAULT_PASSWORD.equals(message.getPassword())) {
MsgUtils.writeMsg(outputStream, new Message(MessageType.FORM_SERVER, Constant.FAIL, Constant.SERVER_NAME));
} else {
//验证成功放入在线用户map中
Constant.ONLINE_USERS.put(message.getUsername(), accept);
System.out.println(message.getUsername() + "登录成功!");
MsgUtils.writeMsg(outputStream, new Message(MessageType.FORM_SERVER, Constant.SUCCESS, "accept"));
}
}
给服务器发消息
//客户端
private static void sendToServer(String username, OutputStream outputStream, InputStream inputStream) {
System.out.print(username + ":");
String msg = ScannerUtil.input();
//给服务器发消息
MsgUtils.writeMsg(
outputStream, new Message(MessageType.TO_SERVER, msg, username)
);
//接收消息,服务器收到消息后回消息给客户端
Optional<Message> massage = MsgUtils.readMsg(inputStream);
massage.ifPresent(m -> System.out.println(m.getUsername() + ":" + m.getContent()));
}
//服务器端
private void sendToClient(InputStream inputStream, OutputStream outputStream, Message message) {
//接收消息并打印
System.out.println(message.getUsername() + ":" + message.getContent());
//给客户端回消息
MsgUtils.writeMsg(outputStream, new Message(MessageType.FORM_SERVER, Constant.OK, Constant.SERVER_NAME));
}
给指定在线用户发消息
既然是用户之间的通信那么就应该开启多线程进行多用户登录
//之前服务器端的操作已经进行封装
//开始监听
//建立一个连接
while (true){
Socket accept = serverSocket.accept();
new ServerThread(accept).start();
}
一个人发,一个人收,所以就要再弄两个方法一个收一个发
现在做发的操作
//客户端代码
//在while循环中加入方法,具体可看前面的Client完整代码
case MessageType.TO_FRIEND:
sendToFriend(username, outputStream, inputStream);
break;
//发送主方法
private static void sendToFriend(String username, OutputStream outputStream, InputStream inputStream) {
System.out.println("请输入好友的名字:");
String friend = ScannerUtil.input();
boolean flag = true;
while (flag) {
System.out.print(username + ":");
String msg = ScannerUtil.input();
//退出机制
if ("bye".equals(msg)) {
flag = false;
}
MsgUtils.writeMsg(
outputStream,new Message(MessageType.TO_FRIEND, msg, username, friend)
);
}
}
//接收方法
private static void receiveMsg(InputStream inputStream) {
while (true){
Optional<Message> massage = MsgUtils.readMsg(inputStream);
massage.ifPresent(m -> System.out.println(m.getUsername() + ":" + m.getContent()));
}
}
//---------------------------------------------------
//服务端
//给目标用户发送消息
private void sendToTarget( Message message) {
//先找到对应的socket
try {
Socket friendSocket = Constant.ONLINE_USERS.get(message.getFriendUsername());
MsgUtils.writeMsg(friendSocket.getOutputStream(),message);
} catch (IOException e) {
e.printStackTrace();
}
}
群发消息
群发需要用到多线程的知识,因此,在一开始把服务端的所有操作都打包起来,然后重写Thread的run方法,这样可以实现一个客户端操作对应一个线程
//线程创建如下
public class ServerThread extends Thread {
private Socket accept;
public ServerThread() {
}
//构造器,在server类中进行调用
public ServerThread(Socket accept) {
this.accept = accept;
}
@Override
public void run() {
try (InputStream inputStream = accept.getInputStream();
OutputStream outputStream = accept.getOutputStream()
) {
while (true) {
//调用readMsg方法准备接收客户端发来的消息
Optional<Message> message = MsgUtils.readMsg(inputStream);
if (message.isPresent()) {
//获得客户端输入内容
Message msg = message.get();
//判断客户端操作的类型,然后进行判断转跳到对应的处理方法
switch (msg.getType()) {
case MessageType.LOGIN:
loginHandler(inputStream, outputStream, msg, accept);
break;
case MessageType.TO_SERVER:
sendToClient(inputStream, outputStream, msg);
break;
case MessageType.TO_FRIEND:
sendToTarget(msg);
break;
case MessageType.TO_ALL:
sendToAll(msg);
break;
default:
break;
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
//server类中进行接收客户端传来的消息
while (true){
Socket accept = serverSocket.accept();
//每次创建一个Socket就启动一个线程,实现多用户用时在线
new ServerThread(accept).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
客户端写群发方法
private static void sendToAll(String username, OutputStream outputStream, InputStream inputStream) {
boolean flag = true;
while (flag) {
System.out.print(username + ":");
String msg = ScannerUtil.input();
if ("bye".equals(msg)) {
flag = false;
}
MsgUtils.writeMsg(
outputStream,new Message(MessageType.TO_ALL, msg, username)
);
}
}
服务端进行处理
private void sendToAll(Message message) {
//遍历map
for (Map.Entry<String,Socket> entry :Constant.ONLINE_USERS.entrySet()){
try {
MsgUtils.writeMsg(entry.getValue().getOutputStream(), message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
1万+

被折叠的 条评论
为什么被折叠?



