一个基于TCP/IP的小项目,实现广播消息的功能。(超详细版)

1.结合现状 功能分析

该功能基于上个项目的改进,主要是通过对服务器端代码的修改,以及对客户端作少许修改,实现开启多客户端时,一个客户端发送消息,达到对所有客户端广播的效果。可参考网吧里的点歌系统,比如某某用户在网吧点了一首歌,其他用户电脑的左下角都会弹出一个某某用户点了一首七里香,或者游戏里面的频道聊天,每个人发完消息后,聊天室里的人都知道你发的消息了,就像下图一样,这也正是做这个功能的初衷吧。

2.图说代码

代码细说:

服务器里面定义了两个字段,一个用于服务器与客户端的连接,另一个目的在于做一个接受广播的客户端表单。

服务器里有四个函数,分别对应着客户端监听、客户端连接、接受客户端消息和发报广播。

客户端定义了一个字段

客户端包含4个函数,分别为建立连接,接受广播,非后台的发送消息线程、发送消息四部分

操作流程:

1)开启服务器,即黑线①的过程,启动监听。

2)开启客户端,自动根据IP连接服务器,即绿线②。

3)客户端1发送消息至服务器,服务器广播消息至客户端2,即红线③。

3.代码实现

服务器端包含一个主函数和一个ServerControl类

主函数:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ServerTest
{
    class Program
    {
        static void Main(string[] args)
        {
            // 调用构造函数,使用Start方法
            ServerControl server = new ServerControl();
            server.Start();

            Console.ReadKey();
        }
    }
}

ServerControl类:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ServerTest
{
    public class ServerControl
    {
        // 声明变量(使用Socket需using System.Net.Sockets;)
        private Socket serverSocket;
        // 声明一个集合
        private List<Socket> clientList;

        // 自定义有参构造函数,包含两个方法(IP地址,流程传输方式,TCP协议)
        public ServerControl()
        {
            serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            clientList = new List<Socket>();
        }

        // 创建启动方法(IPEndPoint用于指定地址及端口初始化,需using System.Net;)
        public void Start()
        {
            // 服务器启动
            // 绑定IP地址(为任意IP)与端口(设置为12345)
            serverSocket.Bind(new IPEndPoint(IPAddress.Any,12345));
            serverSocket.Listen(10);
            Console.WriteLine("服务器启动成功");

            // 开启线程:目的实现服务器和客户端一对多连接
            Thread threadAccept = new Thread(Accept);
            threadAccept.IsBackground = true;
            threadAccept.Start();
        }
         // Accept方法测试:接收客户端连接
        private void Accept()
        {
            // 接收客户端方法,会挂起当前线程(.RemoteEndPoint表示远程地址)
            Socket client = serverSocket.Accept();
            IPEndPoint point = client.RemoteEndPoint as IPEndPoint;
            Console.WriteLine(point.Address + "[" + point.Port + "] 连接成功!");
            clientList.Add(client);

            // 开启一个新线程线程,实现消息多次接收
            Thread threadReceive = new Thread(Receive);
            threadReceive.IsBackground = true;
            threadReceive.Start(client);

            // 尾递归
            Accept();
        }

        // Receive方法的使用测试
        // 接收客户端发送过来的消息,以字节为单位进行操作
        // 该方法会阻塞当前线程,所以适合开启新的线程使用该方法
        // Accept()中将Receive作为线程传递对象,所以要注意一点,使用线程传递对象只能是object类型的!!
        private void Receive(object obj)
        {
            // 将object类型强行转换成socket
            Socket client = obj as Socket;

            IPEndPoint point = client.RemoteEndPoint as IPEndPoint;

            // 此处的异常抛出主要针对客户端异常的问题
            // 比如,客户端关闭或者连接中断
            // 程序会停留在int msgLen = client.Receive(msg);这段代码,而导致无法继续往下走
            try
            {
                byte[] msg = new byte[1024];
                // 实际接收到字节数组长度,该方法会阻塞当前线程,即(client.Receive(msg)开始挂起)
                // 同时,这里还是尾递归挂起处
                int msgLen = client.Receive(msg);
                // 将msg装换成字符串
                string msgStr = point.Address + "[" + point.Port + "]:" + Encoding.UTF8.GetString(msg, 0, msgLen);
                Console.WriteLine(msgStr);

                // 调用广播函数
                Broadcast(client,msgStr);
                // 尾递归实现多条消息的接收;和while同理。
                Receive(client);
            }
            catch
            {
                Console.WriteLine(point.Address + "[" + point.Port + "]积极断开");
                // 若客户端中断,则将他在集合中删除
                clientList.Remove(client);
            }
        }

        private void Broadcast(Socket clientOther,string msg)
        {
            foreach(var client in clientList)
            {
                if(client == clientOther)
                {
                    // 不做任何响应
                }
                else
                {
                    client.Send(Encoding.UTF8.GetBytes(msg));
                }
            }
        }
    }
}

客户端包含一个主函数和一个ClientControl类

主函数:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ClientTest
{
    class Program
    {
        static void Main(string[] args)
        {
            // 调用构造函数
            ClientControl client = new ClientControl();
            // 输入本机IP与端口号
            client.Connect("129.211.7.135", 12345);
            // 启动send方法
            client.Send();

            Console.ReadKey();
        }
    }
}

ClientControl类:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ClientTest
{
    public class ClientControl
    {
        // 声明变量
        private Socket clientSocket;

        // 自定义有参构造方法((IP地址,流程传输方式,TCP协议))
        public ClientControl()
        {
            clientSocket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
        }

        // 创建通过IP与端口号连接的方法
        public void Connect(string ip,int port)
        {
            clientSocket.Connect(ip, port);
            Console.WriteLine("连接服务器成功");

            // 客户端接收服务器消息的线程
            Thread threadReceive = new Thread(Receive);
            threadReceive.IsBackground = true;
            threadReceive.Start();
        }

        // 用于测试服务器向客户端返回一条消息
        private void Receive()
        {
            while(true)
            {
                try
                {
                    // 用于接收服务器的回复信息
                    byte[] msg = new byte[1024];
                    int msgLen = clientSocket.Receive(msg);
                    Console.WriteLine("服务器:"+Encoding.UTF8.GetString(msg,0,msgLen));
                }
                // 异常处理方法
                catch
                {
                    Console.WriteLine("服务器积极拒绝!!");
                    // 退出while循环
                    break;
                }
            }
        }

        // Send方法测试:即发送消息,以字节为单位
        public void Send()
        {
            Thread threadSend = new Thread(ReadAndSend);
            // 将该线程设为非后台线程。
            // threadSend.IsBackground = true;
            threadSend.Start();
        }

        private void ReadAndSend()
        {
            // 提示操作方法
            Console.WriteLine("请输入发送至服务器的内容或者输入quit退出");
            // 输入内容
            string msg = Console.ReadLine();
            // 非退出情况下操作方式,使用while可以持续不断的接收用户输入
            while (msg != "quit")
            {
                clientSocket.Send(Encoding.UTF8.GetBytes(msg));
                msg = Console.ReadLine();
            }
        }
    }
}

4.实现过程

该过程将服务器部署至腾讯云服务器,分别在腾讯云服务器和本地PC上各开启2个客户端演示广播过程。

若没有多余的电脑或者云服务器,可将客户端主函数里面的IP地址代码改为127.0.0.1,即可完成本地测试。

原文地址:https://www.cnblogs.com/WeiMLing/p/11332921.html

时间: 2024-10-07 22:31:25

一个基于TCP/IP的小项目,实现广播消息的功能。(超详细版)的相关文章

一个基于TCP/IP的服务器与客户端通讯的小项目(超详细版)

1.目的:实现客户端向服务器发送数据 原理: 2.建立两个控制台应用,一个为服务器,用于接收数据.一个为客户端,用于发送数据. 关键类与对应方法: 1)类IPEndPoint: 1.是抽象类EndPoint的实现类 2.Socket对象的RemoteEndPoint. LocalEndPoint都是这个类型 3.属性Address: 使用IPv4表示的地址 4.属性Port:使用int表示的端口 2)类Socket: 这个类即可以用于作服务器端的开发,又可以作客户端的开发 构造方法: 参数 Ad

基于 TCP/IP 协议的网络编程

在说明基于 TCP/IP 协议的网络编程之前,先来了解一下 Socket(网络套接字): 利用套接字(Socket)开发网络应用程序早已被广泛的采用,以至于成为事实上的标准 通信的两端都要有 Socket,是两台机器间通信的端点(API 原话) 网络通信其实就是 Socket 间的通信 Socket 允许程序把网络连接当成一个流,数据在两个 Socket 间通过 IO 传输 一般主动发起通信的应用程序属客户端,等待通信请求的为服务端 网络编程某种程度上可以称作"Socket 编程" T

java socket 基于TCP/IP 协议

Java socket 基于TCP/IP 协议应用 多线程服务器原理: 1.          服务器端创建serversocket并绑定要监听的端口号 ,循环调用serversoket 的accept()方法,等待客户端的连接请求 2.          客户端创建一个socket绑定服务器端的IP地址和服务器监听的端口号并请求和服务器端连接 3.          服务器端接收到客户端的请求后,创建一个socket与客户端建立专线连接 4.          建立连接的两个socket在一个

JAVA Socket 底层是怎样基于TCP/IP 实现的???

首先必须明确:TCP/IP模型中有四层结构:       应用层(Application Layer).传输层(Transport  Layer).网络层(Internet Layer  ).链路层(LinkLayer)  其中Ip协议(Internet Protocol)是位于网络层的,TCP协议时位于传输层的.通过Ip协议可以使可以使两台计算机使用同一种语言,从而允许Internet上连接不同类型的计算机和不同操作系统的网络.Ip协议只保证计算机能够接收和发送分组数据. 当计算机要和远程的计

IntelliJ IDEA 12 创建Web项目 教程 超详细版

原文:IntelliJ IDEA 12 创建Web项目 教程 超详细版 IntelliJ IDEA 12 新版本发布 第一时间去官网看了下  黑色的主题 很给力 大体使用了下  对于一开始就是用eclipse的童鞋们 估计很难从eclipse中走出来 当然 我也很艰难的走在路上 ... 首先要说一点,在IntelliJ IDEA里面“new Project” 就相当于我们eclipse的“workspace”,而“new Module”才是创建一个工程. 这个和Eclipse有很大的区别 1.官

IntelliJ IDEA 12 创建Web项目 教程 超详细版(转)

IntelliJ IDEA 12 新版本发布 第一时间去官网看了下  黑色的主题 很给力 大体使用了下  对于一开始就是用eclipse的童鞋们 估计很难从eclipse中走出来 当然 我也很艰难的走在路上 ... 首先要说一点,在IntelliJ IDEA里面“new Project” 就相当于我们eclipse的“workspace”,而“new Module”才是创建一个工程. 这个和Eclipse有很大的区别 1.官网下载下来的默认不是黑色的主题 这里需要修改一下 工具栏上的扳手图标 或

基于tcp/ip以太网通信实现0-5v,4-20ma模拟量AI采集以及模拟量AO输出控制-综科智控

ZKA-XXX-ETH 使 用 说 明 书 [ ]绝密 [ ]NDA [X]公开 版本历史 版本 修订日期 修订人 修订内容 1.0 2014-10-13 综科智控 1.0初版正式发布 1.1 2014-12-07 综科智控 填图 1.2 2015-03-01 综科智控 更新配置 1.3 2015-08-13 综科智控 修正组网图 1.4 2015-09-28 综科智控 增加ZKA型号 1.5 2016-05-22 综科智控 增加附件 1.6 2016-11-01 综科智控 修改组网图 1.7

[转] Linux TCP/IP网络小课堂:net-tools与iproute2大比较

http://os.51cto.com/art/201409/450886.htm 如今许多系统管理员仍结合使用ifconfig.route.arp和netstat等命令行工具(它们统称为net-tools),管理和排查各 种网络配置.这类工具原先起源于BSD TCP/IP工具箱,旨在配置老式Linux内核的网络功能.自2001年以后,它在Linux社区的发展就止步不前了.Arch Linux和CentOS/RHEL 7等一些Linux发行版已经弃用了net-tools,其他发行版计划弃用net

网络通信-在浏览器输入url,基于TCP/IP协议的解释

知识点1: 网络模型 TCP/IP四层 和ISO七层模型 (统一省略后面层字.比如传输代表传输层) 知识点2: 在应用层中TCP建立连接,经历的三次握手协议 首先:,TCP协议是什么? 为什么要三次握手? 答:TCP协议是面向连接的,而且是点对点的可靠(无差错.不丢失.不重复.按顺序)数据传输服务,他的一些特点,字节流.连接建立后随时传输数据, 其次,握手的目的是在通知对方自己的初始化序号(Initial Sequence Number),简称ISN,保证数据不混乱. 然后: 三次握手的过程是什