TCP在线多用户聊天室

项目介绍

该项目是一个用TCP协议实现的多人聊天室,存在一个服务器和多个客户端,总体的思路为-开启一个服务器用于等待其他客户端的连接,有客户端连接之后我们将客户端的socket保存在一个公共的列表中,开启一个线程用于接收和转发给各个连接到服务器的用户,创建一个客户端开启两个线程一个线程用于发送给服务器数据另一个线程用于接收服务器的数据。

项目前知识

当然要是想搞明白该项目我们需要一定的TCP知识:

socket:

  1. 用于创建客户端的对象
  2. socket(String host,int port) -需要传入连接的ip 和 端口
  3. close 用于释放资源

Serversocket:

  1. 用于创建服务端对象
  2. accept() 用于监听客户端的连接,返回连接的客户端的socket对象
  3. getinputStream() 用于获取输入流,读取接受到的数据
  4. close() 用于释放资源

在使用了socket连接之后,剩下的操作就和数据流之间的操作没有什么区别。

DataInputStream 和 DataOutputStream:

  1. 功能: 实现八种基本类型数据的输入/输出。 同时,也可实现字符串的输入/输出。
  2. DataInputStream 数据的字节输入流; DataOutputStream 数据的字节输出流
  3. 特点: 八种基本类型的数据在输入/输出时,会保持类型不变。
  4. 这种流特别适合在网络上实现基本类型数据和字符串的传递。
  5. readUTF() 方法接受String值作为参数,并使用修改后的UTF-8编码将其写入当前输出流.
  6. writeWTF() 方法将经过修改的UTF-8编码的数据读取到String中并返回

项目代码

聊天室一共分为两个类:

  • Server_chat (用于创建服务器,用于接收其它客户端的消息,和分发给每个连接的客户端)
  • client_chat (用于创建用户端 ,用于连接服务器,给服务器发送消息和接收服务器返回的消息)

sever_chat.java:

public class Sever_chat {
    DataInputStream in;
    DataOutputStream out;
    Socket accept;
    List<Socket> outlist;

    public Sever_chat() {}

    public Sever_chat(Socket accept, List<Socket> outlist) throws IOException {
        this.accept = accept;
        in = new DataInputStream(accept.getInputStream());
        this.outlist = outlist;

    }

    public void service() {
        outlist.add(accept);

        new Thread(() -> {
            //            读取客户端发过来信息
            try {
//                        注意我们使用readutf接收数据 就必须用writeutf发送数据
                while (true) {
                    String s = in.readUTF();
                    System.out.println(s);
                    send_ms(s);
                }
            } catch (IOException e) {
                outlist.remove(accept);
                send_ms(accept.getInetAddress() + "离开了聊天室");
                System.out.println(accept.getInetAddress() + "离开了聊天室");
            } finally {
                try {
                    accept.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    public void send_ms(String s) {
        //    将服务器接收到消息转发给连接到的每一个人

        try {
            for (Socket socket : outlist) {
                DataOutputStream outputStream = new DataOutputStream(socket.getOutputStream());
                outputStream.writeUTF(s);
                outputStream.flush();
            }

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

    }


    public static void main(String[] args) throws IOException {
//        创建一个列表保存socket 连接到服务器的客户端
        List<Socket> outlist = new ArrayList<Socket>();
        ServerSocket serverSocket = new ServerSocket(9999);
        while (true) {
//             等待客户端连接
            Socket accept = serverSocket.accept();
            Sever_chat server_file = new Sever_chat(accept, outlist);
            System.out.println(accept.getInetAddress() + "进入聊天室");
            server_file.service();
        }

    }
}

client_file.java

public class client_file {
    private String ip;
    private int port;
    private Socket socket;
    private String name;

    public client_file(String ip, int port,String name) {
        this.ip = ip;
        this.port = port;
        this.name=name;
    }

    public void client(){

        try {
            socket = new Socket(ip, port);

//            创建输出流
            DataOutputStream out = new DataOutputStream(socket.getOutputStream());
            DataInputStream in = new DataInputStream(socket.getInputStream());
            Scanner scanner = new Scanner(System.in);

//            循环执行发送数据
            new Thread(()->{

                    try {
                        while (true) {
                            String s = scanner.nextLine();
                            out.writeUTF(name+":"+s);
                            out.flush();
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
            }).start();

//            循环接收数据
            new Thread(()->{
                    try {
                        while (true) {
                            String s = in.readUTF();
                            System.out.println(s);
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
            }).start();

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

    }

    public static void main(String[] args) {
        new client_file("localhost",9999,"小马").client();
    }


}

如果想运行多个客户端只需要new client_file()对象就可以如我们创建一个新的类模拟创建一个新的用户:

package com.ithei.UDP_manychat;

public class feng_user {
    public static void main(String[] args) {
        new client_file("192.168.43.35",9999,"小冯").client();
    }
}

运行效果:

服务器显示:
在这里插入图片描述
客户端:小冯界面
在这里插入图片描述
客户端:小马界面
在这里插入图片描述

总结:

该项目只要理解了socket之间的通信之后,就变得非常的简单就和我们平时操作流文件没有区别,唯一写的时候有一些困惑的是,创建多个客户端去连接服务器,我将服务器接收到的客户端socket的输出流对象保存在列表中然后在将消息转发给每个客户端的时候便利每个客户端的时候每次都讲消息多次转发给同一个客户端,我试用hashcode查看他们的值是否相同显示不相同但是还是每次将消息发送给了同一个用户,后来我给列表中保存每个连接对象的socket,在给每个客户端发送消息的时候使用socket获取到输出流对象然后发送就解决了这个问题。

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
一、实验目的 1.掌握通信规范的制定及实现。 2.练习较复杂的网络编程,能够把协议设计思想应用到现实应用中。 二、实验内容和要求 1.进一步熟悉VC++6编程环境; 2.利用VC++6进行较复杂的网络编程,完成网络聊天室的设计及编写; 三、实验(设计)仪器设备和材料 1.计算机及操作系统:PC机,Windows; 2.网络环境:可以访问互联网; 四、 TCP/IP程序设计基础 基于TCP/IP的通信基本上都是利用SOCKET套接字进行数据通讯,程序一般分为服务器端和用户端两部分。设计思路(VC6.0下): 第一部分 服务器端 一、创建服务器套接字(create)。 二、服务器套接字进行信息绑定(bind),并开始监听连接(listen)。 三、接受来自用户端的连接请求(accept)。 四、开始数据传输(send/receive)。 五、关闭套接字(closesocket)。 第二部分 客户端 一、创建客户套接字(create)。 二、与远程服务器进行连接(connect),如被接受则创建接收进程。 三、开始数据传输(send/receive)。 四、关闭套接字(closesocket)。 CSocket的编程步骤:(注意我们一定要在创建MFC程序第二步的时候选上Windows Socket选项,其中ServerSocket是服务器端用到的,ClientSocket是客户端用的。) (1)构造CSocket对象,如下例: CSocket ServerSocket; CSocket ClientSocket; (2)CSocket对象的Create函数用来创建Windows Socket,Create()函数会自行调用Bind()函数将此Socket绑定到指定的地址上面。如下例: ServerSocket.Create(823); //服务器端需要指定一个端口号,我们用823。 ClientSocket.Create(); //客户端不用指定端口号。 (3)现在已经创建完基本的Socket对象了,现在我们来启动它,对于服务器端,我们需要这个Socket不停的监听是否有来自于网络上的连接请求,如下例: ServerSocket.Listen(5);//参数5是表示我们的待处理Socket队列中最多能有几个Socket。 (4)对于客户端我们就要实行连接了,具体实现如下例: ClientSocket.Connect(CString SerAddress,Unsinged int SerPort);//其中SerAddress是服务器的IP地址,SerPort是端口号。 (5)服务器是怎么来接受这份连接的呢?它会进一步调用Accept(ReceiveSocket)来接收它,而此时服务器端还须建立一个新的CSocket对象,用它来和客户端进行交流。如下例: CSocket ReceiveSocket; ServerSocket.Accept(ReceiveSocket); (6)如果想在两个程序之间接收或发送信息,MFC也提供了相应的函数。如下例: ServerSocket.Receive(String,Buffer); //String是你要发送的字符串,Buffer是发送字符串的缓冲区大小。ServerSocket.Send(String,Butter);//String是你要接收的字符串,Buffer是接收字符串的缓冲区大小。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值