java多线程服务器,实现客户端间聊天

转载请注明出处:http://blog.csdn.net/htwhtw123/article/details/78762364
通过服务器中转消息,实现多客户端之间的对话。客户端输入格式为:接受消息的客户端编号+空格+要发出的消息。客户端输入end下线。有客户端上线或下线,服务器都会通知其他客户端情况。效果如下图:


1.服务端

服务端为连接上的每一个客户端,提供一个接受线程,监听接受消息,在向客户端发送消息时会临时开启一个发送线程。

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by HeTingwei on 2017/12/9.
 * 多线程服务器,实现多客户端聊天
 */
public class Server {

    List<ReceiveThread> receiveList = new ArrayList<>();//存放已连接客户端类
    private final static int MESSAGE_SIZE = 1024;//每次允许接受数据的最大长度
    int num = 0;//客户端编号

    public static void main(String[] args) {
        new Server();
    }

    //服务端处理逻辑
    Server() {
        ServerSocket serverSocket = null;
        try {
            serverSocket = new ServerSocket(45678);//用来监听的套接字,指定端口号
            while (true) {
                Socket socket = serverSocket.accept();//监听客户端连接,阻塞线程
                System.out.println("连接上客户端:" + num);
//在其他线程处理接收来自客户端的消息
                ReceiveThread receiveThread = new ReceiveThread(socket, num);
                receiveThread.start();
                receiveList.add(receiveThread);

                //有客户端新上线,服务器就通知其他客户端
                String notice="有新客户端上线,现在在线客户端有:客户端:";
                for (ReceiveThread thread : receiveList) {
                    notice = notice + "" + thread.num;
                }
                for (ReceiveThread thread : receiveList) {
                    new SendThread(thread.socket, notice).start();
                }
                num++;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    //接受消息的线程(同时也有记录对应客户端socket的作用)
    class ReceiveThread extends Thread {
        int num;
        Socket socket;//客户端对应的套接字
        boolean continueReceive = true;//标识是否还维持连接需要接收

        public ReceiveThread(Socket socket, int num) {
            this.socket = socket;
            this.num = num;
            try {
                //给连接上的客户端发送,分配的客户端编号的通知
                socket.getOutputStream().write(("你的客户端编号是" + num).getBytes());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void run() {
            super.run();
            //接收客户端发送的消息
            InputStream inputStream = null;
            try {
                inputStream = socket.getInputStream();
                byte[] b;
                while (continueReceive) {
                    b = new byte[MESSAGE_SIZE];
                    inputStream.read(b);
                    b = splitByte(b);//去掉数组无用部分
                    //发送end的客户端断开连接
                    if (new String(b).equals("end")) {
                        continueReceive = false;
                        receiveList.remove(this);
                        //通知其他客户端
                        String message = "客户端" + num + "连接断开\n" +
                                "现在在线的有,客户端:";
                        for (ReceiveThread receiveThread : receiveList) {
                            message = message + " " + receiveThread.num;
                        }
                        System.out.println(message);
                        for (ReceiveThread receiveThread : receiveList) {
                            new SendThread(receiveThread.socket, message).start();
                        }
                    } else {
                        try {
                            String[] data = new String(b).split(" ", 2);//以第一个空格,将字符串分成两个部分
                            int clientNum = Integer.parseInt(data[0]);//转换为数字,即客户端编号数字
                            //将消息发送给指定客户端
                            for (ReceiveThread receiveThread : receiveList) {
                                if (receiveThread.num == clientNum) {
                                    new SendThread(receiveThread.socket, "客户端"+num+"发消息:"+data[1]).start();
                                    System.out.println("客户端" + num + "发送消息到客户端" + receiveThread.num + ": " + data[1]);
                                }
                            }
                        } catch (Exception e) {
                            new SendThread(socket, "输入错误,请重新输入").start();
                            System.out.println("客户端输入错误");
                        }

                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {//关闭资源
                    if (inputStream != null) {
                        inputStream.close();
                    }
                    if (socket != null) {
                        socket.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    //发送消息的线程
    class SendThread extends Thread {
        Socket socket;
        String str;

        public SendThread(Socket socket, String str) {
            this.socket = socket;
            this.str = str;
        }

        @Override
        public void run() {
            super.run();
            try {
                socket.getOutputStream().write(str.getBytes());
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }
    //去除byte数组多余部分
   private   byte[] splitByte(byte b[]) {
        int i = 0;
        for(;i<b.length;i++) {
            if (b[i] == 0) {
                break;
            }
        }
        byte[] b2 = new byte[i];
        for (int j = 0; j <i ; j++) {
            b2[j] = b[j];
        }
        return b2;
    }

}

2.客户端

在主线程中接受键盘输入,并发送消息,在子线程中接受消息。


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

/**
 * Created by HeTingwei on 2017/12/9.
 * 多线程客户端
 */
public class Client {
    Socket socket;
    private static final int MESSAGE_SIZE = 1024;//每次允许接受数据的最大长度

    public static void main(String[] args) {
        new Client();
    }

    public Client() {
        try {
            socket = new Socket("127.0.0.1", 45678);
            if (socket.isConnected() == true) {
                System.out.println("连接成功");
                new Thread() {//开启一个接受数据的线程
                    @Override
                    public void run() {
                        super.run();
                        InputStream in;
                        try {
                            in = socket.getInputStream();
                            byte[] b;
                            while (true) {
                                b = new byte[MESSAGE_SIZE];
                                in.read(b);
                                System.out.println("接收到服务端消息:" + new String(b));
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                        }

                    }
                }.start();
            }
            OutputStream out = null;
            while (true) {
                Scanner scanner = new Scanner(System.in);
                String str = scanner.nextLine();
                out = socket.getOutputStream();
                out.write(str.getBytes());
                out.flush();
                if (str.equals("end")) {
                    System.exit(0);//关闭客户端
                }
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

猜你喜欢

转载自blog.csdn.net/htwhtw123/article/details/78762364