基本功能(必须实现和遵循的要求)
1.要求项目服务器端能稳定持续接收进入聊天室的人员,并能够在聊天室实时更新在线人数。
2.客户端可以持续不断的给服务器发送消息,中间不能丢失任何一条信息。
3.客户端的聊天记录框也可以实时更新大家发送的最新消息。
4.客户端可以随时离开聊天室,当某个客户端离开聊天室,所有客户端都能看到该人员的离开,
服务器同时应该减少一个在线人数。
扩展功能:
1.完成基本功能的前提下,可以考虑一下给服务器上传文件的实现。(最好能看到上传进度)
2.考虑进入聊天室的成员之间私聊。
3.支持消息的语音播放。
时间问题我只实现了第二个功能。
package org.sdibt.stc;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
public class ChatUI {
private static String serverAddress = "127.0.0.1"; //服务器地址,127.0.0.1表示本地。
private JFrame frame;
private JTextField textField;
private static Socket socket;
private static OutputStream out;
private static JTextArea textArea ;
private static JTextField textField_1;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
ChatUI window = new ChatUI();
window.frame.setVisible(true);
new Thread(){
@Override
public void run() {
while(true){
try {
InputStream in = socket.getInputStream();
byte[] b = new byte[1024];
int len = in.read(b);
String word = jiexi(new String(b, 0, len));
textArea.append(word+"\n");
textArea.setCaretPosition(textArea.getText().length());
} catch (Exception e) {
// TODO: handle exception
}
}
}
}.start();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
*
* @param word
* @return
*/
public static String jiexi(String word) {
int len = 0;
if((len = word.indexOf("#num")) != -1) {
String renshu = word.substring(len+4, word.length());
textField_1.setText("\u5728\u7EBF\u4EBA\u6570\uFF1A" + renshu);
return word.substring(0, len);
}
return word;
}
/**
* Create the application.
*/
public ChatUI() {
initialize();
}
/**
* Initialize the contents of the frame.
*/
private void initialize() {
try {
//建立与服务器链接
// 知道服务器的地址,端口
socket = new Socket(serverAddress, 8000);
//向服务器打个招呼
out = socket.getOutputStream();
} catch (Exception e) {
// TODO: handle exception
}
frame = new JFrame();
frame.setBounds(100, 100, 728, 744);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(null);
textField = new JTextField();
textField.setBounds(47, 492, 627, 69);
frame.getContentPane().add(textField);
textField.addKeyListener(new KeyListener() {
public void keyPressed(KeyEvent e){
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
SendText();
}
}
@Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
@Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
}
});
JButton btnNewButton = new JButton("send");
btnNewButton.addActionListener(e ->{
// 点击了发送按钮,客户端发送内容到服务器
SendText();
}
);
btnNewButton.setBounds(561, 574, 113, 27);
frame.getContentPane().add(btnNewButton);
textArea = new JTextArea();
textArea.setEditable(false);
textArea.setBounds(47, 74, 627, 374);
textArea.setLineWrap(true); //激活自动换行功能
textArea.setWrapStyleWord(true); // 激活断行不断字功能
JScrollPane scrollPane = new JScrollPane(textArea);//添加滚动条
scrollPane.setBounds(47, 74, 627, 374);
frame.getContentPane().add(scrollPane);
textField_1 = new JTextField();
textField_1.setText("\u5728\u7EBF\u4EBA\u6570\uFF1A0\u4EBA");
textField_1.setBounds(47, 35, 113, 24);
textField_1.setEditable(false);
frame.getContentPane().add(textField_1);
textField_1.setColumns(10);
JButton button = new JButton("\u9000\u51FA");
button.setBounds(420, 574, 113, 27);
frame.getContentPane().add(button);
button.addActionListener(e -> {
SendText("#exit");
System.exit(0);
});
frame.setTitle("聊天室");
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
SendText("#exit");
}
});
}
/*
* 发送消息
*/
void SendText()
{
SendText(textField.getText());
}
void SendText(String content) {
try {
out.write(content.getBytes());
out.flush();
} catch (Exception e) {
// TODO: handle exception
}
}
}
服务器端代码负责接收发和发送客户端之间的数据,当有多个客户端相互发消息时,其客户端都是先发送给服务器,然后服务器在负责把信息发送给其他所有客户端
package org.sdibt.stc;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
/**
* 聊天室服务端
* 2019年12月8日
*/
public class Server {
public static List<Socket> clients = new ArrayList<>();//存储所有的客户端通道
public static void main(String[] args) throws Exception {
//开启一个服务器端对象
ServerSocket server = new ServerSocket(8000);
//开启服务器
while(true){
Socket client=server.accept(); //接受客户端,等待客户端到来
System.out.println("在线人数:" + clients.size());
//获取客户端说的话,派发一个线程
ChatServerThread c = new ChatServerThread(client);
clients.add(client);
c.start();
}
}
}
/**
* 定义一个线程类,用来应付某个客户端的通信
* */
class ChatServerThread extends Thread{
public Socket socket;
private String ip;
public ChatServerThread(Socket socket) {
ip = socket.getInetAddress().getHostAddress();
this.socket = socket;
}
@Override
public void run() {
try {
//从client里面提取信息
InputStream in = socket.getInputStream();
byte[] b = new byte[1024];
sendtextAll(ip + "进入聊天室————#num" + Server.clients.size());
while(true){
int len = in.read(b);
int zhuangtai = jiexi(new String(b,0,len));
if(zhuangtai == 0) {
break;
}
}
} catch (Exception e) {
// TODO: handle exception
}
try {
socket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public int jiexi(String text) throws IOException {
if(text.equals("#exit"))
{
while(!Server.clients.remove(socket));
System.out.println("在线人数:" + Server.clients.size());
sendtextAll(this.ip + "离开聊天室————#num" + Server.clients.size());
return 0;
}
else if(text.charAt(0)=='@') {
text = text.substring(1);
int len = text.indexOf("@");
if(len != -1) {
sendtext_one(text.substring(len + 1),text.substring(0, len));
}
}
else if(text.charAt(0)!='#')
sendtextAll(ip +"说:" + text);
return 1;
}
/**
* 私聊,指定IP
* @param text
* @param ip
* @throws IOException
*/
public void sendtext_one(String text,String ip) throws IOException {
if(text.length()>0) {
text = "来自 " + ip + " 的私聊:" + text;
for(int i = 0;i<Server.clients.size();i++) {
if(Server.clients.get(i).getInetAddress().getHostAddress().equals(ip)) {
try {
Server.clients.get(i).getOutputStream().write(text.getBytes());
Server.clients.get(i).getOutputStream().flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
socket.getOutputStream().write("".getBytes());
return;
}
}
//没有找到IP,返回消息,发送失败
socket.getOutputStream().write("发送失败。。未找到IP".getBytes());
}
}
/**
* 群发!!
* @param text
*/
public void sendtextAll(String text) {
System.out.println(text);
//服务器需要将word这句话推送给每一个客户端
if(text.length()>0)
for (int i =0;i< Server.clients.size();i++) {
try {
Server.clients.get(i).getOutputStream().write(text.getBytes());
Server.clients.get(i).getOutputStream().flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}