【练习】TCP协议的编程模型实现多人聊天

使用基于 tcp 协议的编程模型实现多人同时在线聊天,要求每个客户端将发 送的聊天内容发送到服务器,服务器接收到后转发给当前所有在线的客户端。

思路分析

  • 创建一个服务器类,声明一个Server Socket对象并提供一个端口号,无限循环接收客户端的连接,模拟服务器永远在运转的情况,用一个List集合存放客户端连接的Socket
  • 创建一个服务器通信线程类,重写run方法,使用输入输出流和客户端进行通信,无线接收并转发消息;转发消息要遍历List集合,一个Sokcet转发一次
  • 创建客户端操作类,声明一个Socket连接服务器端,用一个user变量作为客户端标识,声明一个有参构造,参数为Socket和user。
  • 创建客户端的线程方法分为接收信息和发送信息,一直无限循环,当发送和接收为bye时break跳出循环,当接收信息中的user为本类user时,continue跳出循环
  • 创建客户端实例类创建操作类的对象,并调用接收和发送信息的方法来创建线程,使用线程的join方法保证主线程在子线程执行完以后再执行

代码示例

服务器整体类

package com.lagou.homework.homework4_3;

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

/**
 * 服务器类
 */
public class Server {
    private static List<Socket> sockets = new ArrayList<>();//创建一个集合存储客户端的socket
    private static ServerSocket ss = null;
    private static Socket s = null;
    public static void main(String[] args) {
        server();
    }
    //连接客户端开始服务
    public static void server(){
        while (true){
            try {
                ss =  new ServerSocket(10086);
                System.out.println("等待连接ing");
                s= ss.accept();//接收客户端的连接并赋值给Socket
                sockets.add(s);
                System.out.println("客户端"+s.getInetAddress()+"连接成功");
                //每连接一个客户使用一个线程为之服务
                new ServerThread(sockets,s).start();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {

                if (null !=ss){
                    try {
                        ss.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

服务器线程通信

package com.lagou.homework.homework4_3;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.List;
import java.util.Scanner;

/**
 * 服务器通信线程类
 */
public class ServerThread extends Thread{
    private List<Socket> sockets;
    private BufferedReader br;

    private Socket s;
    public ServerThread(List<Socket> sockets,Socket s){
        this.sockets = sockets;
        this.s = s;
    }
    @Override
    public void run() {


        try {
            br = new BufferedReader(new InputStreamReader(s.getInputStream()));//获取输入流接收数据

            while (true){
                String str = br.readLine();//接收客户端发来的字符串
                System.out.println(str);
                String[] strings = str.split(":");
                System.out.println("客户端"+strings[0]+"发来了:"+strings[1]);
                for (Socket socket:sockets
                     ) {
                    boolean flag = false;
                    try {
                        flag = socket.getKeepAlive();
                    }catch (Exception e){
                        continue;
                    }

                    if (!flag){
                        PrintStream ps = new PrintStream(socket.getOutputStream());//获取输出流回发数据
                        ps.println(str);
                        ps.flush();

                    }

                }
                System.out.println("已将发来的数据"+str+"转发给其他客户端!");

                if ("bye".equals(strings[1])){
                    System.out.println("客户端"+s.getInetAddress()+"已下线");
                    break;
                }

            }

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(null != s){
                try {
                    s.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null!=br){
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

客户端操作类

package com.lagou.homework.homework4_3;

import com.lagou.homework.homework4_1.SysMain;

import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

/**
 * 客户端
 *
 */
public class ClientClass {
    private String user;//客户端标识
    private PrintStream ps= null;
    private BufferedReader br = null;
    private Scanner sc = null;
    private String str ;
    public ClientClass(String user,Socket socket){
       try {
           this.user = user;
           ps = new PrintStream(socket.getOutputStream());//打印流
           br = new BufferedReader(new InputStreamReader(socket.getInputStream()));//输入流
           sc = new Scanner(System.in);//保证一直处于可输入状态
       } catch (IOException e) {
           e.printStackTrace();
       }

   }

   //发送消息线程
    public Thread send(){
      Thread threadSend = new Thread(){
           @Override
           public void run() {
               while (true){
                      str= sc.next();//获取键盘输入信息
                       ps.println(user+":"+str);//将用户名和输入信息一起发送给服务器
                        ps.flush();
                       if ("bye".equalsIgnoreCase(str)){
                           System.out.println("退出聊天室");
                           break;
                   }

                   }
               }


       };
      return threadSend;
    }
    //接收消息线程
    public Thread receive(){
       Thread threadReceive = new Thread(){
           @Override
           public void run() {
               while (true){

                       String s = null;//读取服务器传来的消息
                       try {
                           s = br.readLine();
                       } catch (IOException e) {
                           e.printStackTrace();
                       }
                       //进行消息拆分
                   String[] strings = null;
                   try {
                       strings = s.split(":");//以:作为拆分点
                   }catch (Exception e){
                       break;
                   }


                   if (user.equalsIgnoreCase(strings[0])){
                       System.out.println("我:"+strings[1]);//如果是本人发送的消息,此客户端界面显示我:+消息
                       continue;
                   }
                      System.out.println(strings[0]+":"+strings[1]);//否则显示user+消息
                   if ("bye".equalsIgnoreCase(strings[1])){
                       break;//如果服务端传来的时bye 则跳出循环
                   }
                   }



               }


       };
       return threadReceive;
    }
    //关闭流
    public void close(){
       if (null!=null){
           sc.close();
       }
       if (null!=br){
           try {
               br.close();
           } catch (IOException e) {
               e.printStackTrace();
           }
       }
       if (null!=ps){
           ps.close();
       }
    }
}

客户端实例1

package com.lagou.homework.homework4_3;

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

/**
 * 客户端实例
 */
public class Client01 {
    public static void main(String[] args) {
        Socket socket = null;
        try {
            socket = new Socket("127.0.0.1",10086);
            System.out.println("连接服务器成功");
            ClientClass c1 = new ClientClass("小明",socket);//创建一个客户端实例
            Thread t1 = c1.send();//获取发送消息线程
            Thread t2 = c1.receive();//获取接收消息线程
            t1.start();
            t2.start();
            t1.join();
            t2.join();
            c1.close();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            //关闭socket
            if (null!=socket){
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }


    }
}

注意:实例不只一个,客户端实例一为参照
运行结果

—————————服务器显示内容———————————
等待连接ing
客户端/127.0.0.1连接成功
等待连接ing
客户端/127.0.0.1连接成功
等待连接ing
小明:我是小明你好
客户端小明发来了:我是小明你好
已将发来的数据小明:我是小明你好转发给其他客户端!
小红:你好我是小红
客户端小红发来了:你好我是小红
已将发来的数据小红:你好我是小红转发给其他客户端!
小明:好的
客户端小明发来了:好的
已将发来的数据小明:好的转发给其他客户端!
小红:bye
客户端小红发来了:bye
已将发来的数据小红:bye转发给其他客户端!
客户端/127.0.0.1已下线
小明:bye
客户端小明发来了:bye
已将发来的数据小明:bye转发给其他客户端!
客户端/127.0.0.1已下线
—————————客户端一显示内容———————————
连接服务器成功
我是小明你好
我:我是小明你好
小红:你好我是小红
好的
我:好的
小红:bye
bye
退出聊天室

Process finished with exit code 0
—————————客户端二显示内容———————————
连接服务器成功
小明:我是小明你好
你好我是小红
我:你好我是小红
小明:好的
bye
退出聊天室
我:bye

  • 9
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值