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

  • 一、概念简述
    • 1、什么是OSI 和TCP/IP
    • 2、什么是套接字Socket
    • 3、TCP 和 UDP
    • 4、IP 、MAC、PORT
      • (1) IP地址
      • (2) MAC地址
      • (3) Port端口号
  • 二、 UDP上位机的实现
    • 1、 准备
    • 2、 Form主流程实现
    • 3、 Socket类的实现
      • (1) 参数部分
      • (2) UDP的数据打开、接收、发送和关闭
  • 三、TCP Client上位机实现
    • 1、 准备
    • 2、 TCP的连接、发送、接收和关闭实现
  • 四、小结

一、概念简述

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;}                    //初始化TCPSocketthis.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;
#endifcase 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.0Form_Moniter_Load()开始PC是否有正确的IP地址?初始化TCPSocket确设置打开UDP线程?初始化定时器初始化控件状态Form_Moniter_Load()结束yesnoyesno

   其中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";  //发送数据目标IPpublic 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;     //当前接收数据源  IPpublic string _currentIP{get { return currentIP; }set { currentIP = value; }}private int currentPort;    //当前接收数据源端口 Portpublic int _currentPort{get { return currentPort; }set { currentPort = value; }}private string localIP;//本地IPpublic 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; }}//网络通信类型 默认TCPServerprivate 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();//关闭udplocalIpep = 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();//切割出IPlocalIP = 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;}//实际使用BUFtcpRxBuf = 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程序界面吧,就此结束,如需要补充的,随时更新。
  

C# TCP/IP网络数据传输及实现相关推荐

  1. TCP/IP网络编程之基于TCP的服务端/客户端(二)

    回声客户端问题 上一章TCP/IP网络编程之基于TCP的服务端/客户端(一)中,我们解释了回声客户端所存在的问题,那么单单是客户端的问题,服务端没有任何问题?是的,服务端没有问题,现在先让我们回顾下服 ...

  2. TCP/IP网络编程(3)

    基于DUP的服务端与客户端 在TCP/IP网络编程(2)中,介绍了TCP/IP的四层模型,传输层分为TCP和UDP两种方式,通过TCP套接字完成数据交换已经进行了介绍,下面介绍通过UDP套接字完成数据 ...

  3. TCP/IP网络编程(1)

    1. 套接字 套接字是由操作系统提供的网络数据通信软件设备,即使对网络数据传输原理不了解,也能够使用套接字完成网络数据传输.为了与远程计算机进行数据传输,需要连接到英特网,套接字就是进行网络连接的工具 ...

  4. TCP/IP网络编程 - 基础学习

    1. 创建Socket #include<sys/types.h> #include<sys/socket.h>int sock = ::socket(PF_INET, SOC ...

  5. TCP/IP网络编程之基于TCP的服务端/客户端(一)

    TCP/IP网络编程之基于TCP的服务端/客户端(一) 理解TCP和UDP 根据数据传输方式的不同,基于网络协议的套接字一般分为TCP套接字和UDP套接字.因为TCP套接字是面向连接的,因此又称为基于 ...

  6. TCP/IP网络编程之多进程服务端(二)

    TCP/IP网络编程之多进程服务端(二) 信号处理 本章接上一章TCP/IP网络编程之多进程服务端(一),在上一章中,我们介绍了进程的创建和销毁,以及如何销毁僵尸进程.前面我们讲过,waitpid是非 ...

  7. TCP/IP网络通信协议

    概述 TCP/IP 是因特网的通信协议. TCP/IP 通信协议是对计算机必须遵守的规则的描述,只有遵守这些规则,计算机之间才能进行通信. 您的浏览器与服务器都在使用 TCP/IP 协议 浏览器与服务 ...

  8. 通过简单的演示理解TCP/IP网络

    文章目录 1.MAC地址 2.IP地址 2.1.IP地址存在的意义 2.2 子网掩码(Subnet Mask) 2.3DHCP(动态主机设置协议)服务器 2.4路由器是数据传输的指路人 2.4.1 路 ...

  9. TCP/IP网络简介(来自与51CTO学院视频授课内容)

    多线程编程 TCP/IP网络简介 TCP/IP参考模型是计算机网络的始祖,他首先提出了分层的概念.它一共分为: 应用层:应用层协议建立在网络层协议之上,专门用于为用户提供应用服务,一般是可见的. 传输 ...

最新文章

  1. 唐山松下焊接机器人编程招聘_机器人四小家族-松下Panasonic专注焊接
  2. R语言:计算各种距离
  3. 获取打印机分辨率_喵喵机P2S热敏打印机,升级屏幕带来了哪些体验??
  4. dev c++ 代码补全_zsh配置与代码自动补全+tmux配置
  5. android卫星菜单中间,Android卫星菜单:android-satellite-menu
  6. 《HTML5 2D游戏编程核心技术》——第2章,第2.3节使用CSS背景
  7. 因涉嫌信披违规 神州优车被证监会立案调查
  8. WavePad如何在音乐文件上录制声音
  9. vc2013控件第一个程序
  10. 泰迪杯特等奖思路(教育平台线上课程用户行为分析(含数据可视化处理))-思路篇
  11. php清除手机浏览器缓存,js清除浏览器缓存的几种方法
  12. 一个关于IT界牛人的故事,估计是杜撰的。
  13. 茄子快传和腾讯全民Wifi配合使用
  14. Android系统升级
  15. 困扰的Byte、bit 、和16进制之间的关系
  16. 动态网站的搜索引擎优化策略(转)
  17. fiddler抓苹果手机上app包的方法,解决https连接只抓到Tunnel to的问题
  18. jQuery 使用slideToggle()滑动显示隐藏
  19. 贝加莱触摸屏维修4PP065.0571-X74F
  20. 多商户商城系统功能拆解42讲-平台端应用-客服列表

热门文章

  1. 群晖NAS配置DNSPodDDNS实现外网访问(仅适用于阿里云域名)
  2. 好嗨呦是谁_抖音好嗨哟原版 《好嗨哟》原唱是谁怎么火起来的
  3. 第9章第24节:制作商业计划书的盈利模式幻灯片 [PowerPoint精美幻灯片实战教程]
  4. 小学计算机神奇的因特网教案,小学信息技术第三册全册教案(第一单元 神奇的动画城)...
  5. Libuv的安装及运行使用
  6. Node.js—Express、Express 路由 、Express 中间件、使用 Express 写接口
  7. 2017国庆 济南清北学堂 8天乐
  8. 【智能制造】MES在工业4.0标准下的规范和功能
  9. 长春有学计算机的中专吗,长春比较好的中专学校
  10. 百度有啊前端js框架分析(一)