网络流、网络数据的编解码(C#---网络编程)
一、网络流
当通过网络传输数据或对文件数据进行操作时,需要将数据转化为数据流的形式。数据流(Stream)是对串行传输的数据(以字节为单位)的一种抽象表示,数据源可以是文件、外部设备、主存、网络套接字等。数据流分为文件流、内存流和网络流3种类型。其中,网络流用于在网络上传输数据。在使用网络流时,数据在网络的各个位置之间以连续的字节形式传输。C#在System.Net.Sockets命名空间中提供了NetworkStream类用于收发网络数据。
NetworkStream类只支持面向连接的套接字,对NetworkStream流,写入操作是从源端内存缓冲区到网络上的数据传输,读取操作是从网络上到目的端内存缓冲区的数据传输。
1、获取NetworkStream实例
在构造一个NetworkStream实例后,就可以用它来收发网络数据。
(1)利用TcpClient获取网络流对象
TcpClient tcpClient=new TcpClient(); tcpClient.Connect("www.fzu.edu.cn",5188); NetworkStream myNetworkStream=tcpClient.GetStream();
(2)利用Socket获取网络流对象
NetworkStream myNetworkStream=new NetworkStream(mySocket); //myScoket为获取的Socket对象
(3)利用NetworkStream实例收发数据
Write方法负责将字节数组从进程缓冲区发送到本机的TCP缓冲区,然后TCP/IP协议栈通过网络适配器将数据真正发送到网络上,最终到达接收方的TCP接收缓冲区。
由于Write方法为同步方法,所以在发送成功或者返回异常前都处于阻塞状态,直到发送成功或者返回异常。
示例1.使用NetworkStream发送数据
if(myNetworkStream.Canwrite) { byte[] myWriteBuffer=Encoding.ASCII.GetBytes("Are you receiving this message?"); myNetworkStream.Write(myWriteBuffer,0,myWriteBuffer.Length); } else Console.WriteLine("Sorry.You cannot write to this NetworkStream.");
示例2.使用NetworkStream读取数据
if(myNetworkStream.CanRead) { byte[] myReadBuffer=new byte[1024]; String myCompleteMessage=""; int numberOfBytesRead=0; //准备接收的信息有可能大于1024,所以用循环 do { numberOfBytesRead=myNetworkStream.Read(myReadBuffer,0,myReadBuffer.Length); myCompleteMessage=String.Concat(myCompleteMessage,Encoding.ASCII.GetString(myReadBuffer,0,numberOfBytesRead)); }while(myNetworkStream.DataAvailable); }
二、网络数据的编码与解码
1、常用字符编码方式
(1)ASCII字符集
(2)非ASCII字符集:我国常用的标准GB2312和GB18030-2000编码。
(3)Unicode字符集
C#的默认字符都是Unicode码,一个英文和一个汉字一样,都占2个字节。
- UTF-8:是一种针对Unicode的可变长度字符编码。UTF-8用1到4个字节编码Unicode字符
- UTF-16
- UTF-32
(1)Encoding类
Encoding类位于System.Text命名空间中,主要用于在不同的编码和Unicode之间进行转换。
利用Convert方法将字节数组从一种编码转换为另一种编码,其方法原型:
public static byte[] Convert(Encoding srcEncoding,Encoding dstEncoding,byte[] bytes);
返回值:包含转换结果的byte类型的数组。
参数:
- srcEncoding:表示源编码格式。
- dstEncoding:表示目标编码格式。
- bytes:为待转换的字节数组。
在将Unicode字符串转换为UTF-8字符串的步骤:
- (1)利用Encoding的UTF-8和Unicode属性获取UTF-8格式的编码实例utf8和Unicode编码实例unicode,如:
string unicodeString="unicode字符串pi(\u03a0)";
Encoding unicode=Encoding.Unicode;
Encoding utf8=Encoding.UTF8;
- (2)利用unicode实例的GetBytes方法将Unicode字符编码为Unicode字节数组:
byte[] unicodeBytes=unicode.GetBytes(unicodeString);
- (3)利用Encoding的Convert方法将Unicode字节数组转换为UTF-8字符串:
byte[] utf8Bytes=Encoding.Convert(Encoding.Unicode,Encoding.UTF8,unicodeBytes);
- (4)利用实例utf8的GetString方法将UTF-8字节数组解码为UTF-8字符串:
string utf8String=utf8.GetString(utf8Bytes);
(2)Encoder类和Decoder类
在网络传输和文件操作中,如果数据量比较大,需要将其划分为较小的块,对于跨块传输的情况直接使用Encoding类的GetBytes方法编程比较麻烦,而Encoder和Decoder由于维护了数据块的结尾信息,可以轻松实现。
Encoder可以将一组字符串转换为一个字节序列。
Decoder则将已编码的字节序列解码为字符序列。
Encoder编码步骤:
(1)获取Encoder实例。由于Encoder的构造函数为protected,不能直接创建该类的实例,必须通过Encoding提供的GetEncoder方法创建实例:
//获取ASCII编码的Encoder实例 Encoder ASCiiEncoder=Encoding.ASCII.GetEncoder();
//获取Unicode编码的Encoder实例 Encoder unicodeEncoder=Encoding.Unicode.GetEncoder();
(2)GetBytes方法。获取Encoder实例后,利用它的GetBytes方法将一字符编码转换为字节序列:
方法原型:
public virtual int GetBytes( char [] chars, //要编码的字符数组 char Index, //第一个要编码的字符索引 int charCount, //要编码的字符的数目 byte[] bytes, //存储编码后的字节序列 int byteIndex, //开始写入所产生的字节序列的索引位置 bool flush //是否在转换后清除编码器的内部状态 );
(3)GetByteCount方法。该方法计算对字符序列进行编码后所产生的精确字节数,以确定GetBytes方法中byte类型数组实例的长度。
方法原型
public abstract int GetByteCount( char[] chars, //要编码的字符集的字符数组 int index, //第一个要编码的字符索引 int count, //要编码的字符的数目 bool flush //是否在转换后清除编码器的内部状态 );
Decoder解码步骤:
首先通过Encoding的GetDecoder方法创建Decoder实例,然后用实例的GetChars方法将字节序列解码为一组字符。
GetChars方法用于将一个字节序列解码为一组字符,并从指定的索引位置开始存储这组字符。
方法原型:
public abstract int GetChars( byte[] bytes, //要解码的字符序列的字符数组 int byteIndex, //第一个要解码的字节的索引 int byteCount, //要解码的字符的数目 char[] chars, //包含所产生的字符集的字符数组 int charIndex //开始写入所产生的字符集的字节数组的索引位置 );
该方法返回值:chars写入的实际字符数。
示例.利用Encoder类和Decoder类实现编码和解码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Encoder_Decoder { class Program { static void Main(string[] args) { //Encoder string test = "ABCDE1234测试"; Console.WriteLine("The test of string is:{0}", test); Encoding encoding = Encoding.UTF8; char[] source = test.ToCharArray(); int strLength = test.Length; int len = encoding.GetEncoder().GetByteCount(source, 0, strLength, false); byte[] result = new byte[len]; encoding.GetEncoder().GetBytes(source, 0, strLength, result, 0, false); Console.WriteLine("After Encoder,the byte of test is output below."); foreach (byte b in result) { Console.Write("{0:X}-", b); } Console.WriteLine();
//Decoder Console.Write("After Decoder,the string is"); int deslen = encoding.GetDecoder().GetCharCount(result, 0, result.Length); char[] des = new char[deslen]; encoding.GetDecoder().GetChars(result, 0, result.Length, des, 0); foreach (char c in des) { Console.Write("{0}", c); } Console.WriteLine("\n"); } } }
运行结果