public class Server {
public static void main(String[] args) {
new Thread(new ServerTask()).start(); //执行服务任务
}
}
/**
*创建服务任务类
*/
class ServerTask implements Runnable {
private ServerSocket serverSocket; //服务端口
private List<Socket> clients; //客户端列表,用于保存所有客户端的socket
public ServerTask() {
try {
serverSocket = new ServerSocket(6666); //初始化服务端口
} catch (IOException e) {
e.printStackTrace();
}
if (serverSocket.isBound()) {
System.out.println("服务器端口开启成功......");
}
}
@Override
public void run() {
clients = new ArrayList<>(); //初始化客户端列表
try {
while (true) {//这里使用循环,一直处理用户请求
Socket socket = serverSocket.accept(); //开始接收连接对象,没有用户请求挂起,阻塞。
clients.add(socket); //有用户请求后,将该客户端的socket加入客户端列表
new Thread(new HandlingClientCheat(socket)).start(); //开启线程处理聊天任务
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 定义处理客户端信息的内部类
*/
class HandlingClientCheat implements Runnable {
private Socket socket; //该客户端的socket
public HandlingClientCheat(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); //获得网络输入流
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = new Date(); //创建一个日期对象,可以显示信息时间
String str;
while ((str = reader.readLine()) != null) {
date.setTime(System.currentTimeMillis());//获取当前时间
str = simpleDateFormat.format(date) + "\n" + str;//格式化当前时间
System.out.println(str); //服务端输出聊天信息
//给其他客户端转发此客户端的信息
for (Socket client : clients)
if (client != socket) {
PrintWriter writer = new PrintWriter(new OutputStreamWriter(client.getOutputStream()));
writer.println(str);
writer.flush();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
客户端程序
public class Client {
private ClientSendThread clientSendThread; //客户端发送信息线程
private ClientFrame clientFrame; //所属的客户端界面
private String username; //用户名
public Client(ClientFrame clientFrame,String username){
this.clientFrame = clientFrame;
this.username = username;
try {
Socket socket = new Socket("localhost", 6666); //根据主机及端口号开启连接,并获得Socket对象
if (socket.isConnected())
clientFrame.showText.append("服务器连接成功.....\n\n");
clientSendThread = new ClientSendThread(socket);
clientSendTask.start();//创建并开启发送信息线程
new ClientReceiveThread(socket).start();//创建并开启接收信息线程
} catch (IOException e) {
clientFrame.showText.append("服务器连接失败,请检查服务器是否开启.....");
e.printStackTrace();
}
}
public ClientSendThread getClientSendThread() {
return clientSendTask;
}
/**
*创建一个处理发送信息的内部类
*/
class ClientSendThread extends Thread {
private Socket socket; //此客户端的socket对象
private String message; //此客户端发送的信息
public ClientSendThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
PrintWriter writer = new PrintWriter(socket.getOutputStream()); //获取网络输出流
while (true) {
synchronized (this){
wait(); //这里先让此线程等待,知道客户端界面有内容输入就唤醒
}
writer.println(username+":"+message);//将此用户发送的信息输出给服务端
clientFrame.showText.append("\t\t\t\t\t\t\t\t\t我的:\n\t\t\t\t\t\t\t\t\t\t"+message+"\n\n"); //在对应的客户端界面显示信息
message = null;
writer.flush(); //刷新输入流
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void setMessage(String message) {
this.message = message;
}
}
/**
*创建客户端接收信息的内部类
*/
class ClientReceiveThread extends Thread {
private Socket socket; //此客户端的socket对象
public ClientReceiveThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); //获取网络输入流
String str;
while ((str = reader.readLine()) != null) {
clientFrame.showText.append(str+"\n\n"); //在客户端界面显示
clientFrame.showText.setCaretPosition(clientFrame.showText.getText().length());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
搭建客户端界面
我这里搭建客户端界面使用的是JavaSwing
class ClientFrame extends JFrame {
ClientFrame frame = this;
JPanel inputBlock;
JTextArea inputText;
JTextArea showText;
JScrollPane jScrollPane;
JButton send;
Client client;
public static void main(String[] args) {
new ClientFrame(); //开启客户端界面
}
public ClientFrame() {
init();
String username = JOptionPane.showInputDialog(this, "请输入一个用户名用于聊天", "提示", JOptionPane.INFORMATION_MESSAGE);
setTitle("聊天室"+" 用户:"+username);
client = new Client(this,username); //创建客户端处理程序
}
void init() {
//窗口设置
setBounds(100, 100, 800, 600);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setDefaultLookAndFeelDecorated(true);
setLayout(new GridLayout(2, 1));
Container contentPane = getContentPane();
//输入块
inputBlock = new JPanel(null);
//输入框
inputText = new JTextArea(20, 20);
inputText.setMargin(new Insets(10, 10, 10, 10));
inputText.setFont(new Font("谐体", Font.BOLD, 16));
inputText.setBounds(10, 10, 761, 230);
//发送按钮
send = new JButton("发送");
send.setBounds(660, 243, 100, 30);
inputBlock.add(inputText);
inputBlock.add(send);
//显示框
jScrollPane = new JScrollPane(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
showText = new JTextArea("聊天记录\n\n");
showText.setFont(new Font("宋体", Font.BOLD, 13));
showText.setBackground(new Color(43, 43, 44));
Border border = BorderFactory. createLineBorder(new Color(60, 63, 65));
showText.setBorder(BorderFactory.createCompoundBorder(border, BorderFactory.createEmptyBorder(10, 10, 10, 10)));
showText.setEnabled(false);
jScrollPane.setViewportView(showText);
//插入组件
contentPane.add(jScrollPane);
contentPane.add(inputBlock);
setVisible(true);
sendEvent();
}
//组件的事件绑定
public void sendEvent() {
//输入域的键盘事件
inputText.addKeyListener(new KeyListener() {
@Override
public void keyTyped(KeyEvent e) {
}
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyChar() == KeyEvent.VK_ENTER) {
sendMessage();
}
}
@Override
public void keyReleased(KeyEvent e) {
}
});
//发送按钮的点击事件
send.addActionListener(l -> {
sendMessage();
});
}
//发送信息的方法
public void sendMessage() {
String str = inputText.getText().trim();//获取到输入域的文本
if (!str.equals("")) {
client.getClientSendThread().setMessage(str);//设置客户端发送线程需要发送的信息。
synchronized (client.getClientSendThread()) {
client.getClientSendThread().notify(); //唤醒客户端发送线程
}
inputText.setText("");
showText.setCaretPosition(showText.getText().length());
}
}
}
用户张三
:
用户李四
:
用户王五
:
服务端
:
总结:如此一个简单的基于TCP协议的网络聊天室就搭建好了。