C#网络编程整理

本文最后更新于:2021年9月29日 下午

引言

不好意思各位,最近公司项目比较忙,鸽了好久的文章,现在重新拾起来。

我个人比较愿意将工作过程中学习的知识,问题的解决思路,通用的处理方式等做一个总结,方便自己,也照亮他人。为什么是工作过程中的,因为只有真实的场景下碰到的问题才是有价值的内容,我愿意分享,但是我也做不到像某些大佬一样熬夜肝文章,本着身体是第一位的原则,适当做一些总结是我现在做事的思路。

项目叙述

这次要讲的不是什么复杂的项目,是一个基于WebSocket的通信服务程序。

在做这个项目前,我其实没有接触过通信服务程序这一块内容,之前主要都是写的WebApi和后台管理系统接口。当然我也知道Tcp是面向连接的可靠的,Udp是不可靠的这些基于知识,但在实际的应用场景下使用这还是第一次,可能有些人觉得这种简单的东西有什么好讲的,那这样你可以选择关闭网页,合适的东西应该给合适的人看,这是我的观点。

开发语言c#,开发一个基于WebSocket的通信程序,客户端按照规定的报文格式发送,服务端可以正常解析。

报文格式如下

名称 帧头 数据长度 CRC 校验 数据段
长度(字节) 2 2 2 N
说明 TT

所有数据帧, 皆为高字节在前模式, 即小端模式。
CRC 的校验范围为: 从包头至包尾巴。 校验初始, CRC 校验位置要先清零。
CRC 校验方式:
初始校验值为 0xAA, 所有字节依次累加, 最后求和

项目技术预演

没有先例如何开发一个稳定的Tcp通信服务?

在开发本任务前我并没有做通信服务的经验,为了最快成本的减少踩坑和开发顺畅,我选择了使用现成的框架。在综合后我选择了RRMQSocket

日月之行,若出其中;星汉灿烂,若出其里。

RRQMSocket是一个整合性的、超轻量级的网络通信服务框架。它具有 高并发连接高并发处理事件订阅插件式扩展多线程处理内存池对象池 等特点,让使用者能够更加简单的、快速的搭建网络框架。

选择他的理由其实比较简单,开源【gitee 推荐项目】、有文档、持续维护。

我通过作者提供的文档首先对通信服务有了一个基本了解,然后使用框架编写一个最基本案例:一个简单的tcp通信服务,增加自己对框架的了解。

到此,对框架的工作暂时高一段落。

什么是小端模式

在计算机中一般讲字节序分为两类:Big-Endian(大端字节序) 和 Little-Endian(小端字节序)
a) Little-Endian 高位字节在前,低位字节在后。
b) Big-Endian 低位字节在前,高位字节在后。

网络字节序:TCP/IP各层协议将字节序定义为Big-Endian,因此TCP/IP协议中使用的字节序通常称之为网络字节序。
举个小例子:
整数127(十进制)在计算机(64位)中大/小端字节序

img

c#中对字节序的处理

首先默认的 C# 使用的是小端序。直接上案例,大家可以按照上图进行分析。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;

namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
byte[] b = { 1, 2, 3, 4 };
Console.WriteLine(bytes2string(b));

//x1是小端模式值:x1=67305985=0x 04 03 02 01
int x1 = BitConverter.ToInt32(b, 0);
Console.WriteLine(x1.ToString("X2"));

//x2是大端模式值:x2=16909060=0x 01 02 03 04
int x2 = IPAddress.NetworkToHostOrder(x1);

Console.WriteLine(x2.ToString("X2"));
}
}
}

如何进行大端序和小端序的转换?

在上面的案例中细心的服友肯定已经发现了处理方式

推荐方式

1
2
System.Net.IPAddress.HostToNetworkOrder(本机到网络转换)
System.Net.IPAddress.NetworkToHostOrder(网络字节转成本机)

其他方式

字节数组reverse反转,即得到相反的字节序

1
byte[].Reverse().ToArray();

代码示例

short表示短整形,short 6 占2个字节,内存表现为 06 00 值=06

使用HostToNetworkOrder将x转换为大端字节表现形式,b=1536 ,内存表现形式00 06

最调用`BitConverter.GetBytes’将大端字节数1536转为大端字节数组表现形式 00 06

1
2
3
4
short x = 6;
byte[] aa = System.BitConverter.GetBytes(x); //默认的小端字节序字节数组。
short b = System.Net.IPAddress.HostToNetworkOrder(x); //把x转成相应的大端字节数
byte[] bb = System.BitConverter.GetBytes(b); //这样直接取到的就是大端字节序字节数组。

image-20210511154830146

对于字符串型:使用 System.Text.Encoding.Default.GetBytes();

直接取字串对应字节数组。

CRC校验是什么,有什么作用

crc定义

CRC即循环冗余校验码:是数据通信领域中最常用的一种查错校验码,其特征是信息字段和校验字段的长度可以任意选定。循环冗余检查(CRC)是一种数据传输检错功能,对数据进行多项式计算,并将得到的结果附在帧的后面,接收设备也执行类似的算法,以保证数据传输的正确性和完整性。

crc校验作用

在信息的传输过程中,有时会发生误码。

例如,传送1001,接收到1000,这就产生了误码,但接收方并不知道产生了误码。

而当发送方与接收方使用同一标准的CRC校验,就能够判断在信息传输的过程中是否发生了误码。

进制处理相关

c# 在进行进制处理时有一些常用的方法,记录下来方便自己后期回顾

单个10进制转为16进制字符串
1
2
3
4
5
6
X.ToString("X2")即转化为大写的16进制。
X.ToString("x2")即转化为小写的16进制。
2表示输出两位,不足2位的前面补0,如 0x0A 如果没有2,就只会输出0xA

Console.WriteLine(10.ToString("X")); //输出A
Console.WriteLine(10.ToString("X2"));//输出0A

借着这个机会我又重温了一下 string.Format函数

1
2
Console.WriteLine(string.Format("{0:X}", 12)); //输出A
Console.WriteLine(string.Format("{0:X2}", 12));//输出OA
字节数组转为字符串,其实就是一个循环
public static string BytesTostring(byte[] bytes)
{
    string hexString = string.Empty;
    if (bytes != null)
    {
        StringBuilder strB = new StringBuilder();

        for (int i = 0; i < bytes.Length; i++)
        {
        strB.Append(bytes[i].ToString("X2"));
        }
        hexString = strB.ToString();
        }
    return hexString;
}
官方库自带的字节处理帮助类

using namespace System

BitConverter.方法

参考

C# 大端与小端

C#中string.format用法详解