欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

利用TCP实现多客户聊天系统

程序员文章站 2022-03-01 20:44:15
...

读书笔记:《Java语言程序设计》–郭克华

案例介绍

  上一篇博客已经介绍了客户端和服务器的相互通信。但是实际应用中,应该是客户端和客户端聊天,而不是客户端和服务器聊天。客户端和客户端聊天的本质是信息由服务器端转发。因此本篇博客将开发一个支持多个客户端的程序。服务器端界面如图1所示。
利用TCP实现多客户聊天系统
       图一
  当客户端出现时,需要输入昵称,如图2所示。
利用TCP实现多客户聊天系统
        图二
  单击“确定”按钮,连接到服务器。如果连接成功服务器回送一个“连接成功”的信息,如图3所示。
利用TCP实现多客户聊天系统
       图三
  单击“确定”按钮,即可进行聊天。
  为了体现多客户端效果,打开了3个客户端,如图4所示。
利用TCP实现多客户聊天系统利用TCP实现多客户聊天系统利用TCP实现多客户聊天系统
      图四
  在界面下方可以输入消息,按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