利用TCP实现多客户聊天系统
读书笔记:《Java语言程序设计》–郭克华
案例介绍
上一篇博客已经介绍了客户端和服务器的相互通信。但是实际应用中,应该是客户端和客户端聊天,而不是客户端和服务器聊天。客户端和客户端聊天的本质是信息由服务器端转发。因此本篇博客将开发一个支持多个客户端的程序。服务器端界面如图1所示。
图一
当客户端出现时,需要输入昵称,如图2所示。
图二
单击“确定”按钮,连接到服务器。如果连接成功服务器回送一个“连接成功”的信息,如图3所示。
图三
单击“确定”按钮,即可进行聊天。
为了体现多客户端效果,打开了3个客户端,如图4所示。
图四
在界面下方可以输入消息,按Enter键,消息发出。消息发出后,文本框自动清空。消息发送后,能够让各个客户端收到聊天消息,聊天消息打印在界面上的多行文本框内,在打印聊天信息的同时,还能够打印这条聊天消息的发出人。
编写服务器程序
要让服务器端能够接受多个客户端的连接,需要注意以下几个问题:
1.由于事先不知道客户端什么时候连接,因此,服务端必须首先有一个线程负责接受多个客户端的连接,结构如下:
public class Server extends JFrame implements Runnable {
public Sever() {
//服务器端打开端口
//服务器端开启线程,接受客户端连接
}
public void run() {
//不断接受客户端连接
while(true) {
//接受客户端连接
//开一个聊天线程给这个客户端
//将该聊天线程对象添加进集合
//聊天线程启动
}
}
}
2.当客户端连接上之后,服务器端要等待这些客户端传送消息过来,而实现并不知道客户端什么时候会发信息过来。所以,每一个一个客户端连上之后,必须为这个客户端单独看一个线程,读取它发过来的信息。因此,需要再编写一个线程类。
3.服务器收到谋改革客户端信息后,需要将其转发给各个客户端,这就需要再服务端保存各个客户端的输入输出流的引用。实际上,这些引用可以保存在客户端服务的线程中。
因此,整个服务器端程序的基本结构如下:
public class Server extends JFrame implements Runnable {
public Server() {
//服务器端打开端口
//服务器端开启线程,接受客户端连接
}
public void run() {
//不断接受客户端连接
while(true) {
//接收客户端连接
//开一个聊天线程给这个客户端
//将该聊天线程对象添加进集合
//聊天线程启动
}
}
/*聊天线程类,每连接上一个客户端,就为它开一个聊天线程*/
class ChatThread extends Thread {
//负责读取相应SocketConnection的信息
public void run() {
while(true) {
//读取客户端发来的信息
//将该信息发送给所有其他客户端
}
}
}
}
服务器端详细代码如下:
package practice;
import java.awt.*;
import java.io.*;
import java.net.*;
import javax.swing.JFrame;
import java.util.ArrayList;
public class Server extends JFrame implements Runnable {
private Socket s=null;
private ServerSocket ss=null;
private ArrayList clients=new ArrayList(); //保存客户端的线程
public Server() throws Exception {
this.setTitle("服务器端");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setBackground(Color.yellow);
this.setSize(200,100);
this.setVisible(true);
ss=new ServerSocket(9999); //服务器端开辟端口,接受连接
new Thread(this).start(); //接受客户连接的死循环开始运行
}
@Override
public void run(){
try {
while(true) {
s=ss.accept();
//s就是当前的连接对应的Socket,对应一个客户端
//该客户端随时可能发信息过来,必须要接受
//另外开辟一个线程,专门为这个s服务,负责接受信息
ChatThread ct=new ChatThread(s);
clients.add(ct);
ct.start();
}
}
catch (Exception ex) {
}
}
class ChatThread extends Thread { //为某个Socket负责接受信息
private Socket s=null;
private BufferedReader br=null;
public PrintStream ps=null;
public ChatThread(Socket s) throws Exception {
this.s=s;
br=new BufferedReader(new InputStreamReader(s.getInputStream()));
ps=new PrintStream(s.getOutputStream());
}
@Override
public void run() {
try {
while(true) {
String str=br.readLine();//读取该Socket传来的信息
sendMessage(str); //将str转发给所有客户端
}
}
catch (Exception ex) {
}
}
}
public void sendMessage(String msg) { //将信息发给所有客户端
for(int i=0;i<clients.size();i++) {
ChatThread ct=(ChatThread)clients.get(i);
//向ct内的Socket内写msg
ct.ps.println(msg);
}
}
public static void main(String[] args) throws Exception {
new Server();
}
}
编写客户端程序
客户端编程相对简单,只需要编写发送信息、连接服务器、接受服务器端传送的信息即可。代码如下:
package practice;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.Socket;
import javax.swing.*;
public class Client extends JFrame implements ActionListener,Runnable {
private JTextArea taMsg=new JTextArea("以下是聊天记录\n");
private JTextField tfMsg=new JTextField();
private Socket s=null;
private String nickName=null;
public Client() {
this.setTitle("客户端");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.add(taMsg,BorderLayout.CENTER);
tfMsg.setBackground(Color.yellow);
this.add(tfMsg,BorderLayout.SOUTH);
tfMsg.addActionListener(this);
this.setSize(280,400);
this.setVisible(true);
nickName=JOptionPane.showInputDialog("输入昵称:");
try {
s=new Socket("127.0.0.1",9999);
JOptionPane.showMessageDialog(this,"连接成功");
this.setTitle("客户端:"+nickName);
new Thread(this).start();
}
catch (Exception ex) {
}
}
@Override
public void run(){
try {
while(true) {
InputStream is=s.getInputStream();
BufferedReader br=new BufferedReader(new InputStreamReader(is));
String str=br.readLine();//读
taMsg.append(str+"\n"); //添加内容
}
}
catch (Exception ex) {
}
}
@Override
public void actionPerformed(ActionEvent e) {
try {
OutputStream os=s.getOutputStream();
PrintStream ps=new PrintStream(os);
ps.println(nickName+"说:"+tfMsg.getText());
tfMsg.setText("");
}
catch (Exception ex) {
}
}
public static void main(String[] args) throws Exception {
new Client();
}
}
运行服务器和客户端,就可以得到本案例需求中的效果。
推荐阅读
-
java学习之利用TCP实现的简单聊天示例代码
-
java学习之利用TCP实现的简单聊天示例代码
-
C#使用Socket实现服务器与多个客户端通信(简单的聊天系统)
-
利用java实现一个客户信息管理系统
-
nodejs简单实现TCP服务器端和客户端的聊天功能示例
-
TCP/IP课程设计总结(基于socket实现的简易聊天系统)
-
实现TCP的 客户端和服务器端的 即时聊天通信
-
C#使用Socket实现服务器与多个客户端通信(简单的聊天系统)
-
Go语言实践_实现一(服务器端)对多(客户端)在线聊天室
-
【Socket网络通信】利用TCP/IP协议实现从服务端的文件中读取数据打印到客户端的控制台,服务端对客户端输入过来的数据做出响应...