不说废话,先看效果,目前实现的基本功能有一对一私聊。一对多群聊。
1、先启动服务端,等待客户端连接…。
2、启动三个客户端,代表不同的用户。右上角分别选择不同的用户进行登录。
3、登录的用户可对所有用户发送消息,也可以对某个用户发送消息。
下面是具体的代码实现(采用传统的BIO实现,也可参考我另一篇博客NIO实现其中的功能)。
server端代码:
package Server;
import java.io.*;
import java.net.*;
import java.util.*;
/*
server端
*/
public class ChatServer {
private ServerSocket ss;
private boolean flag = false;
private Map<String, Client> clients = new HashMap<>();
public static void main(String args[]) {
new ChatServer().start();
}
public void start() {
try {
ss = new ServerSocket(8888);
flag = true;
System.out.println("Server启动成功...");
} catch (BindException e) {
System.out.println("端口使用中!!!");
} catch (IOException e) {
e.printStackTrace();
}
try {
//接收客户端的连接
while (flag) {
//accept方法是一个阻塞的方法,会阻塞当前的线程。
Socket socket = ss.accept();
Client c = new Client(socket);
System.out.println("一个客户端已经连接...");
new Thread(c).start();
}
} catch (IOException e) {
System.out.println("Client closed...");
} finally {
try {
if (ss != null)
ss.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private class Client implements Runnable {
private Socket s;
private String userName = "";
private DataInputStream input = null;
private DataOutputStream output = null;
private boolean connected = false;
private BufferedOutputStream fout = null;
private String saveFilePath = "";
public Client(Socket s) {
this.s = s;
try {
input = new DataInputStream(s.getInputStream());
output = new DataOutputStream(s.getOutputStream());
connected = true;
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
byte buffer[] = new byte[1024];
int len = 0;
try {
while (connected) {
String msg[] = input.readUTF().split("#");//msg={" ","登录用户","目标用户","要发送的信息"}
switch (msg[0]) {
case "LOGIN":
String userName = msg[1];
if (clients.containsKey(userName)) {
output.writeUTF("FAIL");//已经登录
System.out.println("拒绝了一个重复连接...");
closeConnect();
} else {
output.writeUTF("SUCCESS");
clients.put(userName, this);
//将所有登录用户信息发送给新登录的用户
StringBuffer allUsers = new StringBuffer();
allUsers.append("ALLUSERS#");
for (String user : clients.keySet())
allUsers.append(user + "#");
output.writeUTF(allUsers.toString());
//将新登录的用户信息发送给其他用户
String newLogin = "LOGIN#" + userName;
sendMsg(userName, newLogin);
this.userName = userName;
}
break;
case "LOGOUT":
clients.remove(this.userName);
String logoutMsg = "LOGOUT#" + this.userName;
sendMsg(this.userName, logoutMsg);
System.out.println("用户" + this.userName + "已下线...");
closeConnect();
break;
case "SENDONE":
Client c = clients.get(msg[1]);//获取目标用户的连接
String msgToOne="";
if (c != null) {
msgToOne="SENDONE#" + this.userName + "#" + msg[2];
c.output.writeUTF(msgToOne);
c.output.flush();
}
break;
case "SENDALL":
String msgToAll = "";
msgToAll = "SENDALL#" + this.userName + "#" + msg[1];
sendMsg(this.userName, msgToAll);
break;
}
}
} catch (IOException e) {
System.out.println("Client closed...");
connected = false;
} finally {
try {
if (input != null)
input.close();
if (output != null)
output.close();
if (fout != null)
fout.close();
if (s != null)
s.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void closeConnect() {
connected = false;
try {
if (input != null)
input.close();
if (output != null)
output.close();
if (s != null)
s.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public void sendMsg(String fromUser, String msg) {
String tempUser = "";
try {
for (String toUser : clients.keySet()) {
if (!toUser.equals(fromUser)) {
tempUser = toUser;
DataOutputStream out = clients.get(toUser).output;
out.writeUTF(msg);
out.flush();
}
}
} catch (IOException e) {
System.out.println("用户" + tempUser + "已经离线!!!");
}
}
}
}
客户client端:
package Client;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.Socket;
import java.net.SocketException;
/*
客户client端
*/
public class ChatClient extends JFrame {
private JTextArea sendArea, contentArea;
JPanel p1, p11, p12, p2, p21, p22;
JComboBox<String> user1, user2;
private RecvThread clientThread = null;
private String filePath = null;
public void start() {
Container con = getContentPane();
con.setLayout(new BorderLayout());
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
int screenWidth = screenSize.width / 2;
int screenHeight = screenSize.height / 2;
int height = getHeight();
int width = getWidth();
setSize(350, 400);
setLocation((screenWidth - width) / 2, (screenHeight - height) / 2);
sendArea = new JTextArea(3, 10);//设置发送区域几行几列
sendArea.setBorder(BorderFactory.createLineBorder(Color.BLUE, 1));
sendArea.setLineWrap(true);
sendArea.setWrapStyleWord(true);//激活断行不断字功能
sendArea.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if ('\n' == e.getKeyCode())
if (clientThread != null) {
clientThread.sendMsg();
}
}
});
contentArea = new JTextArea(6, 10);
contentArea.setBorder(BorderFactory.createLineBorder(Color.BLUE, 1));
//北边接收消息区域
p1 = new JPanel();
p1.setLayout(new BorderLayout());
p11 = new JPanel();
p11.setLayout(new GridLayout(1, 2));
JLabel l1 = new JLabel("选择你的身份:");
user1 = new JComboBox<>();
user1.addItem("-----选择-----");
user1.addItem("张三");
user1.addItem("李四");
user1.addItem("王五");
user1.addItem("小六");
user1.addItemListener(e -> {
if (e.getStateChange() == ItemEvent.SELECTED) {
// System.out.println("选中的项:" + user1.getSelectedItem());
if (user1.getSelectedIndex() == 0) {
return;
}
clientThread = new RecvThread((String) user1.getSelectedItem());
new Thread(clientThread).start();
}
});
p11.add(l1);
p11.add(user1);
p12 = new JPanel();
p12.setLayout(new GridLayout(1, 1));
p12.add(new JScrollPane(contentArea));
p1.add(p11, BorderLayout.NORTH);
p1.add(p12, BorderLayout.SOUTH);
//南边发送消息区域
p2 = new JPanel();
p2.setLayout(new BorderLayout());
p21 = new JPanel();
p21.setLayout(new GridLayout(2, 2));
user2 = new JComboBox<>();
user2.addItem("所有用户");
JLabel l2 = new JLabel("选择要发送的用户:");
p21.add(l2);
p21.add(user2);
p22 = new JPanel();
p22.setLayout(new GridLayout(1, 1));
p22.add(new JScrollPane(sendArea));
p2.add(p21, BorderLayout.NORTH);
p2.add(p22, BorderLayout.SOUTH);
con.add(p1, BorderLayout.NORTH);
con.add(p2, BorderLayout.SOUTH);
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
if (clientThread != null)
clientThread.exit();
System.exit(0);
}
});
}
private class RecvThread implements Runnable {
private Socket s = null;
private DataInputStream in = null;
private DataOutputStream out = null;
private String userName;
private boolean isLogin = false;
StringBuilder msg = new StringBuilder();
public RecvThread(String userName) {
this.userName = userName;
}
//用户登录
public void login() {
try {
s = new Socket("127.0.0.1", 8888);//监听本机的8888端口
in = new DataInputStream(s.getInputStream());
out = new DataOutputStream(s.getOutputStream());
String sendMsg = "LOGIN#" + userName;
out.writeUTF(sendMsg);
out.flush();
//服务器返回的信息
String recv = in.readUTF();
if (recv.equals("FAIL")) {
showMsg(userName + "已经登录!!!");
user1.setEnabled(true);
exit();
return;
} else if (recv.equals("SUCCESS")) {
showMsg("登录成功!!!");
user1.setEnabled(false);
isLogin = true;
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void exit() {//用户退出,释放资源
try {
if (isLogin) {
out.writeUTF("LOGOUT");
out.flush();
isLogin = false;
}
if (in != null)
in.close();
if (out != null)
out.close();
if (s != null)
s.close();
} catch (IOException e) {
System.out.println("连接已关闭!!!");
}
}
//用户发送消息
public void sendMsg() {
int len = 0;
// String fileName = "";
if (!isLogin) {
showMsg("没有登录,请登录!!!");
return;
}
msg.setLength(0);
String sendInfo = sendArea.getText().trim();
String user = (String) user2.getSelectedItem();
if(sendInfo.equals(""))
sendInfo=" ";
try {
if (user.equals("所有用户")) {//给所有用户发送消息
msg.append("SENDALL#");
msg.append(sendInfo);
} else {//只给某个用户发送消息
msg.append("SENDONE#");
msg.append(user+"#" + sendInfo);//如果这里sendinfo=SENDFILE那么可能就会发生错误。有待改进....
}
out.writeUTF(msg.toString());
showMsg("我说:" + sendInfo);
sendArea.setText("");
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
login();
try {
while (isLogin) {
String msgs[] = in.readUTF().split("#");
switch (msgs[0]) {
case "LOGIN":
user2.addItem(msgs[1]);//后面可以加个提示信息
break;
case "ALLUSERS":
for (int i = 1; i < msgs.length; i++) {
if (!"".equals(msgs[i]))
user2.addItem(msgs[i]);
}
break;
case "SENDONE":
showMsg(msgs[1] + ":" + msgs[2]);
break;
case "SENDALL":
showMsg(msgs[1] + "对所有人说:" + msgs[2]);
break;
case "LOGOUT":
showMsg("用户" + msgs[1] + "已下线!!!");
user2.removeItem(msgs[1]);
break;
}
}
} catch (SocketException e) {
System.out.println(userName + "已退出...");
} catch (IOException e) {
isLogin = false;//返回数据出现异常,退出登录
e.printStackTrace();
}
}
}
//设置显示信息
public void showMsg(String msg) {
contentArea.append(msg + "\n");
contentArea.setCaretPosition(contentArea.getText().length());// 自动滚动到文本区的最后一行
}
public static void main(String args[]) {
//将swing风格控件渲染成Windows风格
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
e.printStackTrace();
}
new ChatClient().start();
}
}
上面代码我都放在gitte上面,需要的自己去拿聊天室源代码