我正在尝试学习C#中套接字的工作原理。我的想法是编写一个简单的http代理: 这是我的代码:
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
class Program
{
static void Main(string[] args)
{
ThreadPool.SetMaxThreads(1000, 500);
ThreadPool.SetMinThreads(500, 250);
TcpListener listener = new TcpListener(IPAddress.Any, 8282);
listener.Start();
while (true)
{
Socket client = listener.AcceptSocket();
ThreadPool.QueueUserWorkItem(ProcessSocket, client);
}
}
private static readonly string patternHostPort = @"(Host:\s)(\S+)(:)(\d+)";
private static readonly string patternHost = @"(Host:\s)(\S+)";
private static Regex regexHostPort = new Regex(patternHostPort);
private static Regex regexHost = new Regex(patternHost);
static void ProcessSocket(object request)
{
string requestString = string.Empty;
MemoryStream mStream = new MemoryStream();
int bytesReceived;
int bytesSended;
byte[] buffer;
byte[] byteOriginalRequest;
Socket socketClient = (Socket)request;
Console.WriteLine("Incoming connection: " + socketClient.RemoteEndPoint.ToString());
buffer = new byte[4096];
bytesReceived = socketClient.Receive(buffer, 0, buffer.Length, SocketFlags.None);
mStream.Write(buffer, 0, bytesReceived);
while (socketClient.Available > 0)
{
bytesReceived = socketClient.Receive(buffer, 0, buffer.Length, SocketFlags.None);
mStream.Write(buffer, 0, bytesReceived);
}
mStream.Close();
byteOriginalRequest = mStream.ToArray();
requestString = Encoding.ASCII.GetString(byteOriginalRequest);
//Console.WriteLine(requestString);
#region Get requested Host and Port
string srvHost = string.Empty;
string srvPort = string.Empty;
Match matchHostPort = regexHostPort.Match(requestString);
if (matchHostPort.Success)
{
srvHost = matchHostPort.Groups[2].Value;
srvPort = matchHostPort.Groups[4].Value;
}
else
{
Match matchHost = regexHost.Match(requestString);
if (matchHost.Success)
{
srvHost = matchHost.Groups[2].Value;
srvPort = "80";
}
else
{
Console.WriteLine("Invalid request?");
}
}
#endregion
Console.WriteLine(string.Format("Request to {0} on port {1}", srvHost, srvPort));
IPAddress[] ipAddress = Dns.GetHostAddresses(srvHost);
IPEndPoint endPoint = new IPEndPoint(ipAddress[0], int.Parse(srvPort));
using (Socket socketProxy = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp))
{
socketProxy.Connect(endPoint);
bytesSended = socketProxy.Send(byteOriginalRequest, byteOriginalRequest.Length, SocketFlags.None);
MemoryStream m2Stream = new MemoryStream();
bytesReceived = 1;
while (bytesReceived > 0)
{
bytesReceived = socketProxy.Receive(buffer, 0, buffer.Length, SocketFlags.None);
m2Stream.Write(buffer, 0, bytesReceived);
}
m2Stream.Close();
byte[] finalResponse = m2Stream.ToArray();
string stringFinalResponse = Encoding.ASCII.GetString(finalResponse);
bytesSended = socketClient.Send(finalResponse, finalResponse.Length, SocketFlags.None);
socketProxy.Close();
}
socketClient.Close();
}
}
以下是一些问题:
1)为什么我要替换
bytesReceived = 1;
while (bytesReceived > 0)
{
bytesReceived = socketProxy.Receive(buffer, 0, buffer.Length, SocketFlags.None);
m2Stream.Write(buffer, 0, bytesReceived);
}
用这个
while (socketProxy.Available > 0)
{
bytesReceived = socketProxy.Receive(buffer, 0, buffer.Length, SocketFlags.None);
m2Stream.Write(buffer, 0, bytesReceived);
}
我只获得一个空页面(在浏览器中)?
2)也许与1)相关但是...使用当前代码加载任何简单页面(如谷歌)需要一段时间,调试我发现问题可能是在socketProxy.Receive中...但我不知道为什么。
3)
之间有什么区别吗?socketProxy.Send(byteOriginalRequest, byteOriginalRequest.Length, SocketFlags.None);
或
socketProxy.Send(byteOriginalRequest);
? (更多参数不是有效答案:)
4)任何推荐阅读?书,教程,......?
5)任何其他学习套接字的建议?
感谢您的时间。 最好的问候。
答案 0 :(得分:3)
1)如果在您调用它的瞬间缓冲区中没有可用数据,Socket.Available将返回零。如果没有可用的数据,Socket.Read将阻止(等待)数据到达。这就是差异。通过调用Read,您可以等待数据。通过检查可用,您不要等待。因此,如果您在数据到达之前碰巧看到可用,则它将为零。
2)不确定为什么它很慢,但是你不需要为发送回客户端的数据使用内存缓冲区,因为你没有检查它。只需从一个套接字读取并直接写入另一个套接字。
3)这两个电话是相同的。
对于4)和5),CLR套接字API非常接近原始的C API,因此如果很难找到C#教程,你可以查看C语言中有关良好套接字编程的任何教程或信息以获取更多提示
此外,您应在调用Close之前调用Shutdown。当您调用Close时,套接字断开连接,这意味着另一端丢失连接并且无法读取他们尚未读取的任何数据 - 操作系统会破坏另一端的缓冲区并丢弃数据。调用Shutdown(SocketShutdown.Send)会导致另一端在调用Read时(在读取剩余数据之后)获得零字节。因此,您调用Read直到它返回零,这告诉您另一端已获得所有数据并且还调用了Shutdown。然后你可以打电话给Close。
最后,当您最后一次调用Send将所有数据发送回客户端时,它可能不会一次性发送所有数据。所以你应该循环并继续发送任何剩余的发送内容,直到它们全部被发送完毕。
答案 1 :(得分:1)
如果这是您的第一个Sockets投资,我强烈建议您从“聊天”应用程序开始。无论哪种方式,一个有价值的测试工具也是一个名为WinsockTool的小工具。
不知道您是否可以在此处直接将链接发布到.MSI文件,但如果这不起作用:
http://www.isatools.org/tools/winsocktool.msi
Google for site:isatools.org WinsockTool
这非常适合连接到“服务器”套接字并发送原始文本并查看原始响应,而无需创建客户端应用程序。