网页版在线聊天java Socket实现

时间:2021-10-30 09:52:57

本文为大家分享了一个满足在线网页交流需求的实例,由于java Socket实现的网页版在线聊天功能,供大家参考,具体内容如下

实现步骤:
1、使用awt组件和socket实现简单的单客户端向服务端持续发送消息;
2、结合线程,实现多客户端连接服务端发送消息;
3、实现服务端转发客户端消息至所有客户端,同时在客户端显示;
4、把awt组件生成的窗口界面改成前端jsp或者html展示的界面,java socket实现的客户端改为前端技术实现。

这里首先实现第一步的简单功能,难点在于:
1、没有用过awt组件,没有用过java相关的监听事件;
2、长时间没有使用socket进行客户端和服务端的交互,并且没有真正进行过cs结构的开发。

实现功能的代码
在线聊天客户端:
1、生成图形窗口界面轮廓
2、为轮廓添加关闭事件
3、在轮廓中加入输入区域和内容展示区域
4、为输入区域添加回车事件
5、建立服务端连接并发送数据

?
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
package chat.chat;
 
import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
 
/**
 * 在线聊天客户端 1、生成图形窗口界面轮廓 2、为轮廓添加关闭事件 3、在轮廓中加入输入区域和内容展示区域 4、为输入区域添加回车事件
 * 5、建立服务端连接并发送数据
 *
 * @author tuzongxun123
 *
 */
public class ChatClient extends Frame {
  // 用户输入区域
  private TextField tfTxt = new TextField();
  // 内容展示区域
  private TextArea tarea = new TextArea();
  private Socket socket = null;
  // 数据输出流
  private DataOutputStream dataOutputStream = null;
 
  public static void main(String[] args) {
    new ChatClient().launcFrame();
  }
 
  /**
   * 建立一个简单的图形化窗口
   *
   * @author:tuzongxun
   * @Title: launcFrame
   * @param
   * @return void
   * @date May 18, 2016 9:57:00 AM
   * @throws
   */
  public void launcFrame() {
    setLocation(300, 200);
    this.setSize(200, 400);
    add(tfTxt, BorderLayout.SOUTH);
    add(tarea, BorderLayout.NORTH);
    pack();
    // 监听图形界面窗口的关闭事件
    this.addWindowListener(new WindowAdapter() {
 
      @Override
      public void windowClosing(WindowEvent e) {
        System.exit(0);
        disConnect();
      }
    });
    tfTxt.addActionListener(new TFLister());
    setVisible(true);
    connect();
  }
 
  /**
   * 连接服务器
   *
   * @author:tuzongxun
   * @Title: connect
   * @param
   * @return void
   * @date May 18, 2016 9:56:49 AM
   * @throws
   */
  public void connect() {
    try {
      // 新建服务端连接
      socket = new Socket("127.0.0.1", 8888);
      // 获取客户端输出流
      dataOutputStream = new DataOutputStream(socket.getOutputStream());
      System.out.println("连上服务端");
    } catch (UnknownHostException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
 
  /**
   * 关闭客户端资源
   *
   * @author:tuzongxun
   * @Title: disConnect
   * @param
   * @return void
   * @date May 18, 2016 9:57:46 AM
   * @throws
   */
  public void disConnect() {
    try {
      dataOutputStream.close();
      socket.close();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
 
  /**
   * 向服务端发送消息
   *
   * @author:tuzongxun
   * @Title: sendMessage
   * @param @param text
   * @return void
   * @date May 18, 2016 9:57:56 AM
   * @throws
   */
  private void sendMessage(String text) {
    try {
      dataOutputStream.writeUTF(text);
      dataOutputStream.flush();
    } catch (IOException e1) {
      e1.printStackTrace();
    }
  }
 
  /**
   * 图形窗口输入区域监听回车事件
   *
   * @author tuzongxun123
   *
   */
  private class TFLister implements ActionListener {
 
    @Override
    public void actionPerformed(ActionEvent e) {
      String text = tfTxt.getText().trim();
      tarea.setText(text);
      tfTxt.setText("");
      // 回车后发送数据到服务器
      sendMessage(text);
    }
  }
}

服务端:

?
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
package chat.chat;
 
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;
 
/**
 * java使用socket和awt组件简单实现在线聊天功能服务端 可以实现一个客户端连接后不断向服务端发送消息
 * 但不支持多个客户端同时连接,原因在于代码中获得客户端连接后会一直循环监听客户端输入,造成阻塞
 * 以至于服务端无法二次监听另外的客户端,如要实现,需要使用异步或者多线程
 *
 * @author tuzongxun123
 *
 */
public class ChatServer {
 
  public static void main(String[] args) {
    // 是否成功启动服务端
    boolean isStart = false;
    // 服务端socket
    ServerSocket ss = null;
    // 客户端socket
    Socket socket = null;
    // 服务端读取客户端数据输入流
    DataInputStream dataInputStream = null;
    try {
      // 启动服务器
      ss = new ServerSocket(8888);
    } catch (BindException e) {
      System.out.println("端口已在使用中");
      // 关闭程序
      System.exit(0);
    } catch (Exception e) {
      e.printStackTrace();
    }
 
    try {
      isStart = true;
      while (isStart) {
        boolean isConnect = false;
        // 启动监听
        socket = ss.accept();
        System.out.println("one client connect");
        isConnect = true;
        while (isConnect) {
          // 获取客户端输入流
          dataInputStream = new DataInputStream(
              socket.getInputStream());
          // 读取客户端传递的数据
          String message = dataInputStream.readUTF();
          System.out.println("客户端说:" + message);
        }
 
      }
    } catch (EOFException e) {
      System.out.println("client closed!");
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      // 关闭相关资源
      try {
        dataInputStream.close();
        socket.close();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
  }
}

继续,在单客户端连接的基础上,这里第二步需要实现多客户端的连接,也就需要使用到线程。每当有一个新的客户端连接上来,服务端便需要新启动一个线程进行处理,从而解决之前的循环读取中造成阻塞的问题。

写线程通常有两种方法,集成Thread或者实现runnable接口,原则上是能实现runnable的情况下就不继承,因为实现接口的方式更加灵活。

客户端代码相较之前没有变化,变得是服务端,因此这里便只贴出服务端代码:

java使用socket和awt组件以及多线程简单实现在线聊天功能服务端 :

实现多个客户端连接后不断向服务端发送消息, 相对于第一个版本,重点在于使用了多线程。服务端还未实现转发功能,客户端图形窗口中只能看到自己输入的信息,不能看到其他客户端发送的消息。

?
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
package chat.chat;
 
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
 
/**
 * *
 * @author tuzongxun123
 *
 */
public class ChatServer {
 
  public static void main(String[] args) {
    new ChatServer().start();
  }
 
  // 是否成功启动服务端
  private boolean isStart = false;
  // 服务端socket
  private ServerSocket ss = null;
  // 客户端socket
  private Socket socket = null;
 
  public void start() {
    try {
      // 启动服务器
      ss = new ServerSocket(8888);
    } catch (BindException e) {
      System.out.println("端口已在使用中");
      // 关闭程序
      System.exit(0);
    } catch (Exception e) {
      e.printStackTrace();
    }
 
    try {
      isStart = true;
      while (isStart) {
        // 启动监听
        socket = ss.accept();
        System.out.println("one client connect");
        // 启动客户端线程
        Client client = new Client(socket);
        new Thread(client).start();
      }
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      // 关闭服务
      try {
        ss.close();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
 
  }
 
  /**
   * 客户端线程
   *
   * @author tuzongxun123
   *
   */
  class Client implements Runnable {
    // 客户端socket
    private Socket socket = null;
    // 客户端输入流
    private DataInputStream dataInputStream = null;
    private boolean isConnect = false;
 
    public Client(Socket socket) {
      this.socket = socket;
      try {
        isConnect = true;
        // 获取客户端输入流
        dataInputStream = new DataInputStream(socket.getInputStream());
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
 
    @Override
    public void run() {
      isConnect = true;
      try {
        while (isConnect) {
          // 读取客户端传递的数据
          String message = dataInputStream.readUTF();
          System.out.println("客户端说:" + message);
        }
      } catch (EOFException e) {
        System.out.println("client closed!");
      } catch (SocketException e) {
        System.out.println("Client is Closed!!!!");
      } catch (Exception e) {
        e.printStackTrace();
      } finally {
        // 关闭相关资源
        try {
          dataInputStream.close();
          socket.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }
 
  }
 
}

上面主要介绍了利用线程使服务端实现了能够接收多客户端请求的功能,这里便需要客户端接收多客户端消息的同时还能把消息转发到每个连接的客户端,并且客户端要能在内容显示区域显示出来,从而实现简单的在线群聊。

在实现客户端转发,无非就是增加输出流;而之前客户端都只发不收,这里也需要更改客户端达到循环接收服务端消息的目的,因此也需要实现多线程。

在实现这个功能的时候,偶然想起随机生成验证码的功能,于是也灵机一动随机给每个客户端生成一个名字,从而在输出的时候看起来更加像是群聊,不仅有消息输出,还能看到是谁。

实现这些功能之后,基本上就可以几个人同时在线群聊了,因为代码中有main方法,因此可以把服务端和客户端都打成可执行jar包,可参考我的另一篇博文:使用eclipse创建java程序可执行jar包

之后在桌面双击相应的jar文件启动服务端和客户端即可,不需要再依赖eclipse运行。

修改后的客户端代码如下:

?
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
package chat.chat;
 
import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Random;
 
/**
 * 在线聊天客户端 步骤:
 
 *1、生成图形窗口界面轮廓
 
 *2、为轮廓添加关闭事件
 
 *3、在轮廓中加入输入区域和内容展示区域
 
 *4、为输入区域添加回车事件
 * 5、建立服务端连接并发送数据
 *
 * @author tuzongxun123
 *
 */
public class ChatClient extends Frame {
  /**
   *
   */
  private static final long serialVersionUID = 1L;
  // 用户输入区域
  private TextField tfTxt = new TextField();
  // 内容展示区域
  private TextArea tarea = new TextArea();
  private Socket socket = null;
  // 数据输出流
  private DataOutputStream dataOutputStream = null;
  // 数据输入流
  private DataInputStream dataInputStream = null;
  private boolean isConnect = false;
  Thread tReceive = new Thread(new ReceiveThread());
  String name = "";
 
  public static void main(String[] args) {
    ChatClient chatClient = new ChatClient();
    chatClient.createName();
    chatClient.launcFrame();
 
  }
 
  /**
   * 建立一个简单的图形化窗口
   *
   * @author:tuzongxun
   * @Title: launcFrame
   * @param
   * @return void
   * @date May 18, 2016 9:57:00 AM
   * @throws
   */
  public void launcFrame() {
    setLocation(300, 200);
    this.setSize(200, 400);
    add(tfTxt, BorderLayout.SOUTH);
    add(tarea, BorderLayout.NORTH);
    // 根据窗口里面的布局及组件的preferedSize来确定frame的最佳大小
    pack();
    // 监听图形界面窗口的关闭事件
    this.addWindowListener(new WindowAdapter() {
 
      @Override
      public void windowClosing(WindowEvent e) {
        System.exit(0);
        disConnect();
      }
    });
    tfTxt.addActionListener(new TFLister());
    // 设置窗口可见
    setVisible(true);
    connect();
    // 启动接受消息的线程
    tReceive.start();
  }
 
  /**
   * 连接服务器
   *
   * @author:tuzongxun
   * @Title: connect
   * @param
   * @return void
   * @date May 18, 2016 9:56:49 AM
   * @throws
   */
  public void connect() {
    try {
      // 新建服务端连接
      socket = new Socket("127.0.0.1", 8888);
      // 获取客户端输出流
      dataOutputStream = new DataOutputStream(socket.getOutputStream());
      dataInputStream = new DataInputStream(socket.getInputStream());
      System.out.println("连上服务端");
      isConnect = true;
    } catch (UnknownHostException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
 
  // 生成随机的客户端名字
  public void createName() {
    String[] str1 = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
        "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v",
        "w", "x", "y", "z", "1", "2", "3", "4", "5", "6", "7", "8",
        "9", "0", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J",
        "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V",
        "W", "X", "Y", "Z" };
    Random ran = new Random();
 
    for (int i = 0; i < 6; i++) {
      // long num = Math.round(Math.random() * (str1.length - 0) + 0);
      // int n = (int) num;
      int n = ran.nextInt(str1.length);
      if (n < str1.length) {
        String str = str1[n];
        name = name + str;
        System.out.println(name);
      } else {
        i--;
        continue;
      }
 
    }
    this.setTitle(name);
  }
 
  /**
   * 关闭客户端资源
   *
   * @author:tuzongxun
   * @Title: disConnect
   * @param
   * @return void
   * @date May 18, 2016 9:57:46 AM
   * @throws
   */
  public void disConnect() {
    try {
      isConnect = false;
      // 停止线程
      tReceive.join();
    } catch (InterruptedException e) {
      e.printStackTrace();
    } finally {
      try {
        if (dataOutputStream != null) {
          dataOutputStream.close();
        }
        if (socket != null) {
          socket.close();
          socket = null;
        }
 
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
  }
 
  /**
   * 向服务端发送消息
   *
   * @author:tuzongxun
   * @Title: sendMessage
   * @param @param text
   * @return void
   * @date May 18, 2016 9:57:56 AM
   * @throws
   */
  private void sendMessage(String text) {
    try {
      dataOutputStream.writeUTF(name + ":" + text);
      dataOutputStream.flush();
    } catch (IOException e1) {
      e1.printStackTrace();
    }
  }
 
  /**
   * 图形窗口输入区域监听回车事件
   *
   * @author tuzongxun123
   *
   */
  private class TFLister implements ActionListener {
 
    @Override
    public void actionPerformed(ActionEvent e) {
      String text = tfTxt.getText().trim();
      // 清空输入区域信息
      tfTxt.setText("");
      // 回车后发送数据到服务器
      sendMessage(text);
    }
 
  }
 
  private class ReceiveThread implements Runnable {
 
    @Override
    public void run() {
      try {
        while (isConnect) {
          String message = dataInputStream.readUTF();
          System.out.println(message);
          String txt = tarea.getText();
          if (txt != null && !"".equals(txt.trim())) {
            message = tarea.getText() + "\n" + message;
          }
          tarea.setText(message);
        }
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
 
  }
}

修改后的服务端代码如下:

?
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
package chat.chat;
 
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.List;
 
/**
 * java使用socket和awt组件以及多线程简单实现在线聊天功能服务端 :
 * 实现服务端把接收到的客户端信息转发到所有连接的客户端,并且让客户端读取到这些信息并显示在内容显示区域中。
 *
 * @author tuzongxun123
 *
 */
public class ChatServer {
 
  public static void main(String[] args) {
    new ChatServer().start();
  }
 
  // 是否成功启动服务端
  private boolean isStart = false;
  // 服务端socket
  private ServerSocket ss = null;
  // 客户端socket
  private Socket socket = null;
  // 保存客户端集合
  List<Client> clients = new ArrayList<Client>();
 
  public void start() {
    try {
      // 启动服务器
      ss = new ServerSocket(8888);
    } catch (BindException e) {
      System.out.println("端口已在使用中");
      // 关闭程序
      System.exit(0);
    } catch (Exception e) {
      e.printStackTrace();
    }
 
    try {
      isStart = true;
      while (isStart) {
        // 启动监听
        socket = ss.accept();
        System.out.println("one client connect");
        // 启动客户端线程
        Client client = new Client(socket);
 
        new Thread(client).start();
        clients.add(client);
      }
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      // 关闭服务
      try {
        ss.close();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
 
  }
 
  /**
   * 客户端线程
   *
   * @author tuzongxun123
   *
   */
  private class Client implements Runnable {
    // 客户端socket
    private Socket socket = null;
    // 客户端输入流
    private DataInputStream dataInputStream = null;
    // 客户端输出流
    private DataOutputStream dataOutputStream = null;
    private boolean isConnect = false;
 
    public Client(Socket socket) {
      this.socket = socket;
      try {
        isConnect = true;
        // 获取客户端输入流
        dataInputStream = new DataInputStream(socket.getInputStream());
        // 获取客户端输出流
        dataOutputStream = new DataOutputStream(
            socket.getOutputStream());
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
 
    /**
     * 向客户端群发(转发)数据
     *
     * @author:tuzongxun
     * @Title: sendMessageToClients
     * @param @param message
     * @return void
     * @date May 18, 2016 11:28:10 AM
     * @throws
     */
    public void sendMessageToClients(String message) {
      try {
        dataOutputStream.writeUTF(message);
      } catch (SocketException e) {
 
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
 
    @Override
    public void run() {
      isConnect = true;
      Client c = null;
      try {
        while (isConnect) {
          // 读取客户端传递的数据
          String message = dataInputStream.readUTF();
          System.out.println("客户端说:" + message);
          for (int i = 0; i < clients.size(); i++) {
            c = clients.get(i);
 
            c.sendMessageToClients(message);
          }
 
        }
      } catch (EOFException e) {
        System.out.println("client closed!");
      } catch (SocketException e) {
        if (c != null) {
          clients.remove(c);
        }
        System.out.println("Client is Closed!!!!");
      } catch (Exception e) {
        e.printStackTrace();
      } finally {
        // 关闭相关资源
        try {
          if (dataInputStream != null) {
            dataInputStream.close();
          }
          if (socket != null) {
            socket.close();
            socket = null;
          }
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }
  }
 
}

就先为大家介绍到这里,之后如果有新的内容再为大家进行更新。

关于网页在线聊天功能的实现大,大家还可以参考一下几篇文章进行学习:

java实现一个简单TCPSocket聊天室功能分享

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家可以继续关注服务器之家的更多精彩内容。