我正在使用c#开发一个类似thrift的程序,而现在我需要像IIS这样的服务器可以更快地在客户端和服务器之间发送数据。这样做了,但我发现了一个问题:当服务器从客户端发送/接收数据时。它比IIS慢得多。
我的测试用例是我的程序和IIS返回请求客户端一个空白页面(内容长度为零),只是为了测试和比较它们之间的并发级别。
我发现:我程序的传输速度最高可达600KB-1.1MB / s,但IIS最高可达1.1MB-1.8MB / s
我的问题是: C#中的socket是否比本机socket更慢,或者我能做些什么来加快socket的传输速度?
主要代码如下:
/// <summary>
/// Http Service context
/// </summary>
public class HttpServerContext : ServerContext
{
private const int BUFFER_SIZE = 16384;
private static readonly byte[] HEADER_CACHE_CONTROL = FrameworkConfig.Encoding.GetBytes("Cache-Control:");
private static readonly byte[] HEADER_CONNECTION_CLOSE = FrameworkConfig.Encoding.GetBytes("Connection:Close\r\n");
private static readonly byte[] HEADER_CONNECTION_KEEP_ALIVE = FrameworkConfig.Encoding.GetBytes("Connection:keep-alive\r\n");
private static readonly byte[] HEADER_CONTENT_ENCODING = FrameworkConfig.Encoding.GetBytes("Content-Encoding:");
private static readonly byte[] HEADER_CONTENT_ENCODING_DEFLATE = FrameworkConfig.Encoding.GetBytes("Content-Encoding:deflate\r\n");
private static readonly byte[] HEADER_CONTENT_TYPE_HTML = FrameworkConfig.Encoding.GetBytes("Content-Type:text/html\r\n");
private static readonly byte[] HEADER_CONTENT_ENCODING_LENGTH = FrameworkConfig.Encoding.GetBytes("Content-Length:");
private static readonly byte[] HEADER_CONTENT_ENCODING_LENGTH_0 = FrameworkConfig.Encoding.GetBytes("Content-Length:0\r\n");
private static readonly byte[] HEADER_LAST_MODIFIED = FrameworkConfig.Encoding.GetBytes("Last-Modified:");
private static readonly byte[] HEADER_EXPIRES = FrameworkConfig.Encoding.GetBytes("Expires:");
private static readonly byte[] HEADER_ETAG = FrameworkConfig.Encoding.GetBytes("ETag:");
private static readonly byte[] STATUS_OK = FrameworkConfig.Encoding.GetBytes("HTTP/1.1 200 OK\r\n");
private static readonly byte[] STATUS_ERROR = FrameworkConfig.Encoding.GetBytes("HTTP/1.1 500 Internal Service Error\r\n");
private static readonly byte[] STATUS_NOT_MODIFIED = FrameworkConfig.Encoding.GetBytes("HTTP/1.1 304 Not Modified\r\n");
private static readonly byte[] HEADER_SERVER = FrameworkConfig.Encoding.GetBytes("Server:Aria\r\n");
private static readonly byte[] HEADER_END = FrameworkConfig.Encoding.GetBytes("\r\n");
private readonly SocketAsyncEventArgs m_args;
private readonly HttpServer m_server;
private readonly byte[] m_buffer;
private string m_ifModifiedSince;
private int m_length;
private int m_offset;
private int m_timeout;
private readonly LinkedList<byte[]> m_output = new LinkedList<byte[]>();
private string m_queryString;
private string m_requestEtag;
private bool m_shutdown;
private Socket m_socket;
private string m_ticket;
private string m_url;
private static string m_sample;
internal HttpServerContext(HttpServer server)
: base(server)
{
this.m_server = server;
this.m_args = new SocketAsyncEventArgs();
m_buffer = new byte[BUFFER_SIZE];
this.m_args.SetBuffer(m_buffer, 0, BUFFER_SIZE);
this.m_args.Completed += Args_Completed;
}
private void Args_Completed(object sender, SocketAsyncEventArgs e)
{
if (e.SocketError != SocketError.Success)
{
m_server.ReportError(e.SocketError);
this.Close();
return;
}
if (m_socket == null)
{
return;
}
switch (e.LastOperation)
{
case SocketAsyncOperation.Receive:
this.ProcessReceive();
break;
case SocketAsyncOperation.Send:
ProcessSend();
break;
}
}
private void ProcessSend()
{
int rest;
LOOPSTART:
int offset = 0;
while (m_output.Count > 0)
{
LinkedListNode<byte[]> node = m_output.First;
byte[] buffer = node.Value;
CHUNK_START:
rest = buffer.Length - m_offset;
if (rest > BUFFER_SIZE)
{
Buffer.BlockCopy(buffer, m_offset, m_buffer, 0, BUFFER_SIZE);
m_offset += BUFFER_SIZE;
Log.Debug.Write("Send a chunk:" + m_socket.RemoteEndPoint + "\r\n" + FrameworkConfig.Encoding.GetString(m_buffer, 0, BUFFER_SIZE));
if (!m_socket.SendAsync(this.m_args))
{
goto CHUNK_START;
}
}
else if (rest == BUFFER_SIZE)
{
Buffer.BlockCopy(buffer, m_offset, m_buffer, 0, BUFFER_SIZE);
m_offset = 0;
m_output.Remove(node);
Log.Debug.Write("Send final chunk of block:" + m_socket.RemoteEndPoint + "\r\n" + FrameworkConfig.Encoding.GetString(m_buffer, 0, BUFFER_SIZE));
if (!m_socket.SendAsync(this.m_args))
{
goto LOOPSTART;
}
return;
}
else
{
Buffer.BlockCopy(buffer, m_offset, m_buffer, offset, rest);
offset += rest;
m_offset = 0;
m_output.Remove(node);
}
}
if (offset > 0)
{
Log.Debug.Write("Send final data of request:" + m_socket.RemoteEndPoint + "\r\n" + FrameworkConfig.Encoding.GetString(m_buffer, 0, offset));
m_args.SetBuffer(0, offset);
if (m_socket.SendAsync(this.m_args))
{
return;
}
Log.Debug.Write("Done by sync:" + m_socket.RemoteEndPoint + "," + m_args.BytesTransferred);
}
m_args.SetBuffer(0, BUFFER_SIZE);
if (m_socket == null)
{
m_server.ReportError("Socket closed");
this.Close();
}
else if (m_shutdown)
{
m_server.ReportError("Shutdown");
this.Close();
}
else
{
Log.Debug.Write("Next request/response workflow:" + m_socket.RemoteEndPoint);
this.NextWorkflow();
}
}
private void ProcessReceive()
{
int bytes = m_args.BytesTransferred;
if (bytes == 0)
{
m_timeout++;
if (m_timeout < 100)
{
if (!m_socket.ReceiveAsync(this.m_args))
{
Args_Completed(null, m_args);
}
return;
}
m_server.ReportError("Zero data");
this.Close();
return;
}
m_timeout = 0;
if (bytes == BUFFER_SIZE)
{
m_server.ReportError("Request too large");
this.Close();
return;
}
if (m_sample == null)
{
m_sample = FrameworkConfig.Encoding.GetString(m_buffer, 0, bytes);
Console.WriteLine(m_sample);
}
int offset = 0;
int cursor = 0;
int count;
string header = null;
while (cursor < bytes)
{
byte c = m_buffer[cursor];
switch (c)
{
case 0x0D: //\r
count = cursor - offset;
if (count == 0)
{
goto END;
}
if (!ProcessRequest(offset, count, ref header))
{
m_server.ReportError("Parse request error");
this.Close();
return;
}
cursor++;
MoveForwardIf(ref cursor, '\n', bytes);
offset = cursor;
continue;
case 0x0A: //\n
count = cursor - offset;
if (count == 0)
{
goto END;
}
if (!ProcessRequest(offset, count, ref header))
{
m_server.ReportError("Parse request error");
this.Close();
return;
}
offset = cursor + 1;
break;
case 0x3A:
if (header == null)
{
header = FrameworkConfig.Encoding.GetString(m_buffer, offset, cursor - offset);
cursor++;
MoveForwardIf(ref cursor, ' ', bytes);
offset = cursor;
continue;
}
break;
}
cursor++;
}
END:
if (m_url != null)
{
if (m_socket != null)
{
this.ProcessExecute();
}
}
else
{
m_server.ReportError("Parse url error");
this.Close();
}
}
private void MoveForwardIf(ref int cursor, char ch, int len)
{
while (cursor < len)
{
if (m_buffer[cursor] == ch)
{
cursor++;
}
else
{
break;
}
}
}
private bool ProcessRequest(int offset, int count, ref string header)
{
try
{
if (m_url == null)
{
#region 分析QueryString 和 Url
if (count < 6)
{
return false;
}
int index1 = offset + 4;
if (m_buffer[index1] != '/')
{
return false; //POST is not supported yet
}
int index2 = -1;
int index3 = -1;
for (int i = offset + count - 1; i != offset; i--)
{
byte c = m_buffer[i];
if (c == ' ' && index3 != -1)
{
index3 = i;
}
if (c == '?')
{
index2 = i;
break;
}
}
if (index2 == -1)
{
m_queryString = string.Empty;
if (index3 == -1)
{
m_url = string.Empty;
}
else
{
m_url = FrameworkConfig.Encoding.GetString(m_buffer, index1 + 1, index3 - index1 - 1);
}
}
else
{
m_url = FrameworkConfig.Encoding.GetString(m_buffer, index1 + 1, index2 - index1 - 1);
m_queryString = FrameworkConfig.Encoding.GetString(m_buffer, index2 + 1, index3 - index2 - 1);
}
#endregion
return true;
}
else if (header == null)
{
return false;
}
switch (header.ToLower())
{
case "if-match":
case "if-none-match":
m_requestEtag = FrameworkConfig.Encoding.GetString(m_buffer, offset, count);
break;
case "if-unmodified-since":
case "if-modified-since":
m_ifModifiedSince = FrameworkConfig.Encoding.GetString(m_buffer, offset, count);
break;
case "connection":
string keepAlive = FrameworkConfig.Encoding.GetString(m_buffer, offset, count);
m_server.ReportError("KeepAlive:" + keepAlive);
break;
case "ticket":
m_ticket = FrameworkConfig.Encoding.GetString(m_buffer, offset, count);
break;
}
header = null;
return true;
}
catch (Exception e)
{
Log.Critical.WriteError(e);
return false;
}
}
private void ProcessExecute()
{
try
{
m_output.AddLast(STATUS_OK);
m_output.AddLast(HEADER_CONTENT_TYPE_HTML);
m_output.AddLast(HEADER_CONNECTION_KEEP_ALIVE);
m_output.AddLast(HEADER_CONTENT_ENCODING_DEFLATE);
m_output.AddLast(HEADER_CONTENT_ENCODING_LENGTH_0);
m_output.AddLast(HEADER_SERVER);
m_output.AddLast(HEADER_END);
//TODO Execute the real service....
}
catch (Exception e)
{
Log.Critical.WriteError(e);
m_server.ReportError("Execute error");
m_output.Clear();
m_length = 0;
m_output.AddLast(STATUS_ERROR);
m_output.AddLast(HEADER_CONTENT_TYPE_HTML);
m_output.AddLast(HEADER_CONNECTION_CLOSE);
m_output.AddLast(HEADER_CONTENT_ENCODING_DEFLATE);
m_output.AddLast(HEADER_CONTENT_ENCODING_LENGTH_0);
m_output.AddLast(HEADER_SERVER);
m_output.AddLast(HEADER_END);
m_shutdown = true;
}
this.ProcessSend();
}
internal void Start(Socket socket)//Here is the main workflow portal, the server accept a socket and send to this method to process request
{
this.m_socket = socket;
this.m_args.AcceptSocket = socket;
m_shutdown = false;
this.NextWorkflow();
}
private void NextWorkflow()
{
m_url = null;
m_ticket = null;
m_ifModifiedSince = null;
m_queryString = null;
m_requestEtag = null;
m_output.Clear();
Log.Debug.Write("Receive next request:" + m_socket.RemoteEndPoint);
this.m_output.Clear();
if (!m_socket.ReceiveAsync(this.m_args))
{
Args_Completed(null, m_args);
}
}
}