我认为学习应该【基于问题+知识】的方式去学习,也就是说我应该先去发现问题,然后再去寻找解决方法,然后再去学习知识,比如下面的这个网络传输协议的学习过程。
在学习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层。 这样的话,就比之前的代码更容易添加新功能了。
在这里,如果没有【我想要拓展新功能、然后发现拓展很麻烦】的过程,那么我就不太可能理解架构是什么,以及架构的作用是什么,以及架构为什么是这样的。