一套基于TCP连接的聊天系统(服务器端+客户端)

这套聊天系统是本人为了完成计算机网络大作业而独自编写的,百分百原创,在提交完大作业后便拿出来给大家分享。

其主要功能的使用和代码的解释都在注释里,如有写的不好的地方请多见谅。

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;

//服务器端
public class Server {

    private static HashMap<String,ServerSocket> ConnectPort; //监听端口池,用于让服务器知道有哪些端口号正在被使用,便于管理端口资源
    private static HashMap<String,Socket> PublicConnect; //公共聊天室连接池
    private static Set<Map.Entry<String,Socket>> PublicConnectSet; //公共连接池集合,用于轮询用户进行广播
    private static ServerSocket listenSocket; //8080公共端口,用于给用户分发专属端口
    private static Scanner scanner;//控制台输入
    private static int modeflag; //服务器运行模式标志
    private static int privateflag; //私人聊天室建立完成标志,用于释放公共聊天线程的堵塞状态

    //主线程,负责服务器启动和模式切换
    public static void main(String[] args) throws IOException {

        ServerInit();
        new GetConnectionThread().start();
        new PublicBroadcastThread().start();

        while (true){
            String command = scanner.nextLine();
            ModeChange(command);
        }
    }

    //服务器全局变量初始化
    public static void ServerInit() throws IOException {
        listenSocket = new ServerSocket(8080,10);
        ConnectPort = new HashMap<>();
        PublicConnect = new HashMap<>();
        PublicConnectSet = PublicConnect.entrySet();
        scanner = new Scanner(System.in);
        modeflag = 2;//默认为监控模式
        privateflag = 0;
        if (modeflag == 2){
            System.out.println("TCP服务器初始化成功");
        }
    }

    //模式切换
    public static void ModeChange(String command){
        if("mode".equals(command)){
            System.out.println("模式选择:1、静默模式 2、监控模式");
            modeflag = scanner.nextInt();
            System.out.println("现在是" + modeflag + "模式");
        }
    }

    //通过8080端口分发专属端口的方式与客户端建立连接(TCP)
    public static void BuildNewConnect(ServerSocket listenSocket) throws Exception {
        Socket TCPsocket = null;
        try {
            byte[] bytes = new byte[1024];
            ServerSocket ConnectSocket = new ServerSocket();
            ConnectSocket.bind(new InetSocketAddress("127.0.0.1",0));//端口0表示让操作系统随机分配一个空端口
            int port = ConnectSocket.getLocalPort();
            String portstr = String.valueOf(port);
            TCPsocket = listenSocket.accept();
            InputStream inputStream = TCPsocket.getInputStream();
            OutputStream outputStream = TCPsocket.getOutputStream();
            TCPsocket.setSoTimeout(10000);//设置tcp连接超时
            int num = inputStream.read(bytes);
            String ConnectName = new String(bytes,0,num);//获取连接的用户用户名,便于对该连接进行管理
            bytes = portstr.getBytes();
            outputStream.write(bytes);//向客户端发送新端口号
            TCPsocket.close();
            AddPublicConnectPool(ConnectName,ConnectSocket);
        }
        catch (Exception e){
            TCPsocket.close();
        }
    }

    //将客户端的Socket入公共聊天室连接池,端口号入监听端口池
    public static void AddPublicConnectPool(String ConnectName,ServerSocket ConnectSocket) throws IOException {
        Socket dataSocket = ConnectSocket.accept();
        ConnectPort.put(ConnectName,ConnectSocket);
        PublicConnect.put(ConnectName,dataSocket);
        if (modeflag == 2){
            System.out.println(ConnectName + "与服务器建立通讯");
        }
    }

    //判断特殊字符,‘#’为当用户处于公共池中时与服务器断开连接,‘$’为准备建立私人聊天室连接池
    public static int CheckHead(String words){
        char head = words.charAt(0);
        if(head == '#' ){
            return 1;
        }
        else if (head == '$'){
            return 2;
        }
        return 0;
    }

    //客户端与服务器友好的断开连接
    public static void Finish(String name) throws IOException {
        Socket socket = PublicConnect.get(name);
        ServerSocket serverSocket = ConnectPort.get(name);
        PublicConnect.remove(name);
        ConnectPort.remove(name);
        OutputStream outputStream = socket.getOutputStream();
        String finish = "连接断开";
        outputStream.write(finish.getBytes());//回复用户断开连接的请求
        socket.close();
        serverSocket.close();
        if (modeflag == 2){
            System.out.println(name + "断开与服务器的连接");
        }
    }

    //公共池获取客户端输入流数据,配合轮询食用
    public static int PublicReceive(Socket dataSocket,byte[] bytes) throws IOException {
        InputStream inputStream = dataSocket.getInputStream();
        try {
            dataSocket.setSoTimeout(100); //设置输入流获取超时防止线程堵塞
            int num = inputStream.read(bytes);
            return num;
        }
        catch (Exception e){
            return 0;
        }
    }

    //私人池获取客户端输入流数据,配合轮询食用
    public static int PrivateReceive(Socket dataSocket,byte[] bytes) throws IOException {
        InputStream inputStream = dataSocket.getInputStream();
        try {
            dataSocket.setSoTimeout(100);
            int num = inputStream.read(bytes);
            return num;
        }
        catch (Exception e){
            return 0;
        }
    }

    //将指定客户端的Socket从公共池转移至指定私人池,建立私人池时使用
    public static HashMap<String,Socket> AddPrivateConnectPool(String username,Socket dataSocket,HashMap<String,Socket> PrivateConnect){
        PrivateConnect.put(username,dataSocket);
        PublicConnect.remove(username);
        PublicConnectSet = PublicConnect.entrySet();//更新公共池连接集合
        return PrivateConnect;
    }

    //将指定客户端的Socket从私人池转移回公共池,退出私人池时使用
    public static HashMap<String,Socket> Exit(String name,Socket socket,HashMap<String,Socket> PrivateConnect){
        PublicConnect.put(name,socket);
        PrivateConnect.remove(name);
        PublicConnectSet = PublicConnect.entrySet();
        try {
            OutputStream outputStream = socket.getOutputStream();
            String str = "您离开了私人聊天室,已返回公共聊天室";
            outputStream.write(str.getBytes());
        } catch (IOException e) {

        }
        if (modeflag == 2){
            System.out.println(name + "离开了一个私人聊天室");
        }
        Set<Map.Entry<String,Socket>> PrivateConnectSet = PrivateConnect.entrySet();//建立一个临时私人池连接集合,用于向私人池内其他用户广播该用户退出信息
        for (Map.Entry<String,Socket> entry : PrivateConnectSet){
            try {
                OutputStream outputStream1 = entry.getValue().getOutputStream();
                String str1 = name + "离开了私人聊天室";
                outputStream1.write(str1.getBytes());
            } catch (IOException e) {

            }
        }
        return PrivateConnect;
    }

    //8080端口监听处理线程(常开)
    static class GetConnectionThread extends Thread{
        @Override
        public void run() {
            if (modeflag == 2){
                System.out.println("创建新连接线程启动");
            }
            while (true){
                try {
                    BuildNewConnect(listenSocket);
                    PublicConnectSet = PublicConnect.entrySet();//更新集合
                } catch (Exception e) {
                    if (modeflag == 2){
                        System.out.println("发生一次连接失败");
                    }
                }
            }
        }
    }

    //公共池广播线程(常开)
    static class PublicBroadcastThread extends Thread{
        @Override
        public void run() {
            if (modeflag == 2){
                System.out.println("公共聊天室广播线程启动");
            }
            byte[] bytes = new byte[1024];
            while (true){
                privateflag = 0;
                try {
                    for (Map.Entry<String,Socket> entry : PublicConnectSet){ //轮询用户
                        Socket dataSocket = entry.getValue();
                        String NowUser = entry.getKey();
                        int num = PublicReceive(dataSocket,bytes);
                        if(num == 0){
                            continue;
                        }
                        else {
                            String words = new String(bytes,0,num);
                            if (modeflag == 2){
                                System.out.println(NowUser + ":" + words);
                            }
                            if(CheckHead(words) == 1){
                                Finish(NowUser);
                            }
                            else if (CheckHead(words) == 2){
                                new BuildPrivateConnectThread(NowUser,dataSocket).start();
                                while (true){
                                    Thread.sleep(100);//必须要睡一小会,不然线程会被强制关闭?
                                    if(privateflag == 1){
                                        break;
                                    }
                                }
                            }
                            else {
                                for (Map.Entry<String,Socket> entry1 : PublicConnectSet){ //向其他用户进行广播
                                    if(entry1.getKey().equals(NowUser)){
                                        continue;
                                    }
                                    else {
                                        Socket othersSocket = entry1.getValue();
                                        String str = NowUser + ":" + words;
                                        OutputStream outputStream = othersSocket.getOutputStream();
                                        outputStream.write(str.getBytes());
                                    }
                                }
                            }
                        }
                    }
                }
                catch (Exception e){

                }
            }
        }
    }

    //私人池创建线程,一次性使用
    static class BuildPrivateConnectThread extends Thread {

        private String NowUser;
        private Socket dataSocket;

        public BuildPrivateConnectThread(String NowUser,Socket dataSocket){
            this.NowUser = NowUser;
            this.dataSocket = dataSocket;
        }

        @Override
        public void run() {
            if (modeflag == 2){
                System.out.println("私人聊天室创建线程启动");
            }
            byte[] bytes = new byte[1024];
                try {
                    HashMap<String, Socket> PrivateConnect = new HashMap<>();
                    Set<Map.Entry<String, Socket>> PrivateConnectSet = null;
                    PrivateConnect = AddPrivateConnectPool(NowUser, dataSocket, PrivateConnect);//给每个私人聊天室建立一个单独的池
                    PrivateConnectSet = PrivateConnect.entrySet();
                    OutputStream outputStream = dataSocket.getOutputStream();
                    InputStream inputStream = dataSocket.getInputStream();
                    String str1 = "准备创建" + NowUser + "的私人聊天室";
                    String str2 = "请输入要邀请加入私人聊天室的成员名字,输入-1结束";
                    outputStream.write(str1.getBytes());
                    outputStream.write(str2.getBytes());
                    while (true) { //输入公共池存在的用户名,拉该用户入私人池,-1结束
                        try {
                            int num1 = inputStream.read(bytes);
                            String name = new String(bytes, 0, num1);
                            if ("-1".equals(name)) {
                                break;
                            } else {
                                for (Map.Entry<String, Socket> entry1 : PublicConnectSet) {
                                    if (name.equals(entry1.getKey())) {
                                        String NewUser = entry1.getKey();
                                        Socket socket = entry1.getValue();
                                        PrivateConnect = AddPrivateConnectPool(NewUser, socket, PrivateConnect);
                                        PrivateConnectSet = PrivateConnect.entrySet();
                                        OutputStream outputStream1 = socket.getOutputStream();
                                        String str = "您被拉入" + NowUser + "的私人聊天室";
                                        outputStream1.write(str.getBytes());
                                    }
                                }
                            }
                        }
                        catch (Exception e){

                        }
                    }
                    PrivateBroadcastThread PrivateBroadcast = new PrivateBroadcastThread(PrivateConnect, PrivateConnectSet);
                    PrivateBroadcast.start();
                    String success = "私人聊天室创建成功";
                    outputStream.write(success.getBytes());
                    privateflag = 1;
                } catch (Exception e) {

                }
                if (modeflag == 2){
                    System.out.println("私人聊天室创建线程关闭");
                }
            }
        }

    //私人池广播线程,一个私人池一个线程,私人池为空时自动关闭对应线程
    static class PrivateBroadcastThread extends Thread{

        private HashMap<String,Socket> PrivateConnect;
        private Set<Map.Entry<String,Socket>> PrivateConnectSet;

        public PrivateBroadcastThread(HashMap<String,Socket> PrivateConnect,Set<Map.Entry<String,Socket>> PrivateConnectSet) {
            this.PrivateConnect = PrivateConnect;
            this.PrivateConnectSet = PrivateConnectSet;
        }

        @Override
        public void run() {
            if (modeflag == 2){
                System.out.println("一个私人聊天室广播线程启动");
            }
            byte[] bytes = new byte[1024];
            while (true){
                try {
                    for (Map.Entry<String,Socket> entry : PrivateConnectSet){
                        Socket dataSocket = entry.getValue();
                        String NowUser = entry.getKey();
                        int num = PrivateReceive(dataSocket,bytes);
                        if(num == 0){
                            continue;
                        }
                        else {
                            String words = new String(bytes,0,num);
                            if (modeflag == 2){
                                System.out.println("$" + NowUser + ":" + words);
                            }
                            if("%".equals(words)){ //输入%离开私人聊天室
                                PrivateConnect = Exit(NowUser, dataSocket, PrivateConnect);
                                PrivateConnectSet = PrivateConnect.entrySet();
                            }
                            else {
                                for (Map.Entry<String,Socket> entry1 : PrivateConnectSet){
                                    if(entry1.getKey().equals(NowUser)){
                                        continue;
                                    }
                                    else {
                                        Socket othersSocket = entry1.getValue();
                                        String str = "$" + NowUser + ":" + words;
                                        OutputStream outputStream = othersSocket.getOutputStream();
                                        outputStream.write(str.getBytes());
                                    }
                                }
                            }
                        }
                    }
                }
                catch (Exception e){

                }
                if (PrivateConnect.isEmpty()){
                    if (modeflag == 2){
                        System.out.println("一个私人聊天室无人连接,聊天室线程关闭");
                    }
                    break;
                }
            }
        }
    }
}
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;

//客户端,如需建立多个客户端只需进行复制黏贴,然后修改类名和连接用户名即可
public class Customer1 {
    public static void main(String[] args) throws IOException {
        //主线程,负责获取连接,启动接收线程和负责发送数据
        Scanner scanner = new Scanner(System.in);
        Socket socket = getConnect(8080);

        //接收数据线程
        Thread receiveThread = new Thread(){
            @Override
            public void run() {
                byte[] bytes = new byte[1024];
                while (true){
                    try {
                        InputStream inputStream = socket.getInputStream();
                        int num = inputStream.read(bytes);
                        String str = new String(bytes,0,num);
                        System.out.println(str);
                    }
                    catch (Exception e){

                    }
                }
            }
        };
        receiveThread.start();

        while (true){
            String say = scanner.nextLine();
            if("#".equals(say)){
                Finish(socket,receiveThread);
                return;
            }
            else {
                OutputStream outputStream = socket.getOutputStream();
                outputStream.write(say.getBytes());
                System.out.println("我:" + say);
            }
        }
    }

    //与服务器获取连接(TCP)
    public static Socket getConnect(int connectport) throws IOException {
        Socket TCPsocket = new Socket("127.0.0.1",connectport);
        byte[] bytes = new byte[1024];
        InputStream inputStream = TCPsocket.getInputStream();
        OutputStream outputStream = TCPsocket.getOutputStream();
        String username = "Customer1"; //连接用户名
        outputStream.write(username.getBytes());
        int num = inputStream.read(bytes);
        int port = Integer.parseInt(new String(bytes,0,num));//获取新端口号
        Socket datasocket = new Socket("127.0.0.1",port);
        TCPsocket.close();
        return datasocket;
    }

    //与服务器友好断开连接并关闭客户端
    public static void Finish(Socket socket,Thread thread) throws IOException {
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write("#".getBytes());
        try {
            Thread.sleep(2000);
        }
        catch (Exception e){

        }
        thread.stop();
        socket.close();
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值