HTTP 服务器是一种遵循超文本传输协议(HTTP)的服务器,用于在网络上传输和处理网页及其他相关资源。以下是关于它的详细介绍:
工作原理
- HTTP 服务器监听指定端口(通常是 80 端口用于 HTTP,443 端口用于 HTTPS),等待客户端(如浏览器)发送请求。当客户端发送请求时,服务器解析请求,根据请求的 URL 和其他信息,找到对应的资源(如 HTML 文件、图片、脚本等),然后将这些资源封装在 HTTP 响应消息中,发送回客户端。
搭建HTTP服务器
这里我们使用别人做好的HTTP服务器:hfs

HTTP的关键类
利用Head类型请求资源的可用性
在 C# 里,HTTP 的HEAD
请求方法是一种特殊的 HTTP 请求类型。它和GET
请求类似,区别在于HEAD
请求仅要求服务器返回 HTTP 响应头信息,而不返回请求资源的具体内容。这种方法在很多场景中都很有用,比如检查资源是否存在、获取资源的元数据(像文件大小、修改时间),同时避免传输大量数据。
cs
#region 知识点一 检测资源的可用性
try
{
//利用Head类型请求类型,获取信息
//1.创建Http通讯用连接对象HttpWebRequest对象
HttpWebRequest req = HttpWebRequest.Create("http://172.41.2.6/HTTP_Server/测试图片.png") as HttpWebRequest;
//2.设置请求类型,或其他相关设置参数
req.Method = WebRequestMethods.Http.Head;
req.Timeout = 2000;
//3.发送请求,获取响应结果HttpWebResponse对象
HttpWebResponse res = req.GetResponse() as HttpWebResponse;
if (res.StatusCode == HttpStatusCode.OK)
{
print("文件存在且可用");
print(res.ContentLength);
print(res.ContentType);
res.Close();
}
else
print("文件不能用" + res.StatusCode);
}
catch (WebException w)
{
print("获取出错"+w.Message +w.Status);
}
#endregion
利用Get类型下载服务器中的资源
在 C# 里,GET
是 HTTP 协议里最常用的请求方法之一,其作用是从指定的服务器获取资源
cs
#region 下载资源
try
{
//利用GET请求类型,下载资源
//1.创建HTTP通讯用连接对象HttpWebRequest对象
HttpWebRequest req = HttpWebRequest.Create(new Uri("http://172.41.2.6/HTTP_Server/测试图片.png")) as HttpWebRequest;
//2.设置请求类型,或其他相关操作
req.Method = WebRequestMethods.Http.Get;
req.Timeout = 3000;
//3.发送请求,获取响应结果HttpWebResponse对象
HttpWebResponse res = req.GetResponse() as HttpWebResponse;
//4.获取响应数据流,写入本地路径
if (res.StatusCode == HttpStatusCode.OK)
{
print(Application.persistentDataPath);
using (FileStream file = File.Create(Application.persistentDataPath + "/httpDowmload.png"))
{
Stream downLoadStream = res.GetResponseStream();
byte[] bytes = new byte[2048];
int contentLength = downLoadStream.Read(bytes, 0, bytes.Length);
while (contentLength != 0)
{
file.Write(bytes, 0, contentLength);
contentLength = downLoadStream.Read(bytes, 0, bytes.Length);
}
file.Close();
downLoadStream.Close();
res.Close();
}
print("下载成功");
}
else
print("下载失败");
}
catch (WebException w)
{
print("下载出错了" + w.Message + w.Status);
}
#endregion
利用Post类型给服务器上传资源
在 C# 里,POST
请求方法用于向服务器提交数据,通常用于创建新资源,比如用户注册、表单提交等场景
Post的学前准备
Get和Post的区别是什么
语义和用途
- GET :设计目的是从服务器获取资源。例如,当你在浏览器地址栏输入网址或者点击超链接时,浏览器通常会发送
GET
请求来获取对应的网页、图片、文件等资源。 - POST :主要用于向服务器提交数据,通常用于创建、更新服务器上的资源。像用户注册、登录、提交表单数据等场景,往往会使用
POST
请求。
数据传输方式
- GET :请求参数会附加在 URL 后面,以键值对的形式出现,多个参数之间用
&
符号分隔。例如:https://example.com/search?keyword=apple&category=fruits
。 - POST:请求参数会放在 HTTP 请求体中进行传输,不会显示在 URL 里。这样可以传输大量数据,并且更适合传输敏感信息。
数据长度限制
- GET :由于请求参数会附加在 URL 后面,而不同的浏览器和服务器对 URL 的长度有一定限制,因此
GET
请求所能携带的数据量有限。 - POST:请求参数放在请求体中,理论上对数据长度没有限制,但服务器可能会对请求体的大小进行限制。
安全性
- GET :请求参数会暴露在 URL 中,因此不适合传输敏感信息,如密码、信用卡号等。此外,
GET
请求还可能被缓存,存在一定的安全风险。 - POST:请求参数在请求体中,不会暴露在 URL 里,相对更安全。不过,如果不使用 HTTPS 协议进行加密传输,请求体中的数据仍可能被截获。
缓存机制
- GET :通常会被浏览器缓存,这意味着如果多次发送相同的
GET
请求,浏览器可能会直接从本地缓存中获取响应,而不会再次向服务器发送请求。 - POST :默认情况下不会被缓存,每次发送
POST
请求都会向服务器发送新的请求。
幂等性
- GET :是幂等的,即多次执行相同的
GET
请求,得到的结果是相同的,不会对服务器上的资源产生额外的影响。 - POST :不是幂等的,多次执行相同的
POST
请求可能会在服务器上创建多个相同的资源,或者对资源进行多次更新。
Post如何携带额外参数
cs
#region 知识点二 Post如何携带额外参数
//关键点:将Content-Type设置为 application/x-www-form-urlencoded键值对类型
HttpWebRequest req = HttpWebRequest.Create("http://172.41.2.6/HTTP_Server") as HttpWebRequest;
req.Method = WebRequestMethods.Http.Post;
req.Timeout = 2000;
//设置上传内容的类型
req.ContentType = "application/x-www-form-urlencoded";
//我们要上传的数据
string str = "Name=DamnF&ID=2";
byte[] bytes = Encoding.UTF8.GetBytes(str);
//我们在上传之前一定要设置内容的长度
req.ContentLength = bytes.Length;
//上传数据
Stream stream = req.GetRequestStream();
stream.Write(bytes, 0, bytes.Length);
stream.Close();
//发送数据得到响应结果
HttpWebResponse res= req.GetResponse()as HttpWebResponse;
print(res.StatusCode);
#endregion
Content-Type中重要的类型
cs
#region 知识点四 ContentType中对于我们重要的类型
//1.通用的2进制类型
//application/octet-stream
//2.通用文本类型
//text/plain
//3.键值对参数
//application/x-www-form-urlencoded
//4.复合类型(传递的信息由多种信息组成,比如有键值对参数,文件信息等,上传资源服务器时需要用到该类型)
//multipart//form-data
#endregion
开始使用Post上传资源
cs
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using UnityEngine;
public class Lesson27 : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
#region 知识点一 上传文件到HTTP资源服务器需要遵守的规则
//上传文件内容必须遵守的规则
//1:ContentType="multipart/form-data;boundary=边界字符串";
//2:上传的格式必须按照格式写入流中
// ---边界字符串
//Content-Disposition:form-data;name="file";filename="传到服务器上使用的文件名";
//Content-Type:application/octet-stream(由于我们上传2进制文件,所以这里使用2进制)
//(这里直接写入传入的内容)
//--边界字符串--
//3:保证服务器允许上传
//4:写入流之前需要先设置ContentLength内容长度
#endregion
#region 知识点二 上传文件
//1.创建HttpWebRequest对象
HttpWebRequest req = HttpWebRequest.Create("http://172.41.2.6/HTTP_Server/") as HttpWebRequest;
//2.相关设置(请求类型,内容类型,超时,身份验证等)
req.Method = WebRequestMethods.Http.Post;
req.ContentType = "multipart/form-data;boundary=DamnF";
req.Timeout = 500000;
req.Credentials = new NetworkCredential("DamnF", "123");
req.PreAuthenticate = true;//先验证身份,再上传数据
//3.按格式拼接字符串并且转换为字节数组之后用于上传
//3-1.文件数据前的头部信息
// --边界字符串--
//Content-Disposition:form-data;name="字段名字,之后写入的文件2进制数据和该字段名对应";filename="传到服务器上使用的文件名";
//Content-Type:application/octet-stream(由于我们上传2进制文件,所以这里使用2进制)
//空一行
string head = "--DamnF\r\n" +
"Content-Disposition:form-data;name=\"file\";filename=\"http上传文件.png\"\r\n" +
"Content-Type:application/octet-stream\r\n\r\n";
//头部拼接字符串规则信息的字节数组
byte[] headBytes = Encoding.UTF8.GetBytes(head);
//3-2结束的边界信息
// --边界字符串--
byte[] endBytes = Encoding.UTF8.GetBytes("\r\n--DamnF--\r\n");
//4.写入上传流
using (FileStream localFileStream=File .OpenRead(Application .streamingAssetsPath +"/test.png"))
{
//4-1设置上传长度
//总长度=前部分字符串长度+文件本身长度+后部分边界字符串
req.ContentLength = headBytes.Length + localFileStream.Length + endBytes.Length;
//用于上传的流
Stream upLoadStream = req.GetRequestStream();
//4-2先写入前部分头部信息
upLoadStream.Write(headBytes, 0, headBytes.Length);
//4-3再写入文件数据
byte[] bytes = new byte[2048];
int contentLength = localFileStream.Read(bytes, 0, bytes.Length);
while (contentLength !=0)
{
upLoadStream.Write(bytes, 0, contentLength);
contentLength = localFileStream.Read(bytes, 0, bytes.Length);
}
//4-4再写入结束的边界信息
upLoadStream.Write(endBytes, 0, endBytes.Length);
upLoadStream.Close();
localFileStream.Close();
}
//上传数据,获取响应
HttpWebResponse res = req.GetResponse() as HttpWebResponse;
if (res.StatusCode == HttpStatusCode.OK)
print("上传成功");
else
print("上传失败"+res.StatusCode);
#endregion
}
// Update is called once per frame
void Update()
{
}
}
将Get和Post用单例模式封装到一个HTTPMgr模块中(异步)
cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Events;
public class HttpMgr
{
private static HttpMgr instance=new HttpMgr ();
public static HttpMgr Instance => instance;
//http服务器地址
private string HTTP_PATH = "http://172.41.2.6/HTTP_Server/";
//Http服务器账号和密码
private string HTTP_ID = "DamnF";
private string HTTP_PASSWORD = "123";
public async void DownLoadFile(string fileName,string localPath,UnityAction<bool>action=null)
{
await Task.Run(()=>
{
try
{
HttpWebRequest req = HttpWebRequest.Create(new Uri(HTTP_PATH + fileName)) as HttpWebRequest;
req.Method = WebRequestMethods.Http.Get;
req.Timeout = 3000;
HttpWebResponse res= req.GetResponse()as HttpWebResponse;
if (res.StatusCode == HttpStatusCode.OK)
{
using (FileStream fileStream = File.Create(localPath))
{
Stream downLoadStream = res.GetResponseStream();
byte[] bytes = new byte[2048];
int contentLength = downLoadStream.Read(bytes, 0, bytes.Length);
while (contentLength != 0)
{
fileStream.Write(bytes, 0, contentLength);
contentLength = downLoadStream.Read(bytes, 0, bytes.Length);
}
Debug.Log("下载文件成功");
action?.Invoke(true);
fileStream.Close();
downLoadStream.Close();
}
res.Close();
}
else
Debug.Log("下载文件失败");
}
catch (WebException w)
{
Debug.Log("下载文件出错" + w.Message + w.Status);
action?.Invoke(false);
}
});
}
public async void UpLoadFile(string fileName, string localFileName,UnityAction <bool>action=null)
{
await Task.Run(() =>
{
try
{
HttpWebRequest req = HttpWebRequest.Create(HTTP_PATH) as HttpWebRequest;
req.Method = WebRequestMethods.Http.Post;
req.ContentType = "multipart/form-data;boundary=DamnF";
req.Timeout = 500000;
req.Credentials = new NetworkCredential(HTTP_ID, HTTP_PASSWORD);
req.PreAuthenticate = true;
string head = "--DamnF\r\n" +
"Content-Disposition:form-data;name=\"file\";filename=\"{0}\"\r\n" +
"Content-Type:application/octet-stream\r\n\r\n";
head = string.Format(head, fileName);
byte[] headBytes = Encoding.UTF8.GetBytes(head);
byte[] endBytes = Encoding.UTF8.GetBytes("\r\n--DamnF--\r\n");
using (FileStream localFileStream=File .OpenRead (localFileName))
{
req.ContentLength = headBytes.Length + localFileStream.Length + endBytes.Length;
Stream upLoadStream = req.GetRequestStream();
upLoadStream.Write(headBytes, 0, headBytes.Length);
byte[] bytes = new byte[2048];
int contentLength= localFileStream.Read(bytes, 0, bytes.Length);
while (contentLength !=0)
{
upLoadStream.Write(bytes, 0, contentLength);
contentLength = localFileStream.Read(bytes, 0, bytes.Length);
}
upLoadStream.Write(endBytes, 0, endBytes.Length);
upLoadStream.Close();
localFileStream.Close();
}
HttpWebResponse res= req.GetResponse()as HttpWebResponse;
if(res.StatusCode ==HttpStatusCode.OK )
{
action?.Invoke(true);
}
else
{
action?.Invoke(false);
}
}
catch (WebException e)
{
Debug.Log("上传出错了" + e.Message + e.Status);
}
});
}
}
测试HTTPMgr模块
cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Lesson25_Test : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
print(Application.persistentDataPath);
HttpMgr.Instance.DownLoadFile("/测试图片.png", Application.persistentDataPath + "/downTest.png", (result) =>
{
print(result ? "下载成功调用委托函数" : "下载失败调用委托函数");
});
HttpMgr.Instance.UpLoadFile("http上传测试文件.png",Application.streamingAssetsPath + "/test.png", (result) =>
{
print(result ? "文件上传成功" : "文件上传失败");
});
}
// Update is called once per frame
void Update()
{
}
}
这样就实现了对Http服务器的操作了