效果图:
李四先进入聊天室,张三也在,然后王五加入;
参考地址:Java多线程机交互
额,原代码在客户端显示谁谁谁发送消息有个bug。。。
算了,直接上代码吧!
服务器端:
package testTCP;
import java.net.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.io.*;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
public class TCPServer extends JFrame {
public static JTextArea centerTextArea = new JTextArea();
private JPanel southPanel, bottompanel;
public List<Client> list = new ArrayList<Client>();
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
public void setui() throws Exception {
ServerSocket ss = new ServerSocket(5678);// 创建一个Socket服务器,监听5566端口
// TODO Auto-generated constructor stub
setTitle("服务器");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(400, 400);
setResizable(false); // 窗口大小不可调整
setLocationRelativeTo(null);// 窗口剧中
// 窗口的center部分
centerTextArea.setEditable(false);
centerTextArea.setBackground(new Color(211, 211, 211));
// 窗口的SHOTU部分
southPanel = new JPanel(new BorderLayout());
bottompanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 5));
southPanel.add(bottompanel, BorderLayout.SOUTH);
add(new JScrollPane(centerTextArea), BorderLayout.CENTER);
add(southPanel, BorderLayout.SOUTH);
setVisible(true);
while (true) {
Socket s = ss.accept();// 利用Socket服务器的accept()方法获取客户端Socket对象。
addclient(s);
System.out.println(list.size());
}
}
// 添加客户端
private void addclient(Socket s) {
String name1 = "";
try {
BufferedReader in = new BufferedReader(new InputStreamReader(
s.getInputStream()));
name1 = in.readLine();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Client c = new Client(name1, s);// 创建客户端处理线程对象
list.add(c);
//Thread t = new Thread(c);// 创建客户端处理线程
//t.start();// 启动线程
cachedThreadPool.execute(c);
}
// 客户端处理线程类(实现Runnable接口)
class Client implements Runnable {
String name;// 客户端名字
Socket s = null;// 保存客户端Socket对象
BufferedReader in;
PrintWriter out;
Client(String name, Socket s) {
this.s = s;
this.name = name;
try {
in = new BufferedReader(new InputStreamReader(
s.getInputStream()));
out = new PrintWriter(s.getOutputStream());
centerTextArea.append("客户端" + name + "连接成功\n");
centerTextArea.setCaretPosition(centerTextArea.getText().length());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void run() {
try {
while (true) {
String str = in.readLine();
for (int j = 0; j < list.size(); j++) {
Client c = list.get(j);
if (c != this) {
System.out.println(str);
c.send(str+"-|1|2|-"+name);
}
}
centerTextArea.append(name + "发出消息:" + str + "\n");
centerTextArea.setCaretPosition(centerTextArea.getText().length());
if (str.equals("end"))
break;
}
try {
s.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
public void send(String str) {
//out.println("客户端 " + name + "说:" + str);
out.println(str);
out.flush();
}
}
public static void main(String[] args) throws Exception {
// 利用死循环不停的监听端口
TCPServer tc = new TCPServer();
tc.setui();
}
}
客户端:
package testTCP;
import java.net.*;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
public class TCPClient extends JFrame {
private JLabel stateLB;
private JTextArea centerTextArea, inputTextArea;
private JPanel southPanel, bottompanel;
private JTextField ipTextField, remotePortTF;
private JButton sendBT, clearBT;
static Socket server;
String name = null;
PrintWriter out;
BufferedReader in;
Thread receive = new Thread(new receiveThread());
public TCPClient() throws Exception {
// 此处向服务器发送请求
server = new Socket(InetAddress.getLocalHost(), 5678);
in = new BufferedReader(new InputStreamReader(server.getInputStream()));
out = new PrintWriter(server.getOutputStream());
// 建立接收
receive.start();
}
public void setUpUI() {// 初始化面板
setTitle("客户端");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(400, 400);
setResizable(false); // 窗口大小不可调整
setLocationRelativeTo(null);// 窗口剧中
stateLB = new JLabel("未链接");
stateLB.setHorizontalAlignment(JLabel.RIGHT);
// 窗口的center部分
centerTextArea = new JTextArea();
centerTextArea.setEditable(false);
centerTextArea.setBackground(new Color(211, 211, 211));
// 窗口的SHOTU部分
southPanel = new JPanel(new BorderLayout());
inputTextArea = new JTextArea(5, 20);
bottompanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 5));
ipTextField = new JTextField("127.0.0.1", 8);
sendBT = new JButton("发送");
clearBT = new JButton("清屏");
bottompanel.add(ipTextField);
bottompanel.add(sendBT);
bottompanel.add(clearBT);
southPanel.add(new JScrollPane(inputTextArea), BorderLayout.CENTER);
southPanel.add(bottompanel, BorderLayout.SOUTH);
add(stateLB, BorderLayout.NORTH);
add(new JScrollPane(centerTextArea), BorderLayout.CENTER);
add(southPanel, BorderLayout.SOUTH);
setVisible(true);
name = JOptionPane.showInputDialog(this, "请输入客户端的名字", JOptionPane.QUESTION_MESSAGE);
setTitle(name);
out.println(name);
out.flush();
stateLB.setText(name + "已链接");
}
public void setListener() {// 发送消息
sendBT.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
String sendcontent = inputTextArea.getText();
centerTextArea.append("你说:"+inputTextArea.getText() + "\n");
centerTextArea.setCaretPosition(centerTextArea.getText().length());
inputTextArea.setText("");
try {
out.println(sendcontent);
out.flush();
if (sendcontent.equals("end")) {
server.close();
}
} catch (Exception e2) {
// TODO: handle exception
JOptionPane.showMessageDialog(TCPClient.this, "出错了发送不成功");
e2.printStackTrace();
}
clearBT.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
centerTextArea.setText("");// 清空面板聊天记录
}
});
}
});
}
class receiveThread implements Runnable {
// 接收线程
@Override
public void run() {
// TODO Auto-generated method stub
try {
while (server != null)
{
String info = in.readLine();
System.out.println(info);
String message = info.split("-\\|1\\|2\\|-")[0];
String name = info.split("-\\|1\\|2\\|-")[1];
centerTextArea.append(name + "说:" + message + "\n");
centerTextArea.setCaretPosition(centerTextArea.getText().length());
}
} catch (Exception e) {
// TODO: handle exception
}
}
}
public static void main(String[] args) throws Exception {
TCPClient c = new TCPClient();
c.setUpUI();
c.setListener();
}
}
服务器指定客户端发送消息:
这里我为了在单机上多开客户端,把代码中根据IP来分客户端换成了根据时间线来分客户端,这些都是小问题自行修改~
服务器端代码:
package MyTCP;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Iterator;
import java.util.Map;
import java.util.Scanner;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import tcp.Client;
public class MyTCPServer implements Runnable {
Map<String, Client> clientMap = new ConcurrentHashMap<String, Client>();
// 可缓存线程池 - 建客户端处理线程池
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
// 可缓存线程池 - 建客户端处理线程池
ExecutorService keepAliveThreadPool = Executors.newCachedThreadPool();
/**
*
* @param checkDelay 检查时间:单位毫秒
* @param keepAliveDelay 心跳时间:单位秒
*/
public MyTCPServer(long checkDelay, long keepAliveDelay) {
keepAliveThreadPool.execute(new KeepAliveWatchDog(checkDelay, keepAliveDelay*1000));
}
public MyTCPServer() {
keepAliveThreadPool.execute((new KeepAliveWatchDog(10, 15*1000)));
}
public MyTCPServer(boolean keepAlive) {
if (keepAlive) {
//keepAliveThreadPool.execute((new Thread(new KeepAliveWatchDog(10, 10 * 1000))));
keepAliveThreadPool.execute((new KeepAliveWatchDog(10, 10 * 1000)));
}
}
public static void main(String[] args) {
MyTCPServer server = new MyTCPServer(true);
Thread t = new Thread(server);// 创建客户端处理线程
t.start();// 启动线程
@SuppressWarnings("resource")
Scanner scan = new Scanner(System.in);
while (true) {
System.out.println("输入操作:");
String op = scan.nextLine();
if ("1".equals(op)) {
for (String key : server.clientMap.keySet()) {
System.out.println("key = " + key);
}
for (Client value : server.clientMap.values()) {
System.out.println("Client = " + value.toString());
}
} else {
System.out.println("输入地址");
String ClientIp = scan.nextLine();
System.out.println("输入地址是:" + ClientIp);
System.out.println("输入消息");
String message = scan.nextLine();
System.out.println("输入消息:" + message);
Client c = server.clientMap.get(ClientIp);
if(c!=null){
c.send(ClientIp, message);
}else{
System.out.println("该链接不存在!");
}
}
}
}
@SuppressWarnings("resource")
@Override
public void run() {
ServerSocket ss = null;
try {
ss = new ServerSocket(5678);// 创建一个Socket服务器,监听5566端口
System.out.println("服务已启动。。。");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
while (true) {
Socket client = null;
try {
client = ss.accept();
addclient(client);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
// 添加客户端
private void addclient(Socket s) {
// 第一次收到客户端信息
/*
* String message; try { BufferedReader in = new BufferedReader(new
* InputStreamReader( s.getInputStream())); message = in.readLine(); }
* catch (IOException e) { e.printStackTrace(); }
*/
Client c = new Client(s);// 创建客户端处理线程对象
// clientMap.put(s.getInetAddress().toString().replace("/", ""), c);
clientMap.put(System.currentTimeMillis() + "", c);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// Thread t = new Thread(c);// 创建客户端处理线程
// t.start();// 启动线程
cachedThreadPool.execute(c);
// 遍历
/*
* for (String key : clientMap.keySet()) { System.out.println("key = " +
* key); } for (Client value : clientMap.values()) { System.out.println(
* "Client = " + value.toString()); }
*/
}
class Client implements Runnable {
Socket s = null;// 保存客户端Socket对象
BufferedReader in;
PrintWriter out;
String toClient;
String ClientIp;
boolean send = false;
Client(Socket s) {
this.s = s;
try {
in = new BufferedReader(new InputStreamReader(s.getInputStream()));
out = new PrintWriter(s.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
}
}
void send(String ClientIp, String message) {
this.ClientIp = ClientIp;
this.toClient = message;
send = true;
if (send) {
Client c = clientMap.get(ClientIp);
if (c == null) {
System.out.println(ClientIp + "连接不存在!");
}
try {
c.send(toClient);
} catch (Exception e) {
e.printStackTrace();
c.close();
}
}
send = false;
}
@Override
public void run() {
while (true) {
try {
String str = null;
// str = in.readLine();
if ((str = in.readLine()) != null) {
System.out.println(s.getInetAddress().toString().replace("/", "") + ":" + str);
}
} catch (Exception e) {
// e.printStackTrace();
close();
break;
}
/*
* if (send) { Client c = clientMap.get(ClientIp);
* c.send(toClient); } send = false;
*/
}
}
public void send(String str) {
out.println(str);
if (!"KeepAlive".equals(str))
System.out.println("send:" + str);
out.flush();
}
public void close() {
try {
s.close();
in.close();
;
out.close();
} catch (IOException e) {
e.printStackTrace();
}
clientMap.remove(s.getInetAddress().toString().replace("/", ""));
System.out.println(s.getInetAddress().toString().replace("/", "") + "连接已关闭!");
}
}
class KeepAliveWatchDog implements Runnable {
long lastSendTime;
long checkDelay = 10;
long keepAliveDelay = 10000;
/**
*
* @param checkDelay 检查时间
* @param keepAliveDelay 心跳时间
*/
public KeepAliveWatchDog(long checkDelay, long keepAliveDelay) {
this.checkDelay = checkDelay;
this.keepAliveDelay = keepAliveDelay;
}
@Override
public void run() {
while (true) {
if (System.currentTimeMillis() - lastSendTime > keepAliveDelay) {
for (String ClientIp : clientMap.keySet()) {
Client c = clientMap.get(ClientIp);
c.send("KeepAlive");
}
lastSendTime = System.currentTimeMillis();
} else {
try {
Thread.sleep(checkDelay);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
客户端,略微修改:
package MyTCP;
import java.net.*;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
public class MyTCPClient extends JFrame {
private JLabel stateLB;
private JTextArea centerTextArea, inputTextArea;
private JPanel southPanel, bottompanel;
private JTextField ipTextField, remotePortTF;
private JButton sendBT, clearBT;
static Socket server;
String name = null;
PrintWriter out;
BufferedReader in;
Thread receive = new Thread(new receiveThread());
public MyTCPClient() throws Exception {
// 此处向服务器发送请求
server = new Socket(InetAddress.getLocalHost(), 5678);
in = new BufferedReader(new InputStreamReader(server.getInputStream()));
out = new PrintWriter(server.getOutputStream());
// 建立接收
receive.start();
}
public void setUpUI() {// 初始化面板
setTitle("客户端");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(400, 400);
setResizable(false); // 窗口大小不可调整
setLocationRelativeTo(null);// 窗口剧中
stateLB = new JLabel("未链接");
stateLB.setHorizontalAlignment(JLabel.RIGHT);
// 窗口的center部分
centerTextArea = new JTextArea();
centerTextArea.setEditable(false);
centerTextArea.setBackground(new Color(211, 211, 211));
// 窗口的SHOTU部分
southPanel = new JPanel(new BorderLayout());
inputTextArea = new JTextArea(5, 20);
bottompanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 5));
ipTextField = new JTextField("127.0.0.1", 8);
sendBT = new JButton("发送");
clearBT = new JButton("清屏");
bottompanel.add(ipTextField);
bottompanel.add(sendBT);
bottompanel.add(clearBT);
southPanel.add(new JScrollPane(inputTextArea), BorderLayout.CENTER);
southPanel.add(bottompanel, BorderLayout.SOUTH);
add(stateLB, BorderLayout.NORTH);
add(new JScrollPane(centerTextArea), BorderLayout.CENTER);
add(southPanel, BorderLayout.SOUTH);
setVisible(true);
stateLB.setText(name + "已链接");
}
public void setListener() {// 发送消息
sendBT.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
String sendcontent = inputTextArea.getText();
centerTextArea.append("你说:" + inputTextArea.getText() + "\n");
centerTextArea.setCaretPosition(centerTextArea.getText().length());
inputTextArea.setText("");
try {
out.println(sendcontent);
out.flush();
if (sendcontent.equals("end")) {
server.close();
}
} catch (Exception e2) {
// TODO: handle exception
JOptionPane.showMessageDialog(MyTCPClient.this, "出错了发送不成功");
e2.printStackTrace();
}
clearBT.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
centerTextArea.setText("");// 清空面板聊天记录
}
});
}
});
}
class receiveThread implements Runnable {
// 接收线程
@Override
public void run() {
// TODO Auto-generated method stub
try {
while (server != null)
{
String info = in.readLine();
centerTextArea.append(info + "\n");
centerTextArea.setCaretPosition(centerTextArea.getText().length());
}
} catch (Exception e) {
// TODO: handle exception
}
}
}
public static void main(String[] args) throws Exception {
MyTCPClient c = new MyTCPClient();
c.setUpUI();
c.setListener();
}
}
运行效果:
keepAlive是心跳包。。
输入操作字符1是查看所有客户端;
其他就是制定客户端发送消息;
当然,这里关闭客户端的时候代码没改。。。是根据IP来关闭客户端的,所以关闭后仍可以在服务器上看到;
把根据时间线的代码换成原来的就好;
这时把客户端关闭:
好,基本这样~