基于TCP/IP的Java聊天室软件

GitHub:https://github.com/CodingLink/tcp-chat

流程图:

流程图

代码实现:

客户端:

  1. 在ClientFrame中创建Socket的对象,将IP和端口号分别存入ipIn和portIn两个String类型变量中;
  2. 连接服务器;
  3. 使用BufferedWriter和BufferedReader读取Socket中的输入流和输出流;
  4. 关闭连接。
  • ClientFrame方法用于客户端的界面显示。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    public ClientFrame() {
    this.setDefaultLookAndFeelDecorated(true);

    //关闭窗口即关闭进程
    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    this.setTitle("客户端");
    connect = new JButton("连接");
    send = new JButton("发送");
    showPort = new JTextField(12);//端口填写框
    showIP = new JTextField(12);//ip地址填写框


    Box v1 = Box.createVerticalBox();
    v1.add(new Label("ip:"));
    v1.add(new Label("Port:"));


    Box v2 = Box.createVerticalBox();
    v2.add(showIP);
    v2.add(showPort);


    Box base = Box.createHorizontalBox();
    base.add(v1);
    base.add(v2);


    Container con = this.getContentPane();
    con.setLayout(new FlowLayout());
    showChat = new JTextArea(16, 25);
    sendChat = new JTextArea(4, 18);
    con.add(base);
    con.add(new JScrollPane(showChat));
    con.add(new JScrollPane(sendChat));
    con.add(connect);
    con.add(send);
    //设置监听事件
    connect.addActionListener(this);
    connect.setActionCommand("connect");
    send.addActionListener(this);
    send.setActionCommand("send");
    thread = new Thread();

    //设置窗体大小
    this.setBounds(700, 200, 300, 500);
    portIn = JOptionPane.showInputDialog(null, "请输入端口号:", "8080");
    ipIn = JOptionPane.showInputDialog(null, "请输入ip地址", "127.0.0.1");
    showPort.setText(portIn);
    showIP.setText(ipIn);
    this.setVisible(true);
    this.setResizable(false);
    this.setVisible(true);

    send.setEnabled(false);
    }
  • ActionListener方法用于监听按钮的点击事件,并通过setActionCommandgetActionCommand方法设定事件点击的的键值,若事件的返回值为connect则为连接服务器,若为send则为发送消息。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    @Override
    public void actionPerformed(ActionEvent e) {
    if (e.getActionCommand().equals("connect")) {
    try {
    sendChat.setEnabled(true);
    send.setEnabled(true);
    //将套接字实例化
    socket = new Socket(ipIn, Integer.parseInt(portIn));

    //提示用户服务器连接成功
    showChat.append("服务器连接成功,可以开始聊天\n");

    //建立连接
    input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    output = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));

    thread = new Thread(this);
    thread.setPriority(Thread.MIN_PRIORITY);
    thread.start();
    } catch (IOException ioException) {
    showChat.setText("对不起,连接服务器失败!");
    sendChat.setEditable(false);
    send.setEnabled(false);
    } catch (NumberFormatException c) {
    sendChat.setText("端口号请输入数字!");
    }
    } else if (e.getActionCommand().equals("send")) {
    OutStr = sendChat.getText();
    if (OutStr.length() > 0) {
    try {
    output.write(OutStr+"\n");
    output.flush();
    showChat.append("我说: " + OutStr + "\n");
    sendChat.setText(null);
    } catch (IOException es) {
    showChat.append("信息发送失败!\n");
    }
    } else {
    JOptionPane.showMessageDialog(this, "信息不能为空!", "警告提示!", JOptionPane.WARNING_MESSAGE);
    }
    }
    }

     

  • Run方法则为让线程持续运行,即可持续接收服务器端的消息。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    @Override
    public void run() {
    try {
    while (true) {
    showChat.append("对方说:" + input.readLine() + "\n");
    Thread.sleep(1000);
    }
    } catch (IOException el) {
    showChat.setText("与服务器已断开连接");
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }

     

服务器端:

  1. 建立监听服务,使用ActionListener接口和Runnable接口,并创建ServerSocket对象;
  2. 等待客户端的连接
  3. 接受客户端的请求,连接成功后,并通过Socket对象的OutputStream输出消息,InputStream接收消息。
  4. 关闭连接。

其中,通过SeverFrame方法来提供服务器端的软件界面显示,使用ActionPerformed方法监听发送消息的事件,通过Run方法使用线程在持续监听客户端发送的消息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
package tech.codinglink;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerFrame extends JFrame implements ActionListener,Runnable {
private JButton send;//发送按钮
private JTextField showIP,showPort;//显示ip和端口号
private JTextArea showChat,sendChat;//显示聊天窗口和发送窗口
private String portIn;
private String OutStr=null;
private ServerSocket server;
private Socket socket;//创建一个套接字
private Thread thread;
private BufferedReader input;
private BufferedWriter output;


public ServerFrame(){
this.setDefaultLookAndFeelDecorated(true);

//创建及设置窗口
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

this.setTitle("服务器端");
send=new JButton("发送");
showPort=new JTextField(12);


Box v1=Box.createVerticalBox();
v1.add(new Label("Port:"));


Box v2=Box.createVerticalBox();
v2.add(showPort);


Box base=Box.createHorizontalBox();
base.add(v1);
base.add(v2);


Container con=this.getContentPane();
con.setLayout(new FlowLayout());
showChat=new JTextArea(16,25);
sendChat=new JTextArea(4,18);
con.add(base);
con.add(new JScrollPane(showChat));
con.add(new JScrollPane(sendChat));
con.add(send);
send.addActionListener(this);
send.setActionCommand("send");



this.setBounds(700,200,300,500);//设置窗体大校
portIn=JOptionPane.showInputDialog(null,"请输入端口号:","8080");
showPort.setText(portIn);
this.setVisible(true);
this.setResizable(false);
this.setVisible(true);
send.setEnabled(false);
try{
server = new ServerSocket(Integer.parseInt(portIn));
showChat.setText("服务器正在监听...");
socket = server.accept();
showChat.setText("已有客户端连接上,现在可以开始聊天!\n");
sendChat.setEditable(true);
send.setEnabled(true);
//使用BufferedReader和BufferWriter包装输入输出流
input= new BufferedReader(new InputStreamReader(socket.getInputStream()));
output = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
thread=new Thread(this);
thread.setPriority(Thread.MIN_PRIORITY);
thread.start();
}catch(IOException c){
showChat.append("对不起,不能创建服务器!");
sendChat.setEditable(false);
send.setEnabled(false);
}catch(NumberFormatException c){
sendChat.setText("端口号请输入数字!");
}
}

@Override
public void actionPerformed(ActionEvent e) {
OutStr = sendChat.getText();
if (OutStr.length() > 0) {
try {
output.write(OutStr + "\n");
output.flush();
showChat.append("我说: " + OutStr + "\n");
sendChat.setText(null);
} catch (IOException es) {
showChat.append("消息发送失败!\n");
}
} else {
JOptionPane.showMessageDialog(this, "信息不能为空!", "警告提示!", JOptionPane.WARNING_MESSAGE);
}
}

@Override
public void run() {
try{
while(true){
showChat.append("对方说:"+input.readLine()+"\n");
Thread.sleep(1000);
}
}catch(IOException el){
showChat.setText("客户离开");
}catch (InterruptedException e) { }
}
}