基于TCP/IP协议的聊天室(无UI)

整体思路

  1. 服务端(Server),建立ServerSocket 的对象,循环建立一个监听是否有客户端连接的对象,每有一个客户端连接则建立一个线程(MyChannel),把与客户端连接的socket套接字传进去并启动该线程。
  2. (MyChannel)每个线程对象的具体功能:
  • 接收来自客户端的数据
  • 然后分发给每个客户端数据(除了自己)
  1. 客户端(Client),建立与服务器的连接,并输入客户端的名称。为了循环的读取来自服务端发送的数据,以及从键盘上读取数据并写出去,我们可以分开建立两个读取(Receive)和写入(Send)的线程,互不干扰,提高读取和分发的效率。两个线程需要传入的参数都是客户端的socket套接字。最后客户端从服务器端连接成功后,分别启动读取和写出的线程。
  2. 建立一个工具类(CloseUtil),帮助关闭资源,传入的参数是可变参数。

下面是代码的结构

基于TCP/IP协议的聊天室(无UI)_第1张图片
在模块Chat1下,有三个包,Client是客户端的包,com.Server是服务端的包,Util 是关闭资源的工具包。
具体的代码如下:

1、Server

public class Server {

    // 创建集合对象,存储每一个连接进来的客户端
    // 使用静态的
    public static List list = new ArrayList<>();
    // 主方法
    public static void main(String[] args) throws IOException {
        // 1、创建ServerScoket 对象
        ServerSocket server = new ServerSocket(9999);
        System.out.println("--------服务器端--------");
        // 2、监听连接
        while (true) {
            Socket socket = server.accept();
            // 创建线程类对象
            MyChannel channel = new MyChannel(socket);
            // 添加到集合中
            list.add(channel);
            // 启动线程
            new Thread(channel).start();
        }
    }
}

2、MyChannel

import Util.CloseUtil;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.List;

public class MyChannel implements Runnable{

//    private Socket client;
    private DataInputStream dis;
    private DataOutputStream dos;
    private boolean flag=true;

    public MyChannel(Socket client){
//        this.client=client;
        try {
            dis=new DataInputStream(client.getInputStream());
            dos=new DataOutputStream((client.getOutputStream()));
        } catch (IOException e) {
            flag=false;
            CloseUtil.closeAll(dis,dos);
        }

    }

    // 接收数据的方法
    private String receive(){
        String str="";
        try {
            str=dis.readUTF();
        } catch (IOException e) {
            flag=false;
            CloseUtil.closeAll(dis,dos);
            Server.list.remove(this);
        }
        return str;
    }

    // 发送数据的方法
    private void send(String str){
        if(str!=null&&str.length()!=0){
            try {
                dos.writeUTF(str);
                // 注意刷新
                dos.flush();
            } catch (IOException e) {
                flag=false;
                CloseUtil.closeAll(dis,dos);
                Server.list.remove(this);
            }
        }
    }

    // 发送给其他的客户端
    private void sendOther(){
        String str=this.receive();
        List list= Server.list;
        for(MyChannel other:list){
            if(other==this) continue;// 不发给自己
            other.send(str); // 发送给其他人
        }
    }
    @Override
    public void run() {
        while (flag){
            // 调用发送给其他客户端的方法
            sendOther();
        }
    }
}

3、Client

import java.io.IOException;
import java.net.Socket;
import java.util.Scanner;

public class Client {


    public static void main(String[] args) throws IOException {
        // 1、创建socket对象
        Socket client = new Socket("localhost",9999);
        System.out.println("--------客户端--------");
        // 第一次写入的名字应该为用户的名字
        System.out.println("请输入自己的名字:");
        Scanner sc= new Scanner(System.in);
        String username=sc.nextLine();
        // 创建发送信息的线程类
        Send send= new Send(client,username);
        // 创建接收信息的线程类
        Receive receive = new Receive(client);

        // 创建Thread类并启动线程
        new Thread(send).start();
        new Thread(receive).start();
    }
}

4、Receive

import Util.CloseUtil;

import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;

public class Receive implements Runnable{
    // 创建接收数据的对象
    private DataInputStream dis;
    // 判断接收的状态
    private boolean flag=true;

    // 定义构造方法
    public Receive(Socket client){
        try {
            dis=new DataInputStream(client.getInputStream());
        } catch (IOException e) {
            flag=false;
            CloseUtil.closeAll(dis,client);
        }
    }

    private String getMessage(){
        String str="";
        try {
            str = dis.readUTF(); // 堵塞状态
        } catch (IOException e) {
            flag=false;
            CloseUtil.closeAll(dis);
        }
        return str;
    }

    @Override
    public void run() {
        while (flag){
            System.out.println(this.getMessage());
        }
    }
}

5、Send

import Util.CloseUtil;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;

public class Send implements Runnable {

    //从键盘获取数据,利用字符缓冲流
    private BufferedReader br;
    // 发送的数据
    private DataOutputStream dos;
    //发送读取的状态
    private boolean flag = true;// 默认为 true

    private String username;

    // 定义构造方法
    public Send() {
        br = new BufferedReader(new InputStreamReader(System.in));
    }

    public Send(Socket client,String username) {
        this();  // 调用本类的午餐构造方法
        this.username=username;
        try {
            dos = new DataOutputStream(client.getOutputStream());
        } catch (IOException e) {
            flag = false;
            CloseUtil.closeAll(dos);
        }

    }

    private String getMessage() {
        String str = "";
        try {
            str = br.readLine();// 这里应该是堵塞状态,每次在run方法中while发送数据的时候
        } catch (IOException e) {
            flag = false;
            CloseUtil.closeAll(br);
        }
        return str;
    }

    private void send(String str){
        try {
            dos.writeUTF(username+"说:"+str);
            dos.flush();
        } catch (IOException e) {
            flag=false;
            CloseUtil.closeAll(dos);
        }
    }

    @Override
    public void run() {
        while (flag){
            this.send(getMessage());
        }
    }
}

6、CloseUtil

import java.io.Closeable;
import java.io.IOException;

public class CloseUtil {

    // 工具类设为静态方法
    // 参数为可变参数
    public static void closeAll(Closeable... able){
        for(Closeable c:able){
            if(c!=null){
                try {
                    c.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

运行结果

这个是客户端xcl,说了hello
基于TCP/IP协议的聊天室(无UI)_第2张图片
然后再Mike和Tom 的客户端,分别收到了来自xcl说的hello.
基于TCP/IP协议的聊天室(无UI)_第3张图片基于TCP/IP协议的聊天室(无UI)_第4张图片
可以看到,每个客户端,写入的数据,通过与服务端建立的socket 连接,被服务器端读取,并分发给其它的客户端,由此,我们实现了一个简单的多人聊天的功能。

基于TCP/IP协议的聊天室(无UI)_第5张图片

你可能感兴趣的:(java学习,java,tcpip,socket)