Created
November 1, 2016 10:03
-
-
Save cool-delete/31ca1519d1e6fbb080a15d55c004746e to your computer and use it in GitHub Desktop.
socket处理http协议
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
它是网络通信过程中端点的抽象表示,包含进行网络通信必须的五种信息:连接使用的协议, | |
本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口。 | |
套接字之间的连接过程分为三个步骤:服务器监听,客户端请求,连接确认。 | |
1.HTTP请求格式: | |
<request line> | |
<headers> | |
<blank line> | |
[<request-body>] | |
2.了解Socket,TCP,HTTP,直接的关系 | |
HTTP协议(Hypertext Transfer Protocol ),是Web联网的基础,也是手机联网常用的协议之一,HTTP协议是建立在TCP协议之上的一种应用。 | |
创建Socket连接时,可以指定使用的传输层协议,Socket可以支持不同的传输层协议(TCP或UDP),当使用TCP协议进行连接时,该Socket连接就是一个TCP连接。 | |
.NetFrameWork为Socket通讯提供了System.Net.Socket命名空间,在这个命名空间里面有以下几个常用的重要类分别是: | |
·Socket类这个低层的类用于管理连接,WebRequest,TcpClient和UdpClient在内部使用这个类。 | |
·NetworkStream类这个类是从Stream派生出来的,它表示来自网络的数据流 | |
·TcpClient类允许创建和使用TCP连接 | |
·TcpListener类允许监听传入的TCP连接请求 | |
·UdpClient类用于UDP客户创建连接(UDP是另外一种TCP协议,但没有得到广泛的使用,主要用于本地网络) | |
IPAddress类 提供网际协议 (IP) 地址。 | |
IPEndPoint 继承 EndPoint 将网络端点表示为 IP 地址和端口号。 | |
简单的Http请求服务处理类 | |
复制代码 | |
class MyWebServer | |
{ | |
private readonly int port = Convert.ToInt32(ConfigurationManager.AppSettings["port"].ToString()); | |
private readonly string host = ConfigurationManager.AppSettings["host"]; | |
private readonly string sMyWebServerRoot = ConfigurationManager.AppSettings["dir"]; | |
private TcpListener tcplistener=null; | |
public MyWebServer() | |
{ | |
try | |
{ | |
///创建终结点(EndPoint) | |
IPAddress ip = IPAddress.Parse(host);//把ip地址字符串转换为IPAddress类型的实例 | |
//TcpListener类对象,监听端口 | |
tcplistener = new TcpListener(ip, port); | |
tcplistener.Start(); | |
Console.WriteLine("Web Server Running... Press ^C to Stop..."); | |
//同时启动一个兼听进程 ''StartListen'' | |
Thread th = new Thread(new ThreadStart(StartListen)); | |
th.Start(); | |
} | |
catch (Exception e) | |
{ | |
Console.WriteLine("监听端口时发生错误 :" + e.ToString()); | |
} | |
} | |
/// <summary> | |
/// 设置请求的标头 | |
/// </summary> | |
/// <param name="sHttpVersion"></param> | |
/// <param name="sMIMEHeader"></param> | |
/// <param name="iTotBytes"></param> | |
/// <param name="sStatusCode"></param> | |
/// <param name="mySocket"></param> | |
public void SendHeader(string sHttpVersion, string sMIMEHeader, int iTotBytes, string sStatusCode, ref Socket mySocket) | |
{ | |
String sBuffer = ""; | |
if (sMIMEHeader.Length == 0) | |
{ | |
sMIMEHeader = "text/html"; // 默认 text/html | |
} | |
sBuffer = sBuffer + sHttpVersion + sStatusCode + "\r\n"; | |
sBuffer = sBuffer + "Server: cx1193719-b\r\n"; | |
sBuffer = sBuffer + "Content-Type: " + sMIMEHeader + "\r\n"; | |
sBuffer = sBuffer + "Accept-Ranges: bytes\r\n"; | |
sBuffer = sBuffer + "Content-Length: " + iTotBytes + "\r\n\r\n"; | |
Byte[] bSendData = Encoding.ASCII.GetBytes(sBuffer); | |
SendToBrowser(bSendData, ref mySocket); | |
Console.WriteLine("Total Bytes : " + iTotBytes.ToString()); | |
} | |
public void SendToBrowser(String sData, ref Socket mySocket) | |
{ | |
SendToBrowser(Encoding.ASCII.GetBytes(sData), ref mySocket); | |
} | |
/// <summary> | |
/// 负责向客户端发起数据 | |
/// </summary> | |
/// <param name="bSendData">字节数组</param> | |
/// <param name="mySocket">Soket对象!</param> | |
public void SendToBrowser(Byte[] bSendData, ref Socket mySocket) | |
{ | |
int numBytes = 0; | |
try | |
{ | |
if (mySocket.Connected) | |
{ | |
if ((numBytes = mySocket.Send(bSendData, bSendData.Length, 0)) == -1) | |
Console.WriteLine("Socket Error cannot Send Packet"); | |
else | |
{ | |
Console.WriteLine("No. of bytes send {0}", numBytes); | |
} | |
} | |
else | |
Console.WriteLine("连接失败...."); | |
} | |
catch (Exception e) | |
{ | |
Console.WriteLine("发生错误 : {0} ", e); | |
} | |
} | |
public static void Main() | |
{ | |
MyWebServer MWS = new MyWebServer(); | |
} | |
public void StartListen() | |
{ | |
int iStartPos = 0; | |
String sRequest; | |
String sDirName; | |
String sRequestedFile; | |
String sErrorMessage; | |
String sLocalDir; | |
String sPhysicalFilePath = ""; | |
String sFormattedMessage = ""; | |
String sResponse = ""; | |
//进入监听循环 | |
while (true) | |
{ | |
//接受新连接 | |
Socket mySocket = tcplistener.AcceptSocket(); | |
Console.WriteLine("Socket Type " + mySocket.SocketType); | |
if (mySocket.Connected) | |
{ | |
Console.WriteLine("\nClient Connected!!\n==================\nCLient IP {0}\n", mySocket.RemoteEndPoint); | |
//将请求转化成字节数组! | |
// 为读取数据而准备缓存 | |
Byte[] bReceive = new Byte[1024]; | |
int i = mySocket.Receive(bReceive, bReceive.Length, 0); | |
//转换成字符串类型 | |
string sBuffer = Encoding.ASCII.GetString(bReceive); | |
Console.WriteLine(sBuffer); | |
//只处理"get"请求类型 | |
if (sBuffer.Substring(0, 3) != "GET") | |
{ | |
Console.WriteLine("只处理get请求类型.."); | |
mySocket.Close(); | |
return; | |
} | |
// 查找 "HTTP" 的位置 | |
iStartPos = sBuffer.IndexOf("HTTP", 1); | |
string sHttpVersion = sBuffer.Substring(iStartPos, 8); | |
// 得到请求类型和文件目录文件名 | |
sRequest = sBuffer.Substring(0, iStartPos - 1); | |
sRequest.Replace("\\", "/"); | |
//如果结尾不是文件名也不是以"/"结尾则加"/" | |
if ((sRequest.IndexOf(".") < 1) && (!sRequest.EndsWith("/"))) | |
{ | |
sRequest = sRequest + "/"; | |
} | |
//得带请求文件名 | |
iStartPos = sRequest.LastIndexOf("/") + 1; | |
sRequestedFile = sRequest.Substring(iStartPos); | |
//得到请求文件目录 | |
sDirName = sRequest.Substring(sRequest.IndexOf("/"), sRequest.LastIndexOf("/") - 3); | |
//获取虚拟目录物理路径 | |
sLocalDir = sMyWebServerRoot; | |
Console.WriteLine("请求文件目录 : " + sLocalDir); | |
if (sLocalDir.Length == 0) | |
{ | |
sErrorMessage = "<H2>Error!! Requested Directory does not exists</H2><Br>"; | |
SendHeader(sHttpVersion, "", sErrorMessage.Length, " 404 Not Found", ref mySocket); | |
SendToBrowser(sErrorMessage, ref mySocket); | |
mySocket.Close(); | |
continue; | |
} | |
if (sRequestedFile.Length == 0) | |
{ | |
// 取得请求文件名 | |
sRequestedFile = "index.html"; | |
} | |
///////////////////////////////////////////////////////////////////// | |
// 取得请求文件类型(设定为text/html) | |
///////////////////////////////////////////////////////////////////// | |
String sMimeType = "text/html"; | |
sPhysicalFilePath = sLocalDir + sRequestedFile; | |
Console.WriteLine("请求文件: " + sPhysicalFilePath); | |
if (File.Exists(sPhysicalFilePath) == false) | |
{ | |
sErrorMessage = "<H2>404 Error! File Does Not Exists...</H2>"; | |
SendHeader(sHttpVersion, "", sErrorMessage.Length, " 404 Not Found", ref mySocket); | |
SendToBrowser(sErrorMessage, ref mySocket); | |
Console.WriteLine(sFormattedMessage); | |
} | |
else | |
{ | |
int iTotBytes = 0; | |
sResponse = ""; | |
FileStream fs = new FileStream(sPhysicalFilePath, FileMode.Open, FileAccess.Read, FileShare.Read); | |
BinaryReader reader = new BinaryReader(fs); | |
byte[] bytes = new byte[fs.Length]; | |
int read; | |
while ((read = reader.Read(bytes, 0, bytes.Length)) != 0) | |
{ | |
sResponse = sResponse + Encoding.ASCII.GetString(bytes, 0, read); | |
iTotBytes = iTotBytes + read; | |
} | |
reader.Close(); | |
fs.Close(); | |
SendHeader(sHttpVersion, sMimeType, iTotBytes, " 200 OK", ref mySocket); | |
SendToBrowser(bytes, ref mySocket); | |
//mySocket.Send(bytes, bytes.Length,0); | |
} | |
mySocket.Close(); | |
} | |
} | |
} | |
} | |
复制代码 | |
服务端配置文件 | |
复制代码 | |
<?xml version="1.0" encoding="utf-8" ?> | |
<configuration> | |
<appSettings> | |
<add key="port" value="1280"/> | |
<!--IP地址--> | |
<add key="host" value="127.0.0.1"/> | |
<!--设定你自己的虚拟目录--> | |
<add key="dir" value="E:\\MyWebServerRoot\\"/> | |
</appSettings> | |
</configuration> | |
复制代码 | |
客户端请求类 | |
复制代码 | |
static void Main(string[] args) | |
{ | |
int port = 1280; | |
string host = "127.0.0.1"; | |
IPAddress ip = IPAddress.Parse(host); | |
// string hotByname = "www.baidu.com"; | |
// IPHostEntry gist = Dns.GetHostByName(hotByname); | |
//IPAddress ip = gist.AddressList[0]; | |
/**/ | |
///创建终结点EndPoint | |
//IPAddress ipp = new IPAddress("127.0.0.1"); | |
IPEndPoint ipe = new IPEndPoint(ip, port);//把ip和端口转化为IPEndpoint实例 | |
/**/ | |
///创建socket并连接到服务器 | |
Socket c = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//创建Socket | |
Console.WriteLine("Conneting…"); | |
c.Connect(ipe);//连接到服务器 | |
///向服务器发送信息 | |
//{GET /index.php HTTP/1.0Content-Type: application/x-www-form-urlencoded | |
StringBuilder buf = new StringBuilder(); | |
buf.Append("GET ").Append("/index.php").Append(" HTTP/1.0\r\n"); | |
buf.Append("Content-Type: application/x-www-form-urlencoded\r\n"); | |
buf.Append("\r\n"); | |
byte[] bs = Encoding.ASCII.GetBytes(buf.ToString());//把字符串编码为字节 | |
Console.WriteLine("Send Message"); | |
c.Send(bs);//发送信息 | |
/**/ | |
///接受从服务器返回的信息 | |
string recvStr = ""; | |
byte[] recvBytes = new byte[1024]; | |
int bytes; | |
do | |
{ | |
bytes = c.Receive(recvBytes, recvBytes.Length, 0);//从服务器端接受返回信息 | |
recvStr += Encoding.Default.GetString(recvBytes, 0, bytes); | |
Console.WriteLine("client get message:{0}", recvStr);//显示服务器返回信息 | |
} while (bytes!=0); | |
/**/ | |
///一定记着用完socket后要关闭 | |
c.Close(); | |
Console.ReadLine(); | |
复制代码 | |
以上有一个问题注意: | |
GET /xxx.xxx HTTP/1.1 | |
Host: xxx | |
Connection: Close //没有关闭请求! | |
只有加入这最后一行,对方发送完数据才会关闭连接,只有对方关闭连接,我们这边才能recv到一个0, | |
从而根据recv返回的0判断接收数据完毕,然后我们就可以退出拼接数据包的循环。 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment