实现思路:
1. 首先用frame设计一个窗口用于消息的发送与读取
2. socket发送消息一般有两部分,客户端与服务器,两个端可以UDP和TCP进行交互,这边我选用有连接的TCP协议
3. 只要多个客户端连接同一个服务器就可以进行一对一或一对多联通,所以设置了可配置动态的IP让使用者自行配置接入点,并且端口固定,只要是不同主机,端口相同并不会冲突
4. 将客户端与服务器绑在一起运行(可以适应张三想和李四聊,连接了李四的服务器。李四想跟王五聊,连接了王五的服务器)对方主动连接自己的服务器自己也可以收到推送,因为启动时可以先将自己的连接socket保存一份到服务器的List集合中,服务器通过List中存放的连接进行信息推送
效果如下
由于本人只有一台电脑,同时启动了两个所以地址是一样的。如果想要本地启动两个只需要将代码复制两份分开运行即可,但需要更改客户端的端口,因为会端口冲突。但是服务器不需要更改端口,冲突了会默认启动一台,要联通只需要一个服务端即可
代码实现如下,有注释可自行了解
package com.example.mydemo;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.*;
import java.net.*;
import java.util.ArrayList;
import java.util.List;
/**
* @author 吴盈乐
* @date 2022/6/13
*/
public class Demo37 {
public static void main(String[] args) throws UnknownHostException {
window client = new window("Client37");
}
private static String name;
private static JTextField ipText;
private static JTextField strText;
private static JTextArea jTextArea;
private static final List<Socket> sockets = new ArrayList<>();
private static final Integer PORT = 6666;
private static final Socket socket= new Socket();
private static PrintWriter writer;
private static BufferedReader reader;
static class window extends Frame {
//由于服务器需要优先启动所以设置在静态代码块中
static {
new Thread(new Runnable() {
@Override
public void run() {
startServer();
}
}).start();
}
//设置聊天窗口0
public window(String tilte) {
super(tilte);
name=tilte;
setSize(350, 600);
setBackground(Color.ORANGE);
setLayout(new FlowLayout());
//设置文本域
jTextArea = new JTextArea(18, 28);
jTextArea.setFont(new Font("monospaced", Font.PLAIN, 18));
//只读
jTextArea.setEditable(false);
add(jTextArea);
//设置滚动条
JScrollPane textPanel = new JScrollPane(jTextArea);
//textPanel.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
//textPanel.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
add(textPanel);
//设置标签
JLabel label = new JLabel("IP:");
label.setPreferredSize(new Dimension(20, 50));
add(label);
//设置文本输入框
ipText = new JTextField();
ipText.setPreferredSize(new Dimension(160, 30));
strText = new JTextField();
strText.setPreferredSize(new Dimension(200, 30));
add(ipText);
add(strText);
//设置点击按钮
JButton button = new JButton();
button.setText("发送");
add(button);
try {
//设置启动端口与要连接的服务器地址、端口
socket.bind(new InetSocketAddress(8081));
socket.connect(new InetSocketAddress(ipText.getText(),6666));
//创建打印流和字符缓冲输入流用于对信息的读取与写入
writer = new PrintWriter(socket.getOutputStream());
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//创建线程来读取服务器推送的信息并添加到文本域,设置死循环让它一直更新信息
new Thread(new Runnable() {
@Override
public void run() {
try {
while (true){
jTextArea.append(reader.readLine()+ "\n");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
} catch (IOException e) {
e.printStackTrace();
}
setVisible(true);
//设置监听事件,点击按钮时触发
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (tilte.contains("Client")) {
client();
}
//清空输入框信息
ipText.setText("");
strText.setText("");
}
});
//监听关闭聊天窗口
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
/**
* 将从输入框获取到的信息发送到服务器
*/
public static void client() {
try {
String mag = name+":"+strText.getText();
while ("exit".equals(mag)){
writer.close();
reader.close();
}
writer.println(mag);
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 用于启动服务器
*/
public static void startServer() {
try {
ServerSocket serverSocket = new ServerSocket(PORT);
//使其能够获取无限次的连接
while (true) {
//用于阻塞直到有连接进入
Socket accept = serverSocket.accept();
//将socket连接存入集合中,用于后面的消息推送
sockets.add(accept);
//创建线程进行独立的消息分发给sockets中存放的各个客户端
new Thread(new Runnable() {
@Override
public void run() {
try {
boolean flag = true;
//通过转换流将字节进行字符转换,同时获取客户ip
BufferedReader reader = new BufferedReader(new InputStreamReader(accept.getInputStream()));
String ip = accept.getInetAddress().toString();
while (flag) {
String s = reader.readLine();
if ("exit".equals(s)) {
flag = false;
continue;
}
String msg = ip + ":" + s;
send(msg);
}
sockets.remove(accept);
accept.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 消息分发
* @param msg 客户端IP+信息
* @throws IOException
*/
private void send(String msg) throws IOException {
PrintWriter writer = null;
for (Socket socket : sockets) {
writer = new PrintWriter(socket.getOutputStream());
writer.println(msg);
writer.flush();
}
}
}).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}