自己写个IIS玩-协议解析篇2011-07-26 博客园 蛙蛙池塘这里不是说用System.Web.Hosting.ApplicationHost和System.Net.HttpListener做的那种web server ,而是直接用socket api做一个简单的能收发HTTP包的网络服务器,当然也不会完全实现RFC 2616,主要 学习探索用。我们先来看HTTP协议解析部分,做一个HTTP协议栈-HttpStatck,大概看一下HTTP协议基础,1、消息头和消息体中间用两个
(0x0d0x0a)来分割,2、消息头之间用
分割,3、消息头的个数不定,但有最大数,4、消息体的大小根据Content-Length头来确定,5、消息头的名字和值用英文半角冒号分割6、消息头的第一行用来标识协议是request还是response,及协议的版本,请求的方法,应答码,应 答描述协议了解了,协议栈就好写了,如果我们能一次读取一个完整的包,那我们把整个包读出来,解析成 字符串,然后用IndexOf,Split等函数很快的就能解析出一个个都HttpRequest和HttpResponse,但是真是 的网络中,你可能只能解析到半个半个多包,没准连消息头的第一行都分两次才能接受到,甚至像一个中 文字符也有可能会收两次才能包才能解析成字符串。我们要想提高效率,尽量避免把bytes解析成字符串 ,另外我们只解析出header给上层应用就行了,body的话暴露成一个Stream就行了,因为你不知道Body的 格式,由应用去做处理吧,asp.net也是这样的,有对应的InputStream和OutStream。下面是具体的性能方面的分析。1、在Stack收到异步读取的网络包后,首先继续调用BeginReceive方法,然后再解析收到的包,这是 为了防止在解析包的时候出错,或者线程挂起而造成无法接受剩下的包,当然每次尽量多读取一些字节, 读取次数多也会降低性能,buffer可以设置的稍微大一些,这个可能要经过具体平台的测试才能确定最合 适的值。这点有不同意见,说不要在刚收到异步读取回调后就先BeginReceive,应该把包收完再 BeginReceive,否则如果本次没收完包,剩下的包只能在其它的IOCP线程里接收,影响性能,这个我不确 认,但是一次接受完缓冲区的所有数据是可以做到的,用Socket.IOControl(FIONREAD, null, outValue) 或者socket.Available可以获取接受缓冲区有多少数据,然后把这些数据收完;但是微软反对使用这些方 法去探察socket的接受数据大小,因为执行这个方法系统需要内部使用锁锁定数据计算这个值,降低 socket效率。关于接受包这里的最佳实践,欢迎大家讨论。2、按理说收到包后先放队列里,再调用解析包方法,解析包的方法顺序从队列里取包解析,但解析包 和接受包可以都在一个线程里,没有必要引入单独的解析包线程,最后还是考虑不使用队列,每次直接把 收到的字节数组进行解析。原则是我们尽量让一个线程只适用本线程的私有数据,而不去用全局共享的数 据,如果要使用别的线程的数据,就给那个线程发个消息,让那个线程自己去处理自己线程的数据,而不 要直接操作不属于自己的数据,那样的话那个数据就得用加锁之类的线程同步了。线程模型的确定很重要 。