Socket编程之聊天程序 - 模拟Fins/ModBus协议通信过程

2023-11-14

 

设备控制软件编程涉及到的基本通信方式主要有TCP/IP与串口,用到的数据通信协议有Fins与ModBus。 更高级别的通信如.net中的Remoting与WCF在进行C/S架构软件开发时会采用。


本篇文章结合Fins/ModBus协议的指令帧结构与数据编码与解码过程,自定义了一套TcpChatter数据数据通信协议,编写了一个聊天程序,说明TCP/IP的在一个项目中应用。


本文涉及到的源代码工程项目为 - TcpChatter 后面附件提供源代码下载 ( OpenSource Code   软件版本:VS2008    语言:C#)


1 先普及几个基本概念



Socket

接触C/C++的人都知道,编写网络程序会用到Socket,对于Socket编程,其基本编程思想就是使用listen,accept,connect,send与write等几个操作来实现客户端与服务端的通信。
对于使用C#的程序员,.net为我们提供了Socket类来编写服务程序,提供了TcpClient来编写客户端程序。我们只需要知道如何使用listen,accept,connect,send与write操作就能编写我们需要的网络程序了。

简单的说:
Socket是支持TCP/IP协议的网络通信的基本操作单元,它是建立在TCP/IP协议上的一组编程接口,是我们编写代码,使用TCP/IP进行数据通信的入口,它是对TCP/IP协议栈的抽像,等效于是TCP/IP协议栈提供的对外编程接口,在.net中,只是说这个编程接口的实现由Micosoft为我们完成,我们做的唯一工作只是使用这些接口就能在我们的应用程序间进行TCP/IP通信了


TCP/IP - Transmission Control Protocol/Internet Protocol

传输控制协议/因特网互联协议,是Internet互联网络的基础,由传输层的TCP协议与网络层的IP协议构成。网络层负责在节点与节点之间传送数据包(IP数据包),该IP数据包由TCP协议来进行组装,IP数据包再通过它的下层协议以太网协议 (IEEE802)在光纤上进行传输,从而将不同的信息从一台计算机传送到了另一台计算机。

对于程序员,我们编写的程序要在不同的计算机间进行数据通信,可以通过Socket编程来使用TCP/IP,从而将我们的数据从一台计算机传到了另一台计算机。


PLC - Programmable Logic Controller
可编程逻辑控制器,一种数字运算操作的电子系统,专为工业环境应用而设计,与计算机一样,可以把它看成是一种用于工业控制的计算机,它也有自己的编程语言 - T形图,可以通过T形图编程来实现各种设备的控制。

Fins - Factory Interface Network Service
Fins协议是欧姆龙开发的用于工业自动化控制网络的指令/响应通信协议,它借助TCP/IP协议与串口通信协议,通过发送Fins指令实现在各种网络间的无缝通信,这里主工是PC与PLC的通信,我们PC可以通过发送Fins指令与PLC进行通信。

ModBus
Modbus是由Modicon公司于1979年发明,是全球第一个真正用于工业现场的总线协议。在工业控制系统中,目前ModBus已经成为一通用工业标准.
Modbus主要应用于电子控制器上的一种通用数据协议,借助TCP/IP协议与串口通信协议,通过发送Modbus指令实现在各种设备之间的通信。目前公司的温控表与PC间的通信采用ModBus。


2 TcpChatter消息传输结构



2.1 TcpChatter软件架构

与传统的软件系统一样,TcpChatter采用C/S架构,即客户端/服务端架构,Client参与通信会话,Server不参与通信会话,只负责将Client的消息通过Server进行转发,从而实现Client-Client间的通信。
整个TcpChatter的代码结构由 ChatServer + ChatClient构成

2.2 TcpChatter消息处理

如下图所示TcpChatterMessageTransaction,为TcpChatter系统中使用的消息处理结构。下图异展示了从 客户端A←→客户端B 的消息传递过程。

2.3 TcpChatter消息处理原理

在不同的客户端进行通信,客户端通过将消息封装为TcpChatter指定的数据格式 [TcpChater指令 + 数据]  然后发送给服务端程序ChatServer,服务端程序再将消息转发给指定的客户端,客户端收到消息后解析TcpChater数据包然后做其它的处理。




3  TcpChatter指令帧结构



目前工业控制中的温控主流采用串口通信,使用数据通信协议为ModBus协议,而与底层PLC通信则多采用Fins协议。下面分别解释ModBus协议与Fins协议的指令帧结构与TcpChatter指令帧结构。


TcpChatter指令 帧用于在客户端与服务端进行统一格式的数据通信。其基本构成为 : TcpChatter指令域 + TcpChatter数据域;

Fins协议与ModBus协议原理基本一样,其各自的指令帧结构 基本有2部分构成: 指令域 + 数据域
指令域为Fins协议与ModBus协议定义的数据通信格式,指令域字节长度也不一样.比如Fins指令有效指令域(Fin头 + Fin指令域)为12个节字,数据域长度能到2000字节,而ModBus协议有效指令域(地址 + 功能码 + CRC校验码)为4个字节,数据域为(256-4)字节或(260-4)字节。
数据域为发送的真正数据。由于受限于硬件设备通信的数据速率, 在串口与TCP/IP通信中, 指令域 + 数据域的总长度是有限制的。我们通过PC与设备进行通信,实际上是在反复的发送这些 数据包与解析这些数据包,从而达到PC与设备信息交互的目的。

熟悉嵌入式的人都知道,我们编写代码跟设备进行通信,基本是在通过操作设备的寄存器对寄存器进行读写从而达到控制设备状态与获取设备状态的目的。寄存器普通的有8位与16位,我们最常见的温控表是8位寄存器,正好一个byte,每一位都可以看成是硬件上的一个I/O,我们通过操作这些位从而操作了对应的硬件的I/O状态 (0/1),设备跟据这些I/O状态做出相应的动作。



3.1 Fins指令帧结构
(
Fins响应帧(应答帧结构)结构与Fins指令帧结构类似)





3.2 ModBus指令帧结构
(ModBus也具备响应帧结构)



2.3 TcpChatter指令帧结构


TcpChatter指令 帧用于在客户端与服务端进行统一格式的数据通信。其基本构成为 : TcpChatter指令域 + TcpChatter数据域;

TcpChatter指令域构成:   命令头 + 命令请求模式+  发送者ID + 收发模式 + 收接都ID + 预留指令
TcpChatter指令域长度: 8bytes
TcpChatter数据域长度: 20kb



3.4 TcpChatter 指令域结构 (该指令在通信过程中变换成了byte,可以进行位操作)


 

/// <summary>
    /// TcpChatter 数据通信命令格式定义
    /// </summary>
    public struct LCmd
    {
        public int Head;            // 有效命令开始标志(命令头)
        public int CmdMode;         // 命令请求模式
        public int SendID;          // 发送者用户ID
        public int WR;              // 发送或读写模式
        public int RecvID;          // 接收者用户ID
        public int Resv2;           // 预留
        public int Resv3;           // 预留
        public int Resv4;           // 预留
    }




 


3.5 TcpChatter 指令集



 

 /// <summary>
    /// 应答请求命令
    /// </summary>
    public enum CmdRequest
    {
        MinID = -1,             
        Online      = 0x01,     // 在线请求
        FixUser     = 0x02,     // 向固定用户发送消息请求
        Flush       = 0x03,     // 向固定用户闪屏请求
        FlushAll    = 0x04,     // 向所有用户闪屏请求
        Broadcast   = 0x05,     // 广播消息请求
        Offline     = 0x06,     // 离线请求
        UpdateUsers = 0x07,     // 用户列表更新请求
        Success     = 0x08,     // 用户连接服务成功应答
        InvalidUser = 0x09,     // 非法用户名 - (预留)
        Failed      = 0x0A,     // 用户连接服务失败应答
        InvalidCmd  = 0xFF,     // 非法命令包 - (预留)
        MaxID,
    }



 


4  ChatServer   -  TcpChatter服务端程序



4.1 ChatAgent服务端  获取客户端独立的Socket连接请求

在TcpChatter项目中,通过TcpListener创建一个监听端口获取Socket连接请求,不同的客户端连接请求(TcpClient的Connect),服务端会创建客户端各自独立的Socket对象,在ChatAgent中通过ClientContext管理了所有连接客户端的Socket,消息的转发通过各自不同的Socket进行。


4.1.1 ClientContext

ChatServer服务端通过dicClientContext 表保存了所有连接客户端的信息,当客户端异常或离线,其客户端资源会被从Server端移除。

private Dictionary<string, ClientContext> dicClientContext = new Dictionary<string, ClientContext>()


 

#region InnerClass - Client Instance Context

        class ClientContext
        {
            internal ClientContext()
            {
            }
            internal byte[] Buf { get; set; }
            internal byte[] HeadBuf { get; set; }
            internal byte[] DataBuf { get; set; }
            internal int UserID { get; set; }
            internal string UserName { get; set; }
            internal Thread MsgHandle { get; set; }
            internal Socket Skt { get; set; }
        }
        #endregion




 

 

4.2 监听连接请求与消息监听流程图

如下图所示,ChatServer启动了一个监听端口,当有新的连接请求达到,会生成新的Socket对象,同时启动Socket服务消息监听线程:

服务监听线程:客户端连接请求线程,有新的客户端成功连接服务端时会生成新的Socket对象。该线程为所有客户端服务。

Socket服务线程:服务监听线程的子线程,用于处理服务端使用Socket转发的消息。为指定Socket的独立客户端服务。




4.3  IChatAgent服务代理接口

TcpChatter的服务端接口含2个属性与2个接口

Name : 服务器名称
IsAlive:服务器激活状态
StartChatServer: 启动服务接口
StopChatServer:关闭服务接口


 public interface IChatAgent
    {
        string Name { get;}
        bool IsAlive { get; }
        bool StartChatServer();
        bool StopChatServer();
    }




4.4 服务监听线程

/// <summary>
        /// 客户端 消息处理主线程
        /// </summary>
        private void MessageProcessThread()
        {
            ClientContext client = null;
            while (IsAlive)
            {
                try
                {
                    byte[] useNameBuf = new byte[MAXBUFSIZE];

                    // 监听连接请求对像
                    Socket msgSkt = tcpListener.AcceptSocket();

                    // 等待上线请求
                    int actualLens = msgSkt.Receive(useNameBuf);        

                    // 获取实际数据长度
                    byte[] buf = this.CopyArrayFrom(useNameBuf, actualLens);

                    byte[] header = null;
                    byte[] dataBuf = null;

                    // 解析上线请求命令包 : 上线请求 + 用户名
                    LErrorCode error = this.ResolveDataPackage(buf, out header, out dataBuf);
                    if (error != LErrorCode.Success)
                    {
                        Console.Error.WriteLine("ResolveDataPackage failed! LErrorCode = {0}", error);
                        continue;
                    }

                    // 校验命令头
                    if (header[0] != ProtocolMsg.LCML)
                    {
                        Console.Error.WriteLine("Invalid cmmand head = {0}", header[0]);
                        continue;
                    }
                    // 是否是上线请求   -  第 1 个命令必须是: 上线请求命令包 + 用户名
                    CmdRequest request = (CmdRequest)header[1];
                    if (request != CmdRequest.Online)
                    {
                        Console.Error.WriteLine("Invalid request command! Cmd = {0}", request);
                        continue;
                    }

                    // 校验用户名的合法性
                    string user = this.GetStringFrom(dataBuf);
                    if (!CheckUserInvalid(user))
                    {
                        string msg = "User name " + user + " has been existed in TcpChatter system! User tried to join chatting failed!";

                        this.currentRequest = CmdRequest.Failed;
                        this.currentRight = LProtocolRight.WR;

                        msgSkt.Send(CurrentCmd);
                        Console.Error.WriteLine(msg);
                        continue;
                    }


                    // 服务端生成用户信息 并动态分配独立用户ID
                    client = new ClientContext();

                    client.UserID = ChatAgent.ActiveID;
                    client.UserName = user;
                    client.Skt = msgSkt;
                    dicClientContext.Add(user, client);

                    this.currentRequest = CmdRequest.Success;
                    this.currentRight = LProtocolRight.WR;
                    this.senderID = client.UserID;

                    // 发送登陆成功命令
                    msgSkt.Send(CurrentCmd);   

                    string sysmsg = string.Format("[系统消息]\n新用户 {0} 在[{1}] 已成功连接服务器[当前在线人数: {2}]\r\n\r\n", 
                        user, DateTime.Now, dicClientContext.Count);
                    Console.WriteLine(SysInfo.Timestamp + sysmsg);

                    Thread.Sleep(1000);         // Sleep 1s

                    Thread handle = new Thread(() =>
                        {
                            if (PreMessageProcess(client, sysmsg))
                            {
                                // 启用用户 消息监听线程
                                SubMsgProcessThread(client, sysmsg);
                            }
                        });
                    handle.Start();

                    dicClientContext[user].MsgHandle = handle;
                }
                catch (SocketException se)
                {
                    Innerlog.Error(dcrlringType, "SocketException Current user =  " + client.UserName + " was offline!", se);
                }
                catch (Exception ex)
                {
                    Innerlog.Error(dcrlringType, "Exception Current user =  " + client.UserName + " was offline!", ex);
                }
            }
        }


 


4.5 Socket服务消息线程

 

 /// <summary>
        /// 用户消息监听线程
        /// </summary>
        /// <param name="client"></param>
        private void SubMsgProcessThread(ClientContext clientx, string message)
        {
            ClientContext client = clientx;
            while (true)
            {
                try
                {
                    byte[] msgBuf = new byte[MAXBUFSIZE];
                    // 监听 并接收数据
                    int actualLens = client.Skt.Receive(msgBuf);
                    byte[] totalBuf = this.CopyArrayFrom(msgBuf, actualLens);

                    byte[] headBuf = null;
                    byte[] dataBuf = null;

                    // 解析命令包
                    LErrorCode error = this.ResolveDataPackage(totalBuf, out headBuf, out dataBuf);

                    client.HeadBuf = headBuf;
                    client.DataBuf = dataBuf;
                    client.Buf = totalBuf;

                    if (error != LErrorCode.Success) continue;

                    // 是否是有效命令
                    if (headBuf[0] != ProtocolMsg.LCML) continue;

                    
                    CmdRequest cmdHead = (CmdRequest)headBuf[1];
                    if (cmdHead == CmdRequest.InvalidCmd ||
                        cmdHead == CmdRequest.MaxID ||
                        cmdHead == CmdRequest.MinID)
                    {
                        Console.Error.WriteLine("Invalid Send Message!");
                        continue;
                    }
                    else
                    {
                        // 用户消息转发
                        UserMessageProcess(client);
                    }
                }
                catch (Exception ex)
                {
                    ClientOfflineProcess(client);
                    //Innerlog.Error(dcrlringType, "Current user =  " + client.UserName + " was offline!", ex);
                    Thread.CurrentThread.Abort();
                }
            }
        }


 

 

4.6  序列化

序列化与反序列化在TcpChatter中被用于消息的编码与解码。编码与解码过程可以详细的参看ChatAgent代码内部实现。

序列化描述了持久化一个对像对流的过程,反序列化则与此过程相反,表示从流到对象的重建过程。在.net中,消息传递,数据存储都大量的用到了序列化与反序列化的操作。


由于客户端与服务端消息传输以byte字节流的方式进行传输,当在客户端之前传递对象时需要对对象进行序列化。如传递客户端在线列表,该列表是一个Dictionary,在客户端与服务端进行Dictionary传递需用到序列化与反序列化。

见 TcpChatter CHTCommon中的SysInfo.cs

 

public static byte[] SerializeGraph<T>(T graph)


 
4.7  反序列化
见 TcpChatter CHTCommon中的SysInfo.cs

public static T DeserializeGraph<T>(byte[] bytes)

 


4.8 ChatClient



4.9  ChatServer 服务端程序

class Program
    {
        static void Main(string[] args)
        {
            int beginner = Win32Manager.TickCounter;
            Console.WriteLine("\r\n-----------------------------------------------------------------------");
            Console.WriteLine(SysInfo.Timestamp + "ChatServer is starting........\r\n");

            IChatAgent agent = new ChatAgent(null);
            int linkCounter = 0;
            bool isStarted = agent.StartChatServer();
            while (!agent.IsAlive)
            {
                if (linkCounter++ > 10)
                {
                    Console.WriteLine(SysInfo.Timestamp + "ChatServer start failed! Try LinkCounter = {0}",linkCounter);
                    break;
                }
                Thread.Sleep(100);
            }

            Console.WriteLine(SysInfo.Timestamp + "Total ElapsedTime = {0}ms", (Win32Manager.TickCounter - beginner));
            if (linkCounter < 10) Console.WriteLine(SysInfo.Timestamp + "ChatServer is running........");

            Console.WriteLine("-----------------------------------------------------------------------\r\n");
            Application.Run();
        }
    }


 


5  TcpChatter运行测试



运行 ChatServer,如图1所示,输入端口服务启动
运行ChatClient,输入用户名就可以聊天了。当有新用户上线或新用户离线时,ChatServer控制台会显示当前用在线用户的情况。



运行ChatClient输入用户名



ChatClient聊天界面






附录:TcpChatter源代码下载        软件版本: VS2008    语言:C#




TcpChatter源代码.rar
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Socket编程之聊天程序 - 模拟Fins/ModBus协议通信过程 的相关文章

  • TCP/IP协议:传输层之UDP

    一 UDP用户数据报协议 它是一个无连接的 面向数据报的协议 它不提供可靠性但传输速度比TCP要快 UDP数据报中的 UDP长度 为两个字节 所以我们要发送的UDP数据最多支持65507大约68K的数据 超过该大小的话需要自己来分割发送 使
  • TCP/IP协议栈及网络基础,协议栈原理及实现

    1 TCP IP协议栈及网络基础 推荐这个在B站几千观看的视频讲解 底层原理到徒手实现 TCP IP网络协议栈 tcp协议栈 如何实现 C C Linux服务器开发高级架构学习视频点击 C C Linux服务器开发高级架构师 Linux后台
  • 网络通信TCP协议三次握手

    TCP是什么 TCP Transmission Control Protocol 传输控制协议 是一种面向连接 连接导向 的 可靠的 基于IP的传输层协议 TCP在IP报文的协议号是6 TCP是一个超级麻烦的协议 而它又是互联网的基础 也是
  • TCP协议,TCP报头及特点基础介绍

    目录 TCP协议 TCP协议特点 TCP协议适用场景 TCP包首部 什么是TCP连接 如何唯一确定一个TCP连接 有一个 IP 的服务器监听了一个端口 它的 TCP 的最大连接数是多少 TCP与UDP的区别 TCP协议 TCP是一种面向字节
  • UDP服务recvfrom函数设置非阻塞

    基本概念 其实UDP的非阻塞也可以理解成和TCP是一样的 都是通过socket的属性去做 方法一 通过fcntl函数将套接字设置为非阻塞模式 方法二 通过套接字选项SO RECVTIMEO设置超时 方法一源码 编译 g udp server
  • golang之跨语言ipc通信

    1 golang之跨语言ipc通信 文章目录 1 golang之跨语言ipc通信 1 1 unix domain Socket unix域套接字 介绍 1 2 IPC SOCKET通信 1 2 1 函数及地址定义介绍 1 2 2 UNIX
  • 在外远程登录局域网下的象过河ERP管理系统,无需公网IP

    文章目录 概述 1 查看象过河服务端端口 2 内网穿透 3 异地公网连接 4 固定公网地址 4 1 保留一个固定TCP地址 4 2 配置固定TCP地址 5 使用固定地址连接 转发自CSDN远程穿透的文章 公网远程访问公司内网象过河ERP系统
  • Socket编程中的强制关闭与优雅关闭及相关socket选项

    以下描述主要是针对windows平台下的TCP socket而言 首先需要区分一下关闭socket和关闭TCP连接的区别 关闭TCP连接是指TCP协议层的东西 就是两个TCP端之间交换了一些协议包 FIN RST等 具体的交换过程可以看TC
  • 端口开放,ubuntu开放指定端口 包括TCP UDP

    netstat nupl UDP类型的端口 netstat ntpl TCP类型的端口 a 表示所有 n 表示不查询dns t 表示tcp协议 u 表示udp协议 p 表示查询占用的程序 l 表示查询正在监听的程序 在ubuntu下面开放端
  • Windows实例如何通过本地安全策略限制远程登录的IP地址

    Windows实例如何通过本地安全策略限制远程登录的IP地址 阿里云 禁止所有的IP地址连接服务器的RDP端口 远程连接登录服务器 单击 开始 选择 运行 输入gpedit msc 单击 确定 打开本地组策略编辑器 在左侧依次找到 计算机配
  • IP网址可访问,域名网址无法访问

    可以通过修改DNS排查问题 一 修改DNS的好处 适当提高上网速度 更换DNS可以访问某些因为域名解析存在问题而不能访问的网站 可以屏蔽运营商的广告 还可以帮助您避免被钓鱼的危险 二 修改DNS带来的副作用 无法访问页面或者访问的页面不是你
  • 海南省某部队实现资产管理和IP地址管理

    在快速发展的网络环境中 如何有效管理资产和IP地址已成为众多组织面临的挑战 海南省某部队 作为一个肩负重要使命的单位 对此有着更为迫切的需求 为了应对这一挑战 他们选择了一个备受赞誉的系统管理平台 监控易 并附加了三年的维保服务 本文将详细
  • 工业RFID读写器性能参数解析

    工业RFID读写器的性能参数主要有以下几项 工作频率 输出功率 输出接口 读写器类型 工作方式 读写器优先或电子标签优先等 1 工作频率 RFID读写器的工作频率是指其工作的频率范围 通常由读写器的工作频率决定 同时要与电子标签的工作频率保
  • C# Tcplistener,Tcp服务端简易封装

    文章目录 前言 相关文章 前言 设计 代码 简单使用 运行结果 前言 我最近有个需求要写Tcp服务端 我发现Tcp服务端的回调函数比较麻烦 简化Tcp的服务 我打算自己封装一个简单的Tcp服务端 相关文章 C TCP应用编程三 异步TCP应
  • linux下查看所有tcp端口情况

    netstat ntlp
  • 需要 modbus Java 库 [关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我需要带有源代码的简单 modbus Java 库 我在谷歌上找到了 但有 jar 文件 并且没有强大的
  • 如何使用Imagewheel搭建一个简单的的私人图床无公网ip也能访问

    文章目录 1 前言 2 Imagewheel网站搭建 2 1 Imagewheel下载和安装 2 2 Imagewheel网页测试 2 3 cpolar的安装和注册 3 本地网页发布 3 1 Cpolar临时数据隧道
  • 搞懂 三次握手四次挥手

    计算机网络体系结构 在学习TCP 三次握手四次挥手之前 让我们先来看下计算机网络分层 主要分为OSI模型和TCP IP模型 OSI模型比较复杂且学术化 所以我们实际使用的TCP IP模型 以连接Mysql服务器为例理解这五层 应用层 应用层
  • 计算 modbus RTU 3.5 字符时间

    我是 Modbus 新手 正在使用 Modbus RTU 开发应用程序 我想知道如何找出RTU消息帧分离时间 在Modbus RTU规范中 它提到了3 5个字符时间 但是没有更多关于如何决定这个间隔的数据 计算分离时间的步骤是什么 看看第1
  • Modbus 错误:[无效消息] 收到的消息不完整,预计至少 2 个字节(收到 0 个字节)

    Problem pymodbus 主站 客户端可以向从站 服务器发送请求 从属 服务器准备好返回的东西 并等待主控 客户端来接收它们 尽管服务器 从站已准备就绪 但主站 客户端仅返回错误 Modbus 错误 输入 输出 Modbus 错误

随机推荐

  • 剖析muduo网络库核心代码,重写muduo库

    项目简介 模拟muduo库实现nonnon blocking IO multiplexing loop线程模型的高并发 TCP 服务器模型 开发环境 Centos7 技术栈 C 多线程 socket网络编程 epoll多路转接 项目设计 整
  • 某机字长为32位,存储容量为64MB,若按字节编址.它的寻址范围是多少?

    问题 1 某计算机字长为32位 其存储容量为16MB 若按双字编址 它的寻址范围是多少 2 某机字长为32位 存储容量为64MB 若按字节编址 它的寻址范围是多少 解答 我的方法是全部换算成1位2进制的基本单元来算 先计算总容量 如第一题中
  • telnet端口不通怎么解决(单边不通的方法建议)

    telnet端口不通是大家在检测端口的时候可能会遇到的问题之一 遇到这种状况一般要如何解决呢 这里为各位带来分享 看一下telnet端口不通的解决方式 看一下如何处理吧 telnet端口不通怎么解决 1 开放供应商服务器端口 总是出现由于连
  • The engine “node“ is incompatible with this module. Expected version

    前言 vue项目用了yarn yarn install后报错如下 开始 执行 yarn config set ignore engines true 然后yarn install后成功 结束 在此记录问题 如有需要修改的地方 还请不吝赐教
  • Kubernetes—K8S运维管理

    Kubernetes K8S运维管理 更新中 一 Node管理 1 1 Node的隔离与恢复 1 2 Node 的扩容 二 更新资源对象的Label 三 Namespace 集群环境共享与隔离 3 1 创建Namespace 3 2 定义C
  • [病虫害识别|博士论文]面向农作物叶片病害鲁棒性识别的深度卷积神经网络研究

    文章目录 创新点 文章中的方法 国内外现状 手工设计特征 基于深度特征学习的农作物病害识别研究 基于高阶残差的卷积神经网络的农作物病害识别 结构图 对比方法 基于高阶残差和参数共享反馈的卷积神经网络农作物病害识别方法 结构图 对比方法 基于
  • CSS选择除第一个和最后两个以外的所有子元素 + 结构伪类选择器深度解析

    最近在练习网易严选首页的布局时 发现它的顶部导航栏需求很特殊 第一项和最后两项是没有下拉选择框的 那么问题来了 在写css的时候该怎么使用选择器去达到这样的需求呢 首先先贴一下我最后的解决方案 nav first gt li nth chi
  • 数据库技术之mysql50题

    目录 数据表介绍 数据SQL 练习题 数据表介绍 1 学 表 Student SId Sname Sage Ssex SId 学 编号 Sname 学 姓名 Sage 出 年 Ssex 学 性别 2 课程表 Course CId Cname
  • 18-Go语言之单元测试

    go test工具 Go语言中的测试依赖go test命令 编写测试代码和编写普通的Go代码过程是类似的 并不需要学习新的语法或工具 go test命令是一个按照一定约定和组织的测试代码的驱动程序 在包目录内 所有以 test go为后缀的
  • 就业DAY7_web服务器_http协议

    import socket def servece client new socket 为这个客户端返回数据 1 接收浏览器发送过来的请求 即http请求 GET HTTP 1 1 request new socket recv 1024
  • 【Unity3D】如何快速做出点击按钮切换场景

    1 首先建立第一个场景 在Canvas创建一个Button 快捷键为Ctrl N 再按Ctrl S保存该场景到文件 如图所示 图中的 开始 为按钮 2 创建第二个场景 作为点击按钮后切换的场景 点击左上角 File Build Settin
  • 精心挑选了三种热门的Python技术书籍送给大家!!

    本周三狗哥给大家挑选了三种热门的Python书籍 送给大家 每种书送两本 共6本 文末查看送书规则 Python大数据分析 公众号回复 送书 Python最优化算法实战 扫码回复 送书 Python数据分析 扫码回复 送书 公众号回复 送书
  • js 把带有对象的数组里的某个属性组成新的数组

    如果想将数组对象中的某个属性组成一个新的数组 可以使用Array map 方法 这个方法会遍历原始数组的每个元素 并返回一个新的数组 其中包含指定属性的值 以下是一个示例 假设有一个包含对象的数组 每个对象都有一个name属性 你想要将所有
  • html5新特性

    目录 使用语义化标签的目的 1 html5新增的语义化标签 2 html新增的多媒体标签 1 视频 video 2 音频 audio 属性 object fit 3 html5新增的input表单元素属性 1 新增的input标签type属
  • 准备加入第二个项目(第5960小时加入)

    今天 老师过来办事 看了我做的东西后 邀请我加入他的项目 让我受宠若惊 2012年10月 我加入老师的项目后 2天内落荒而逃 因为一句代码都没有写出来 再然后 老师以我没有项目经验为由 拒绝了我后来想加入项目的要求 2年后 老师邀请我去做项
  • 安装Anaconda科学计算包

    Anaconda介绍 最近在看 Python语言及其应用 这本书 作为一本介绍Python语言和应用的书非常不错 在这本书的最后 介绍了一些Python常用的第三方类库 像科学计算库 金融计算库 图形图像库等等 其中也介绍了Anaconda
  • 移动端H5页面生成图片解决方案

    现在有很多微信公众号运营活动 都有生成图片的需求 生成图片后可以发送给好友和发到朋友圈扩散 利于产品的宣传 1 生成图片可以用canvas 但是由于已经有了html2canvas这个开源库 所以为了节省时间就没有自己写了 github地址
  • 为什么文件删除了但磁盘空间没有释放?

    1 案例现象 这天 监控系统发来一条告警消息 内容说某台服务器根目录磁盘占用空间达到阈值 超过百分之八十了 登上服务器 df Th 看一下 发现磁盘空间确实不够用了 root localhost df Th 文件系统 类型 容量 已用 可用
  • java怎么从一个类传值到另一个类,关于JAVA的引用类型传值.

    方法参数传递都按值传递 对于基本类型 传递原始值 对于对象类型 传递其指向的对象的地址值 多个同类型不同的变量可以指向同一个对象 但是其中任何一个变量被重新赋值 也就是指向一个新的对象时 不影响其它变量的指向 方法定义的形参 在调用的发生的
  • Socket编程之聊天程序 - 模拟Fins/ModBus协议通信过程

    设备控制软件编程涉及到的基本通信方式主要有TCP IP与串口 用到的数据通信协议有Fins与ModBus 更高级别的通信如 net中的Remoting与WCF在进行C S架构软件开发时会采用 本篇文章结合Fins ModBus协议的指令帧结