网络传输协议学习记录

28 阅读2分钟

我认为学习应该【基于问题+知识】的方式去学习,也就是说我应该先去发现问题,然后再去寻找解决方法,然后再去学习知识,比如下面的这个网络传输协议的学习过程。

在学习http协议的时候,我真的看不懂这个协议为什么如此的复杂,协议头里为什么要写那么多的内容:

GET / HTTP/1.1`

`Host: hackr.jp`

`User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:13.0) Gecko/20100101 Firefox/13.0`

`Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*; q=0.8`

`Accept-Language: ja,en-us;q=0.7,en;q=0.3`

`Accept-Encoding: gzip, deflate DNT: 1`

`Connection: keep-alive`

`If-Modified-Since: Fri, 31 Aug 2007 02:02:20 GMT`

`If-None-Match: "45bae1-16a-46d776ac"`

`Cache-Control: max-age=0`

我不清楚为什么要添加这些内容。

于是,我就不管了,我先写了一个简单的服务器与客户端之间传输的代码:

服务器的代码如下:

using System.Net;
using System.Net.Sockets;
using System.Text;


TcpListener server = new TcpListener(IPAddress.Any, 8000);

server.Start();
Console.WriteLine("服务器已经启动");


while (true) {
    TcpClient client = await server.AcceptTcpClientAsync();
    _=Task.Run(() => 处理客户端请求(client));
}

async Task 处理客户端请求(TcpClient client) {    
    NetworkStream stream = client.GetStream();
    byte[] buffer = new byte[1024];
    int messageLength = 0;
    Readstate currenState = Readstate.ReadingLength;
    while (true) {
        if (true) {
            switch (currenState) {
                case Readstate.ReadingLength:
                    stream.Read(buffer, 0, 4);
                    messageLength = BitConverter.ToInt32(buffer, 0);
                    currenState = Readstate.ReadingData;
                    //Console.WriteLine("收到了客户端的消息");
                    break;
                case Readstate.ReadingData:
                    int bytesRead = stream.Read(buffer, 0, messageLength);
                    string receivedMessage = Encoding.UTF8.GetString(buffer, 0, bytesRead);
                    currenState = Readstate.ReadingLength;
                    if (receivedMessage.Trim() == "BYE") {
                        break;
                    } else if (bytesRead > 0) {
                        Console.WriteLine($"收到客户端消息:{receivedMessage}");
                        break;
                    } else if (bytesRead == 0) {
                        Console.WriteLine("客户端已关闭");
                        break;
                    }
                    break;
            }
        } else {
            break;
        }
    }
}


enum Readstate { ReadingData, ReadingLength };


客户端的代码如下:

// See https://aka.ms/new-console-template for more information

using System.Net.Sockets;
using System.Text;
TcpClient tcpClient = new TcpClient("localhost", 8000);
Console.WriteLine("成功连接到服务器");

NetworkStream stream = tcpClient.GetStream();

string message = "我是客户端2";
byte[] data = Encoding.UTF8.GetBytes(message);
byte[] bytes = BitConverter.GetBytes(data.Length);


for (int i = 0; i <= 100; i++) {
    try {
        stream.Write(bytes, 0, bytes.Length);
        stream.Write(data, 0, data.Length);
    } catch (Exception e) {
        Console.WriteLine(e.ToString());
    }

    Console.WriteLine("数据已经发送");
}

if (!tcpClient.Connected) {
    Thread.Sleep(100000);
    Console.WriteLine("服务器已经断开连接!");
    return;

}
string goodbye = "BYE";
data = Encoding.UTF8.GetBytes(goodbye);
bytes = BitConverter.GetBytes(goodbye.Length);

byte[] goodbyeData = Encoding.UTF8.GetBytes(goodbye);
stream.Write(bytes, 0, bytes.Length);
stream.Write(goodbyeData, 0, goodbyeData.Length);

byte[] buffer = new byte[1024];
int bytesRead = stream.Read(buffer, 0, buffer.Length);
string response = Encoding.UTF8.GetString(buffer, 0, bytesRead);
Console.WriteLine(response);
Console.ReadLine();
stream?.Close();
tcpClient?.Close();

写到这里,我发现如果想要拓展新功能,那么很麻烦,服务器的所有内容都得进行重构。这里就是一个新问题,项目的架构问题,然后我将项目的文件夹分为三层:协议层,接口层,handler层。 这样的话,就比之前的代码更容易添加新功能了。

在这里,如果没有【我想要拓展新功能、然后发现拓展很麻烦】的过程,那么我就不太可能理解架构是什么,以及架构的作用是什么,以及架构为什么是这样的。