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

Posted weimling

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一个基于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,即可完成本地测试。

技术图片

 

以上是关于一个基于TCP/IP的小项目,实现广播消息的功能。(超详细版)的主要内容,如果未能解决你的问题,请参考以下文章

基于i.MX8MM的数字IP网络广播系统方案

MQTT是IBM开发的一个即时通讯协议,构建于TCP/IP协议上,是物联网IoT的订阅协议,借助消息推送功能,可以更好地实现远程控制

TCP/IP详解学习笔记-广播和多播,IGMP协议

即时通讯开发之详解TCP/IP中的广播和多播IGMP协议

Java TCP/IP Socket基于NIO的TCP通信(含代码)

Java网络编程基础— 基于TCP的NIO简单聊天系统