源码已经上传到资源
学习了网络编程后,我们都可以实现通过TCP协议进行服务端和客户端之间的简单通信
但是这远远不过瘾啊!我想要一个那种带界面的,可以实现多人聊天的聊天室。我就是这样的,终于按耐不住诱惑花了两天时间根据TCP通信做了一个这样的聊天室小项目
如果也有想我一样的盆友,实现时没了思路,可以参考一下我的哦(˵¯͒〰¯͒˵)
一.项目要求
我搭建的聊天室是这样的
- 填写用户名登录
- 登录完成后进入聊天界面聊天
首先当我们要聊天时,要填写用户名进行登录
然后登录完成后就会跳转到聊天界面进行聊天,其中可以进行多人聊天
二.项目逻辑
⑴服务端
- 对于每一个来连接的客户端都需要一条线程进行处理
- 服务端要将信息反馈给每一个客户端
首先我们来分析服务端的逻辑
服务端的作用就是用来接受客户端发送的信息并进行反馈
服务端只有一个,我需要不断的等待客户端来连接,对于每一个客户端我们都要单独开出一条线程进行处理。当ServerSocket进行反馈时,我们需要反馈给每一个客户端,因此我们需要一个集合用来存储已经连接的Socket
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
public class MyServerSocket {
//记录连接的客户端
static ArrayList<Socket> sockets = new ArrayList<>();
public static void main(String[] args) throws IOException {
//服务端
ServerSocket ss = new ServerSocket(10000);
//来一个客户端就开辟一个线程处理
while (true) {
Socket socket = ss.accept();
sockets.add(socket);
new Thread(new ServerRunable(socket, sockets)).start();
}
}
}
class ServerRunable implements Runnable {
Socket socket;//连接处理的Socket
ArrayList<Socket> sockets;
public ServerRunable(Socket socket, ArrayList<Socket> sockets) {
this.socket = socket;
this.sockets = sockets;
}
@Override
public void run() {
//接收客户端发送的消息,并反馈
while (true) {
try {
BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
String str=br.readLine();
System.out.println(str);
for (Socket socket1 : sockets) {//反馈给每一个客户端
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(socket1.getOutputStream()));
bw.write(str);
bw.newLine();
bw.flush();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
⑵客户端
- 客户端的发送数据要与与聊天界面的信息输入框和发送按钮绑定
- 客户端需要单独开出一条线程用来接收服务端反馈的信息并打印到聊天界面的文本框中
客户端的作用就是发送数据并接收服务端反馈的信息打印到聊天界面的文本框中
下面我们来分析客户端的逻辑
首先我们要接收用户登录时的用户名,与线程名字绑定
然后客户端的发送数据需要与聊天界面的信息输入框和发送按钮绑定,当我们点击发送按钮时我们要发送的数据就会被发送到服务端
其次客户端需要单独开出一条线程用来接收服务端反馈的信息并打印到聊天界面的文本框中
import java.io.*;
import java.net.Socket;
public class MySocket {
String name;//用户名
Socket socket;//客户端对象
public MySocket(String name){
//客户端
this.name=name;
Socket socket=null;
try {
socket = new Socket("127.0.0.1",10000);
this.socket=socket;
//单独开出一条线程用来打印
Thread t=new Thread(new SocketRunable(socket));
t.setName(name);
t.start();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public void talk(Socket socket,String str) {//与聊天界面的信息输入框和发送按钮绑定
try {
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bw.write(str);
bw.newLine();
bw.flush();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
class SocketRunable implements Runnable{
Socket socket;
public SocketRunable(Socket socket) {
this.socket=socket;
}
@Override
public void run() {
String name=Thread.currentThread().getName();
while (true){
try {
BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
String str=br.readLine();
//将反馈的信息打印到文本框中
SocketJFrame.jTextArea.append(str+"\r\n");
System.out.println(name+":"+str);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
⑶登录界面
- 登录成功后启动的聊天界面将共享一个文本显示框
我们需要将聊天界面的文本显示框在这里初始化,为什么?因为服务端发送的数据是共享的,因此我们要在登录成功后将共享的文本显示框一同传输给客户端的聊天界面
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class SocketJFrame extends JFrame implements ActionListener {
public Container container=null;
JTextField jTextField=new JTextField();//文本输入框
JButton login=new JButton("登录");
static JTextArea jTextArea=new JTextArea();//聊天界面文本显示框
public SocketJFrame(){
//设置图标
this.setIconImage(Toolkit.getDefaultToolkit().getImage("TalkWirhMyFriend\\src\\image\\坤坤.jpg"));
//初始化界面
initJFrame();
//初始化组件
initView();
this.setVisible(true);
}
public void initJButton(){
login.setBounds(100,90,80,30);
login.addActionListener(this::loginAction);//添加监听事件
container.add(login);
}
public void initView(){
//设置文字
Font codeFont=new Font(null,1,14);
JLabel code=new JLabel("用户名");
code.setBounds(90,20,100,40);
code.setFont(codeFont);
code.setBackground(Color.BLACK);
container.add(code);
//设置输入框
jTextField.setBounds(90,50,150,30);
container.add(jTextField);
//按钮
initJButton();
}
public void initJFrame() {
this.setSize(300, 200);//设置大小
this.setTitle("聊天小程序");//设置标题
this.setDefaultCloseOperation(3);//设置关闭模式
this.setLocationRelativeTo(null);//设置位置居中
this.setAlwaysOnTop(true);//设置置顶
container=this.getContentPane();//获取界面隐藏容器
container.setLayout(null);//取消内部默认的坐标
container.setBackground(Color.WHITE);//设置背景颜色
}
public JScrollPane initJTextArea(){
//文本显示器
jTextArea.setEnabled(false);//设置为不可编辑
jTextArea.setLineWrap(true);//自动换行
jTextArea.setWrapStyleWord(true);//单词边界换行
jTextArea.setFont(new Font(null,1,16));
jTextArea.setBackground(Color.gray);
//添加滚动器
JScrollPane jsp=new JScrollPane(jTextArea);
jsp.setBounds(30,30,500,300);
jsp.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
return jsp;
}
@Override
public void actionPerformed(ActionEvent e) {
}
public void loginAction(ActionEvent e){//登录
System.out.println("登录");
this.setVisible(false);
//启动聊天界面
MainJFrame mj=new MainJFrame(jTextField.getText(),jTextArea);
JScrollPane jsp=initJTextArea();
mj.container.add(jsp);
}
}
class Main{
public static void main(String[] args) {
new SocketJFrame();
}
}
⑷聊天界面
- 登录成功后就创建客户端对象
- 发送信息按钮与客户端的发送信息绑定
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class MainJFrame extends JFrame implements ActionListener {
public Container container=null;
JTextField news=new JTextField();//信息输入框
JButton sendsJButton=new JButton("发送");
String name;//用户名
JTextArea jTextArea;//文本显示框
MySocket s;//当登录成功后创建客户端对象
public MainJFrame(String name,JTextArea jTextArea) {
this.name=name;
this.jTextArea=jTextArea;
s=new MySocket(name);
initJFrame();
initView();
this.setVisible(true);
}
public void initView(){
//信息输入框
news.setBounds(40,400,200,40);
container.add(news);
//发送按钮
sendsJButton.setBounds(380,400,70,50);
sendsJButton.addActionListener(this);
container.add(sendsJButton);
}
public void initJFrame() {
this.setIconImage(Toolkit.getDefaultToolkit().getImage("TalkWirhMyFriend\\src\\image\\坤坤.jpg"));
this.setSize(600, 500);
this.setTitle("聊天小程序");
this.setDefaultCloseOperation(3);
this.setLocationRelativeTo(null);
this.setAlwaysOnTop(true);
container=this.getContentPane();
container.setLayout(null);
container.setBackground(Color.BLACK);
// initBackground();//添加背景图片
}
public void initBackground(){//添加背景图片
ImageIcon background=new ImageIcon("JFrame_Game\\Jframe_knowledge\\src\\image\\坤坤.jpg");
JLabel jLabel=new JLabel(background);
jLabel.setBounds(-80,0,800,800);
container.add(jLabel);
}
@Override
public void actionPerformed(ActionEvent e) {
//点击发送,将输入框中的内容发送给服务器,然后服务器反馈
String str=name+":"+news.getText();
s.talk(s.socket,str);
news.setText("");
System.out.println(str);
}
}
最后检验成果的时刻到了,我们先运行服务端MyServerSocket,再运行登录界面SocketJFrame
我们来运行两个用户毛蛋和辣条
各位实现了吗?实现了的话赶快去运行爽一下吧