C# TCP/IP网络数据传输及实现

2023-11-06


一、概念简述

1、什么是OSI 和TCP/IP

  国际标准化组织(ISO)在1985年研究的网络互连模型,定义了OSI(Open System Interconnect),即开放式系统互联。 也叫OSI参考模型,OSI定义了网络互连的七层框架(物理层、数据链路层、网络层、传输层、会话层、表示层、应用层)。这个框架定义了理论的标准模型,也是通讯学习基础的必修内容。

  实际网络中TCP/IP协议中使用“TCP/IP五层模型”,与其各层有各自的协议, 来保证能互联网中正常通讯。下面用表格简述了模型的对应关系。

在这里插入图片描述

2、什么是套接字Socket

  百度百科的解释:所谓套接字(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。从所处的地位来讲,套接字上联应用进程,下联网络协议栈,是应用程序通过网络协议进行通信的接口,是应用程序与网络协议栈进行交互的接口。
  简单理解就是 Socket就是应用层与TCP/IP协议族通信的中间软件抽象层。将传输层封装后便与应用层更方便的调用。

在这里插入图片描述

3、TCP 和 UDP

  TCP(Transmission Control Protocol) —— 传输控制协议
  UDP(User Datagram Protocol) —— 用户数据报协议
  从协议层我们可以看出TCP和UDP协议已经处在传输层,也就是说该层次已经可以完成数据的传输。他们的两者的主要差异如下面表格:

属性 UDP TCP
连接方式 非连接 面向连接
可靠性 不可靠传输 可靠传输
连接对象 一对一、一对多、多对一、多对多 只能一对一
流量拥塞
数据类型 面向报文 面向字节流
适用场景 适用于实时数据传输(视频、电话等) 适用于可靠准确的数据传输(文件、邮件)

  TCP 是面向连接的传输协议,建立连接时要经过三次握手,断开连接时要经过四次握手,中间传输数据时也要回复 ACK 包确认,多种机制保证了数据能够正确到达,不会丢失或出错。
  UDP 是非连接的传输协议,没有建立连接和断开连接的过程,它只是简单地把数据丢到网络中,也不需要 ACK 包确认。
  与 UDP 相比,TCP 有较为复杂的通讯交互和流控制也只能是1对1的方式,但是这保证了数据传输的正确可靠性。而TCP 的速度是无法超越 UDP,两组协议各有千秋,应用层按照自己的需求选择使用他们,发挥他俩各自的优势。

4、IP 、MAC、PORT

(1) IP地址

  IP地址是 Internet Protocol Address 的缩写,译为“网际协议地址”。
  IP协议是为计算机网络相互连接进行通信而设计的协议。它处在TCP/IP 协议栈模型的网络层。在因特网中,它是能使连接到网上的所有计算机网络实现相互通信的一套规则,规定了计算机在因特网上进行通信时应当遵守的规则。任何厂家生产的计算机系统,只要遵守IP协议就可以与因特网互连互通。
  一台计算机可以拥有一个独立的 IP 地址,一个局域网也可以拥有一个独立的 IP 地址(如同只有一台计算机)。对于目前广泛使用 IPv4 地址,它的资源是非常有限的。在因特网上进行通信时,必须要知道对方的 IP 地址。他就好像是对方的名牌号码,如同收件地址一样。实际上数据包中已经附带了 IP 地址,把数据包发送给路由器以后,路由器会根据 IP 地址找到对方的地里位置,完成一次数据的传递。
  IP地址类别分公有地址和私有地址。公有地址(Public address)由Inter NIC(Internet Network Information Center因特网信息中心)负责。这些IP地址分配给注册并向Inter NIC提出申请的组织机构。通过它直接访问因特网。私有地址(Private address)属于非注册地址,专门为组织机构内部使用。
以下列出留用的内部私有地址:
  A类 10.0.0.0–10.255.255.255
  B类 172.16.0.0–172.31.255.255
  C类 192.168.0.0–192.168.255.255
  另外,还有另一类特殊的D类地址也叫组播地址,范围从224.0.0.0到239.255.255.255。
  IP地址通过子网掩码(NetMask)来区分自己所在的网段。

(2) MAC地址

  MAC 地址是 Media Access Control Address 的缩写,直译为“媒体访问控制地址”,也称为局域网地址(LAN Address),以太网地址(Ethernet Address)或物理地址(Physical Address)。它是一个用来确认网络设备位置的位址。 通讯模型中网络层负责IP地址, 据链路层则负责MAC位址 。
MAC地址用于在网络中唯一标示一个网卡,一台设备如果存在多个网卡,则每个网卡都会有一个唯一的MAC地址。网络数据包中除了会附带对方的 IP 地址,ARP协议会将数据包达到局域网以后,路由器/交换机会根据数据包中的 MAC 地址找到对应的计算机,然后把数据包转交给它,这样就完成了数据的传递。IPv4地址为32位二进制数,MAC地址则是为48位二进制数表示,一般用16进制显示。

(3) Port端口号

   IP 地址和 MAC 地址,虽然可以找到目标计算机,但仍然不能进行通信。因为一台计算机可以同时提供多种网络服务,例如 Web 服务(网站)、FTP 服务(文件传输服务)、SMTP 服务(邮箱服务)等。为了区分不同的网络程序,计算机会为每个网络程序分配一个独一无二的端口号(Port Number),例如,Web 服务的端口号是 80,FTP 服务的端口号是 21,SMTP 服务的端口号是 25。
  端口(Port)是一个虚拟的、逻辑上的概念。可以将端口理解为一道门或者一个通道,数据通过的这个门或者通道的不同的编号,就是端口号,程序是可以自己定义的。个IP地址的端口通过16bit进行编号,最多可以有65536个端口 。端口号只有整数,范围是从0 到65535。


二、 UDP上位机的实现

1、 准备

   当我们使用网线连接网络有较好的通信网络时,用C# 来实现UDP通信来做上位机的应用是一个不错的选择,它既能支持多路通讯和也有速度快的优势。
   首先,要安装好C#的编译编辑平台,我这里是使用的VS2019。安装过程不再描述可以自己搜索; 其次,建立好自己的C# .NETframework工程;然后,就可以开始修改添加自己的代码了。
   码代码前,强调!强调!强调! 务必要理解前面一章描述的基本概念,以及按照工程需求建立起自己想要的逻辑实现框架流程,完全不懂可以在网上找一些实现流程和基础代码来解读。我的实现是直接加载Form框架load中,这也正符合我实现的需求。将需要的TCPIP的socket实现或其它功能块写在独立文件的类中,这里注意哟,要包含到同一个空间命名中。

2、 Form主流程实现

上代码 !先描述主Form流程


		//自定义Socket类实例化
        TCPSocket _TcpSocket = new TCPSocket();
        //文件操作FileOperation类实例化
        FileOperation _FileOperation = new FileOperation();
        
        private void Form_Moniter_Load(object sender, EventArgs e)
        {
             if (CheckLocalIpAddressExist() == false)//检查本地IP
            {
                return;
            }                    
                      
            //初始化TCPSocket
            this.InitTcpSocket();
            //设置打开UDP线程
            if (_TcpSocket.UdpOpen() == false)
            {
                // 关闭程序
                return;
            }
            //启动显示时间定时器
            timer_system.Start();
            //初始化控件
            this.InitControlTool();

        }
     	/// <summary>
        /// 初始化设置网络连接类型 
        /// </summary>
        private void InitTcpSocket()
        {
            string GetFileConnectType = _FileOperation._iniConnectType;//获取连接类型
            int GetFileConnectType_int = 0; //连接类型

            if (GetFileConnectType == "") //连接类型为空
            {
                goto TypeErrEND;
            }
            if (!Utils.IsNumeric(GetFileConnectType))//不为数字
            {
                goto TypeErrEND;
            }

            GetFileConnectType_int = Convert.ToInt32(GetFileConnectType);
            
            switch (GetFileConnectType_int )
            {
#if false		//为后续添加代码预留
                case 0:                
                    _TcpSocket._tcpType = TCPType.TCPServer;
                    return;

                case 1:                
                    _TcpSocket._tcpType = TCPType.TCPClient;
                    return;
#endif
                case 2:            
                    _TcpSocket._tcpType = TCPType.UDP;
                    _TcpSocket._localIP = GetIniFileUdpLocalIp();
                    _TcpSocket._localPort = GetIniFileUdpLocalPort();
                    _TcpSocket._targetIP = GetIniFileUdpTargetIp();
                    _TcpSocket._targetPort = GetIniFileUdpTargetPort();
                    return;
                default:
                    break;
            }

      TypeErrEND:
            //读取内容有问题或者编号不存在//有异常都跑到这里来
            _TcpSocket._tcpType = TCPType.UDP;//数字都为默认2
            _FileOperation._iniConnectType = ((int)TCPType.UDP).ToString();//UDP写入文件
            //读取本地IP和端口以及目标IP地址和端口          
            _TcpSocket._localIP = GetIniFileUdpLocalIp();
            _TcpSocket._localPort = GetIniFileUdpLocalPort();
            _TcpSocket._targetIP = GetIniFileUdpTargetIp();
            _TcpSocket._targetPort = GetIniFileUdpTargetPort();

        }
  

   上面的Form_Moniter_Load()窗体加载函数,已经包含了开启UDP开启的整个流程 。 虽然简单,还是给个流程图吧:

Created with Raphaël 2.3.0 Form_Moniter_Load()开始 PC是否有正确的IP地址? 初始化TCPSocket 确设置打开UDP线程? 初始化定时器 初始化控件状态 Form_Moniter_Load()结束 yes no yes no

   其中InitTcpSocket()则是初始化了我们需要的参数,我是从配置文件读取的,也可以采用自己定义的方法,如果没有正确参数,则写入默认值,因此也不需要返回正确错误的结果。接着Open UDP就可以开始了,正确打开后就完成其它的界面初始化的工作了。流程也很简单。不做过多讲解。

3、 Socket类的实现

   Socket的实现的代码我们把它分为几部分函数:参数部分、UDP的打开、数据发送、数据接收以及关闭。这里使用的是套接字中的UdpClient 这个类来实现功能,实测OK。

(1) 参数部分
	    /// <summary>
	    /// TCP枚举类型
	    /// </summary>
	    public enum TCPType
	    {
	        TCPServer,
	        TCPClient,
	        UDP
	    }
	    
		 //======> 自定义委托事件 捕获数据变化   
        public event EventHandler BufferReceChange;     //Socket接收数据事件
        public event EventHandler ConnStateChange;      //Socket连接状态变化事件
        private void OnBufferReceChange(EventArgs eventArgs)
        {
            this.BufferReceChange?.Invoke(this, eventArgs);
        }
        private void OnConnStateChange(EventArgs eventArgs)
        {
            this.ConnStateChange?.Invoke(this, eventArgs);
        }
        #endregion

        #region    ====> UDP 各类参数
        private bool connState = false;     //连接状态
        public bool _connState
        {
            get { return connState; }
            set
            {
                this.OnConnStateChange(new EventArgs());
                connState = value;
            }
        }
        private string targetIp = "255.255.255.255";  //发送数据目标IP
        public string _targetIP
        {
            get { return targetIp; }
            set { targetIp = value; }
        }
        private int targetPort = 8000;      //发送数据目标端口
        public int _targetPort
        {
            get { return targetPort; }
            set { targetPort = value; }
        }
        private string localIP_Single= Utils.GetIPAddressSingle();    //获取本机电脑一个IP 
        public string _localIP_Single
        {
            get { return localIP_Single; }
        }
        private string  localIP_All = Utils.GetIPAddressAll();      //获取本机电脑所有IP 
        public string  _localIP_ALL
        {
            get { return localIP_All; }
        }
        
        private string currentIP;     //当前接收数据源  IP
        public string _currentIP
        {
            get { return currentIP; }
            set { currentIP = value; }
        }
        private int currentPort;    //当前接收数据源端口 Port
        public int _currentPort
        {
            get { return currentPort; }
            set { currentPort = value; }
        }

        private string localIP;//本地IP
        public string _localIP  
        {
            get { return localIP; }
            set { localIP = value; }
        }

        private int localPort;       //本地端口
        public int _localPort
        {
            get { return localPort; }
            set { localPort = value; }
        }

        private byte[] udpRx; //缓存接收到的数据
        public byte[] _udpReceiveData
        {
            get { return udpRx; }
            set { udpRx = value; }
        }
        //网络通信类型 默认TCPServer
        private TCPType tcpType = TCPType.UDP;
        public TCPType _tcpType
        {
            get { return tcpType; }
            set { tcpType = value; }
        }

        Thread UdpReceiveDataThread = null; //udp clienct接受数据线程
        private UdpClient udp = new UdpClient();//实例化udp服务
        private IPEndPoint localIpep = null;//本地IP结构实例
 

  参数部分首先定义了委托的事件,程序指定了数据不为空就跳转到委托函数中运行。为UDP接收数据传递即时消息给到上层使用,就类似于中断消息处理。
  连接状态、TCP类型等等各类参数,通过GET{}和SET{}方法来方便供外部赋值和获取。
  最后,实例化线程和UDP服务。

(2) UDP的数据打开、接收、发送和关闭
        //UPD通信 ======> 打开udp目标主机 、接受数据线程、 关闭UDP
        /// <summary>
        /// 打开UDP通信
        /// </summary>
        /// <returns>true or false</returns>
        public bool UdpOpen()
        {
            try
            {
                UdpCLose();//关闭udp
                localIpep = new IPEndPoint(IPAddress.Parse(localIP), localPort); // 本机IP和监听端口号
                udp = new UdpClient(localIpep);//绑定本地UDP端口,localPort=0为自动分配
                              
                //创建UDP接收线程
                UdpReceiveDataThread = new Thread(new ThreadStart(UdpRxThread));//指定线程函数
                UdpReceiveDataThread.IsBackground = true;//可后台运行
                UdpReceiveDataThread.Name = "UDP接收线程";//线程名
                UdpReceiveDataThread.Start();//线程开始
                connState = true;//更新连接状态
                return true;
            }
            catch (Exception ex)            
            {
                
                _connState = false;
                if (MessageBox.Show("连接失败:\n Msg:" + ex.Message + "\nSource:" + ex.Source + "\n[OK]继续尝试,或[Cancel]退出程序?", "异常提示/询问",
                                    MessageBoxButtons.OKCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) == DialogResult.OK)
                {
                    UdpOpen();
                }
                return false;
            }
            finally
            {
                this.OnConnStateChange(new EventArgs());
            }
        }

        /// <summary>
        /// UDP发送数据
        /// </summary>
        /// <param name="buffer"></param>
        /// <returns></returns>
        public bool UdpSend(byte[] buffer)
        {
            try
            {
                //向目标IP发数据
                udp.Send(buffer, buffer.Length, _targetIP, _targetPort);
                return true;
            }
            catch (Exception ex)
            {
                _connState = false;
                ExceptionContent = ex.Message + "\n" + ex.ToString();
                this.OnConnStateChange(new EventArgs());
                return false;
            }
        }
        //UDP接收数据线程
        public void UdpRxThread()
        {  
            
            try
            {
                 while (true)
                {

                    byte[] receiveBytes = udp.Receive(ref localIpep);//自动获取接收数据信息//获取数据则跳出

                    //记录当前当前接收数据源数据
                    currentIP = localIpep.Address.ToString();
                    currentPort = localIpep.Port;
                    
                    udpReceiveData = new byte[receiveBytes.Length];//初始化长度
                    Buffer.BlockCopy(receiveBytes, 0, _udpReceiveData, 0, receiveBytes.Length);//拷贝数据

                    this.OnBufferReceChange(new EventArgs());//数据改变事件
                   
                }
            }
            catch (Exception ex)
            {                
                _connState = false;
                ExceptionContent = ex.Message + "\n" + ex.ToString();
                this.OnConnStateChange(new EventArgs());
            }
        }


        /// <summary>
        /// 关闭udp
        /// </summary>
        private void UdpCLose()
        {
            try
            {
                udp.Close();                
            }
            catch (Exception ex)
            {
                ExceptionContent = ex.Message + "\n" + ex.ToString();
                //异常不做处理
                return;
            }
        }

  打开UdpOpen方法是这里最为关键的一环,首先要知道UDP也是有server(服务器)端和client(客户)端,而上位机这端一般作为的是client端(服务端也是能实现的),客户端是首先发起数据的一方。Open之后,客户端将持续监听自己的IP和端口。即这里IP地址是指定自己监听服务器将接收数据地址和端口,一般在服务器没有指定发送端口的时候,指定端口置0,也就是随机分配是没有问题的。 因为务器端的一般操作都会绑定(bind)客户端发送过来的数据中获取其中的IP和端口。但如果服务器指定了端口,那我们open的端口就必须是对方服务器指定的IP和端口的。这个要看实际的工程情况而定。因此,如代码所描述在线程开始前,localIpep 指定了自己的IP以及端口。
  需要注意几点:
1、网线连接状态或者UDP服务是否被占用的问题异常处理;
2、发送接收单独建立socket类时候,要避免端口冲突;
3、及时要更新自己的数据连接状态。如:接收回应或心跳数据等。
  发送数据UdpSend()就简单,将udp.Send(buffer, buffer.Length, _targetIP, _targetPort)函数的几个参数代入就能实现将buffer中的数据发送目标地址去。
  接收的线程方法UdpRxThread最关键的就是udp.Receive(ref localIpep),这个函数将监听接收到的数据,否者将会一直等待,有数据时,将其拷贝出来即可,可以通过委托消息传递给上层显示。
  退出UdpCLose()功能就是关闭Open状态,注意要及时关闭线程,避免反复开线程。


三、TCP Client上位机实现

1、 准备

  如果我们要使用可靠的不允许有任何错误的通信时,我们就需要用到TCP连接通讯了。通过多次握手建立可靠的连接,HTTP、 FTP、 POP、 SSH就是基于这个来实现的,我们可以用C# 来实现TCP通信来做客户端上位机。
  作为客户端的可靠连接,服务器端是首先要开启服务等待客户端连接的,对于客户端这条socket链路是唯一对应的。对于服务器端则可以建立多个客户端socket连接。

2、 TCP的连接、发送、接收和关闭实现

 &emspForm主流程参数和UDP没有区别,调用实现原理也一样,我就不再阐述。 接下来就是TCP client端的连接、发送、接收和关闭的实现代码,大家可以参考。为了说清楚,我认为我注释得还比较详尽。

		
        Thread TCPReceiveDataThread = null; //tcp clienct接收数据线程    
        public Socket _targetSocket = null; //连接目标套接字

        public byte[] tcpRxBuf; //的使用接收到的数据 数据长度不确定
        private const int MAX_RX_LEN = 512;
        private byte[] _tdpReceiveData = new byte[MAX_RX_LEN]; //TCP clienct缓存接收到的直接数据,必须固定大小

			/// <summary>
            /// 连接服务器
            /// </summary>
            /// <returns>TRUE or FALSE</returns>
            private bool ConnServer()
            {
                try
                {
                 //InterNetwork: 地址簇IPV4
                //Stream:套接字类型支持可靠、 双向、 基于连接的字节流
                //Tcp: 指定通讯协议类型为传输控制协议
                _targetSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//建立套接字
                //IP地址转换格式
                IPAddress address = IPAddress.Parse(_targetIP);
                //IP地址端口绑定网络节点
                IPEndPoint point = new IPEndPoint(address, _targetPort);

                //建立与远程主机的连接,失败则进入异常
                 _targetSocket.Connect(point);
                //连接后获取本地IP和端口
                localIpAndPort = _targetSocket.LocalEndPoint.ToString();
                //切割出IP
                localIP = localIpAndPort.Split(':')[0];
                 //正常连接,建线程
                TCPReceiveDataThread = new Thread(new ThreadStart(TcpClientReceiveDataThread));
                //后台运行
                TCPReceiveDataThread.IsBackground = true;
                //线程名
                TCPReceiveDataThread.Name = "Tcp客户端接收数据";
                //线程开始
                TCPReceiveDataThread.Start();
                //置状态
                connState = true;
                return true;
                }
                catch (Exception ex)
                {   
                    //异常返回
                    _connState = false;
                    MessageBox.Show(ex.Message, "异常");
                    return false;
                }
                finally
                {
                    //委托显示
                    this.OnConnStateChange(new EventArgs());
                }
            }
             
            /// <summary>
            /// tcp client接收数据线程
            /// </summary>
            private void TcpClientReceiveDataThread()
            {
                while (true)
                {
                    try
                    {                        
	                    //接收数据,并获取长度  
	                    int length = _targetSocket.Receive(_tdpReceiveData);
	                    if (length == 0)
	                    {
	                        _connState = false;
	                        return;
	                    }
	                    //实际使用BUF
	                    tcpRxBuf = new byte[length];
	                    Array.Copy(_tdpReceiveData,  tcpRxBuf, length);	                    
	                    //委托显示
	                    this.OnBufferReceChange(new EventArgs());    
                    }
                    catch (Exception ex)
                    {
                           _connState = false;
                    }
                  
                }
            }
             
            /// <summary>
            /// tcp client 发送数据
            /// </summary>
            /// <param name="buffer"> 发送的数据</param>
            /// <returns>TRUE or FALSE</returns>
            private bool tcpClientSendData(byte[] buffer)
            {
                try
                {
                    _targetSocket.Send(buffer);//数据发送
                    return true;
                }
                catch (Exception ex)
                {
                    _connState = false;
                    this.OnConnStateChange(new EventArgs());//委托显示
                    return false;
                }
            }

			/// <summary>
            /// 关闭TCP CLient
            /// </summary>
            private void closeTcpClent()
            {
                try
                {
                     //关闭线程                   
                    ReceiveData.Abort();              
                    //关闭socket类实例
                    _targetSocket.Close(); 
                }
                catch (Exception ex)
                {
                    string err = ex.ToString();
                }
                finally 
                {
                    connState = false;
                }
            }

  TCP的client客户端相对服务端要简单,将地址簇类型、套接字类型、传输协议类型以及对方服务器的IP和端口绑定就可以连接服务器了。一定要注意这里绑定的是服务器端的地址端口。而接收和发送线程甚至比UDP还要简单。需要注意的是TCP接收方法缓存空间_tdpReceiveData 必须大于接收数据最大长度,上面描述的代码定义的是512字节。通过数据返回的长度length来控制接收copy到实际使用的tcpRxBuf 中。读取后才会清空buf。 接收的数据可以通过事件委托给上层调用,也可以通过独立线程来解析或显示处理。
  关于连接状态,可以自己建立连接状态的值,当然也可以简单地直接使用socket中的状态connState。

四、小结

  最后,小结一下。
  及结果反复学习和验证,完成了我想要的效果,问过有兴趣可以自己做一个DOME程序,自己研究,也可以用其它的网络通讯工具互相收发数据,条件受限同一台电脑上也可以实现。
  无论是那种网络通讯方式,没有最好,只有最适合。希望我今天的收获可以成为今后的学习的坚实基础。
  这里show一下我的DEMO程序界面吧,就此结束,如需要补充的,随时更新。
  
在这里插入图片描述

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

C# TCP/IP网络数据传输及实现 的相关文章

随机推荐

  • LC两数之和 JS

    LC两数之和 JS 给定一个整数数组 nums 和一个整数目标值 target 请你在该数组中找出 和为目标值 target 的那 两个 整数 并返回它们的数组下标 你可以假设每种输入只会对应一个答案 但是 数组中同一个元素在答案里不能重复
  • 基于fpga的车牌识别系统(已上板实现,完整系统)

    基于fpga的车牌识别系统 已上板实现 完整系统 modelsim仿真代码 可以上板验证 有完整文档 开发板 正点原子达芬奇Artix 7 FPGA开发板A7 Xilinx XC7A35T 1 设计概要 针对车牌识别项目 车牌定位的准确与否
  • C# 清空数组Array.Clear

    using System using System Collections using System Collections Generic using UnityEngine public class ClearArrayText Mon
  • 【H5】 移动端的基本事件

    H5 移动端的基本事件 一 基础事件 1 PC端事件 onclick 鼠标点击触发 onmousedown 鼠标按下触发 onmousemove 鼠标移动触发 onmouseup 鼠标抬起触发 2 移动端触屏事件 ontouchstart
  • hcip脱产班01天

    桌面云 半条命 CS 射击类游戏 CF穿越火线 2M 4M土豪开网吧 电子游戏厅 网吧 方正主机 纯平大屁股显示器 液晶薄酷睿i7 i9 组装电脑 性价比高 奔腾4处理器央视做广告 主机箱 主板 内存 电源风扇 cpu等等物理硬件 有没有可
  • python使用PyInstaller库打包exe

    前言 python制作了一个小程序 想打包成exe怎么办 这篇博客 教你打包python制作的小程序 下载PyInstaller 首先 我们先按住电脑键盘上的window键 然后按R键 记住要同时按住喔 会弹出以下框 然后我们输入cmd 点
  • 关于quicktime

    前两天一直在做一个关于生化实验的教学视频 用premiere做的 本来拍摄的视频素材是mov格式的 这种格式的文件比较大 而且不能导入premiere的 只有转换成mpg格式的 本来的素材是640 480 但转换格式后就变成384 288
  • Swiper轮播图在Vue框架中的运用

    要在Vue中运用Swiper需要下载Swiper插件 在命令行cmd 可以cd 根目录 输入 npm install vue awesome swiper save 下载成功后 查看package json中是否存在 在mian js中全局
  • dll调用nodejs的回调函数

    nodejs使用ffi调用dll dll中有回调函数调用js中的方法 c语言中cdll h文件 extern C typedef void JsCall int index 这个就是要传入的类型结构 extern declspec dlli
  • 监控项目里的流媒体服务器,监控项目里的流媒体服务器

    监控项目里的流媒体服务器 内容精选 换一换 共享型和独享型负载均衡算法 支持以下几种调度算法 加权轮询算法 根据后端服务器的权重 按顺序依次将请求分发给不同的服务器 它用相应的权重表示服务器的处理性能 按照权重的高低以及轮询方式将请求分配给
  • QT设置ToolButton按钮的样式

    QToolButton min width 80px min height 32px QToolButton color rgb 255 255 255 min height 20 border style solid border top
  • 巴比特

    摘要 据 科创板日报 7 月 11 日报道 北京市经济和信息化局党组书记 局长姜广智在接受记者采访时表示 北京经信局将在算力供给层面提升中长期算力供给能力 加快建设海淀区北京人工智能公共算力 朝阳区北京数字经济算力中心等重点项目 尽快形成算
  • Pandas-连接合并函数merge()

    一 merge函数用途 pandas中的merge 函数类似于SQL中join的用法 可以将不同数据集依照某些字段 属性 进行合并操作 得到一个新的数据集 二 merge 函数的具体参数 用法 DataFrame1 merge DataFr
  • C++_面向对象_1

    设计一个圆形类 Circle 和一个点类 Point 计算点和圆的关系 class Circle public int x int y int radius class Point public int x int y void judge
  • SS626V100 SDK安装编译osdrv问题汇总

    文章目录 前言 1 开发环境 2 在 linux 服务器上安装交叉工具链 2 1 安装 aarch64 mix410 linux tgz 2 2 安装 cc riscv32 cfg11 musl 20211008 elf tar gz 2
  • react,umi,antd-pro的layout封装过程

    import React from react import Layout Form Icon from antd import isEqual from lodash isEqual 深度比较对象 import memoizeOne fr
  • TIOBE 8 月编程语言:C、Java 差距拉大,R 语言盛行

    编程语言社区 TIOBE 最新发布了 8 月编程语言排行榜 相比上个月 本月 TIOBE 指数整个体变化并不大 C 语言依然保持强劲地增长势头 与第二名 Java 之间差距逐月增大 从上个月相差 1 35 的份额逐步增长到 2 55 的差额
  • 数据分析学习之路——(八)分类算法介绍

    前面几篇文章都是从数据分析介绍讲到描述统计分析 其实数据分析还需要使用机器学习的相关知识用来建立不同的分析模型 最终对数据信息进行深入的分析和挖掘 在实际工作当中 我们需要对数据进行特征分析 并且从数据中获取有价值的信息 并且为数据产品的市
  • 时不我待,拥抱趋势,开源IM项目OpenIM技术简介

    坚持开源 开源的理念是基于共享 合作和透明的原则 将软件 代码等知识资源公开并允许他人使用 修改和重新分发 以促进创新和发展 以下是几个开源的优点 创新 开源可以促进创新 通过让其他人改进或扩展已有的代码或项目 不断推动技术的进步 透明 开
  • C# TCP/IP网络数据传输及实现

    C TCP IP网络数据传输及实现 一 概念简述 1 什么是OSI 和TCP IP 2 什么是套接字Socket 3 TCP 和 UDP 4 IP MAC PORT 1 IP地址 2 MAC地址 3 Port端口号 二 UDP上位机的实现