UDP写游戏服务器

时间:2022-09-14 20:56:19
整个UDP服务器架构, 我设计成 一个recvfrom线程, 一个sendto线程, 剩余就是并行的数据处理线程若干. 采用无锁机制, 线程与线程之间是独立互不影响的. CPU单频越快, 越多, 运行效率也会越好.

最初, 这种设计, 是考虑: 网卡再快, 也不可能会比一个CPU快吧, 然后设计这个单线程收发.
但却被告之: 多线程进行收发效率才会更快. recvfrom可能单线程多线程没什么影响, 但sendto多线程却效果非常明显...想明白原因..为何多线程调用sendto会比单线程全速调用sendto要效率高那么多呢? 明明都是互锁资源...

12 个解决方案

#1


多线程调用sendto相当于多个回应同时进行,效率肯定比单线程高的。

#2


引用 1 楼 yfqvip 的回复:
多线程调用sendto相当于多个回应同时进行,效率肯定比单线程高的。


什么多个回应? 一块网卡, sendto操作同一个socket, 按道理当两个线程进入的时候就会互锁, 只有等一个线程完成操作后才轮到第二个线程处理吧? 这个sendto操作貌似也没有什么IO等待的, 起码单线程死循环的时候, 看到一个CPU核会被占用100%.

#3


你对线程的理解完全错位了。并不是说一旦线程运行就必须完全执行完成这个线程CPU才去做其它的事情,每个线程的运行都是由CPU的时间片决定的,有可能线程执行了一半的时候CPU去做其它事情了,windows核心编程第8章有详细介绍,建议楼主去看一看。

#4


CPU操作网卡是通过总线来操作的,除非你收发2个网卡,不然没用,而且收发使用不同的网卡要自己改TCP/IP协议栈底层(IP层以下的)的代码,一般不要那样做,一般提高网络连接的程序是通过修改IP层代码,主要是方法是预先通知应用层做出动作,减少传输层与IP层的逻辑连路冲突等方法来解决的,像你那样搞也可以但代价不是一般高,而且改协议栈过于底层的东西移植,测试,维护都成问题

#5


引用 3 楼 yfqvip 的回复:
你对线程的理解完全错位了。并不是说一旦线程运行就必须完全执行完成这个线程CPU才去做其它的事情,每个线程的运行都是由CPU的时间片决定的,有可能线程执行了一半的时候CPU去做其它事情了,windows核心编程第8章有详细介绍,建议楼主去看一看。

在线程和CPU核数上面, 可以确保一个线程对应有一个CPU核直接使用的, 而且线程均是无锁的形式, 所以不存在你说的问题的.

而且我想知道的问题, 还是为何sendto被多线程调用会比单线程调用效率要高

#6


引用 4 楼 cutxyz 的回复:
CPU操作网卡是通过总线来操作的,除非你收发2个网卡,不然没用,而且收发使用不同的网卡要自己改TCP/IP协议栈底层(IP层以下的)的代码,一般不要那样做,一般提高网络连接的程序是通过修改IP层代码,主要是方法是预先通知应用层做出动作,减少传输层与IP层的逻辑连路冲突等方法来解决的,像你那样搞也可以但代价不是一般高,而且改协议栈过于底层的东西移植,测试,维护都成问题

按你的说法来说, 你也是认同没有必要多个线程操作同一个socket来sendto是吧? 但实际上却是多个线程调用sendto效率几倍

#7


LZ看来你还不太理解多线程,哪个只是个调度方案,而不必然提高效率,线程本身就占一定的资源开多了占资源,开多了频繁的上下文切换影响系统效率,而你想搞网络优化首先知道些硬件跟OS地层的东西,现在网卡能支持到TCP/IP到什么程度,目前网卡按硬件支持TCP/IP协议栈的程度划分主要分为:1 MAC层网卡(只取得物理连路数据,并对数据做NZ,FM0等编解码的,这个一些ARM处理器自带用CAN总线的EMAC模块差不多),2.IP层网卡(网卡能支持到TCP/IP的IP层,ICMP那些协议网卡已经做了硬件上的支持,PC端的协议栈就不用做IP层以下的工作,而交给网卡做),3.全协议网卡(至少支持到传输层的,PC只需给网卡发数据就行,怎么发网卡搞完), 而网卡跟CPU通讯要么通过总线要么通过间接连接(CPU跟内存的连接方式),读上去的数据要么通过DMA中断要么经过某个FIFO将数据交给操作系统(操作系统驱动做的),一般WINDOWS会虚拟个设备地址映射这些数据,也就是说无论你开多少线程最后都是将数据串行后放到那区域,不然的话发送数据出错甚至蓝屏,所以哪个意义不大,网络数据的吞吐量并没有得到改变,只为你的进程争取了点CPU资源而已,但用多线程是可以提高你上层数据调度的效率,不过不加锁显然是不现实的,如果是WINDOWS的话可以参考IOCP那个多线程异步端口的优化是怎么弄的,也可以看下哪个基于驱动消息驱动的libev框架

#8


没看懂

#9


引用 7 楼 cutxyz 的回复:
LZ看来你还不太理解多线程,哪个只是个调度方案,而不必然提高效率,线程本身就占一定的资源开多了占资源,开多了频繁的上下文切换影响系统效率,而你想搞网络优化首先知道些硬件跟OS地层的东西,现在网卡能支持到TCP/IP到什么程度,目前网卡按硬件支持TCP/IP协议栈的程度划分主要分为:1 MAC层网卡(只取得物理连路数据,并对数据做NZ,FM0等编解码的,这个一些ARM处理器自带用CAN总线的EMA……

我指的提高效率, 只单纯是UDP协议的sendto上面的. 具体可以写一个循环, 调用sendto, 发送一个1024的缓冲, 然后计算每秒可以循环多少次, 得出sendto的单线程效率, 这样死循环只得10万左右. 假如开两个线程来跑, 这样, 不单单两个线程的效率没有下降, 反而是一个线程跑10万, 两个线程跑20万. 按道理最终串行起来, 不论多少个线程, 效率不会像这样提高的.
1024B一个包, 那么1M就有1024个包, 10M就有10240个包, 通常100Mb的流量, 换算后就是12.5MB, 也就是指, 用单一个线程死循环来跑, 还跑不赢网卡的速率(虽然不知道个中过程是怎样), 但开启两个线程后, 又轻而易举的跑赢网速的. 
我是想知道到底有没有必要开多个sendto线程, 只要能够让网卡满荷工作, 单线程多线程都可以. 只是不理解个中原因.

#10


通常100M网卡达不到100M的通讯速度,100M只是个接口速度,而不是最大传输数据速度,100M网卡能满荷25M就不错了,还有你那样算很多都是由于你不了解底层的东西才那样搞,操作系统是定期到硬件映射区取数据的,而这些区域大小都不大,如果你线程写的数据多,刚好系统对不上点没发出去,你的数据就堆在那,甚至丢失,还有如果你是用异步的SOCKET的话,WINDOWS SOCKET有自带线程池给你管理那些东西,不用你自己去搞,那样弄只是你个人觉得效率提高了而已,况且WINDOWS系统HAL层的东西很多都不公开,与其自己花那么多时间搞还不如用自带的,LINUX的也有现成类似WINDOWS IOCP的端口并发处理解决方案,也不用自己搞。

#11


游戏本来就属于现实中的一部分啊

#12


引用 10 楼 cutxyz 的回复:
通常100M网卡达不到100M的通讯速度,100M只是个接口速度,而不是最大传输数据速度,100M网卡能满荷25M就不错了,还有你那样算很多都是由于你不了解底层的东西才那样搞,操作系统是定期到硬件映射区取数据的,而这些区域大小都不大,如果你线程写的数据多,刚好系统对不上点没发出去,你的数据就堆在那,甚至丢失,还有如果你是用异步的SOCKET的话,WINDOWS SOCKET有自带线程池给你管理那……

也就是说你认为多线程得出来的效率是假的, 似乎是快了, 但实际被硬件访问成功的不变, 也就是网卡效率不会变高也不会变低的意思了? 那我还是使单线程收发好了.

对于用不用自带功能或者用其他现成的库, 看需求吧, 现在时间多, 自己开发就可以了. IOCP和EPOLL说得那么神, 其实用起来很麻烦. 所以还是自己开发了.

IOCP, EPOLL这类说其效率高, 很多人的理解上, 只是单纯的收发速度快, 仅此而已. 但作为实际应用的一个服务器, 从接收, 到可读, 然后处理, 最后回复, 这个是一个完整的过程, 效率是要按这种完整, 具有一般性的数据来测试得出的, 很多服务器说什么1K有几万次之类的, 说老实都是废的. 1K的数据, 不具有可比性之余, 也不具有一般性, 1K, TCP就直接变成UDP来用了, TCP和UDP的分别就看不出来了. 1K, 客户端给服务器的会是1K以下, 但服务器给客户端的, 通常就不会是1K, echo 1K根本没用. 网上很多的说法都是有问题的. 

IOCP, EPOLL这类只能够说IO效率很好, 但对应用层来说, 要配合这种高效的IO, 要做很多事情. 按一般情况下, IOCP对于接收完了, 处理, 然后回复, 很简单, 但假如流程上加上取消, 处理进度, IOCP就有点痛苦的(这代表一个连接被两个处理线程并行访问), 假如要提高处理速度, 要使用内存池管理缓冲, IOCP就更痛苦了(貌似用个临界区就可以了, 但效率还是不够的). EPOLL, 虽然机制来说不错, 但没有IOCP那么方便, 同样是什么东西也得要自己写的. 而且假如是采用UDP的话, 我相信什么模型都不会比死循环接收和发送来得更快.

#1


多线程调用sendto相当于多个回应同时进行,效率肯定比单线程高的。

#2


引用 1 楼 yfqvip 的回复:
多线程调用sendto相当于多个回应同时进行,效率肯定比单线程高的。


什么多个回应? 一块网卡, sendto操作同一个socket, 按道理当两个线程进入的时候就会互锁, 只有等一个线程完成操作后才轮到第二个线程处理吧? 这个sendto操作貌似也没有什么IO等待的, 起码单线程死循环的时候, 看到一个CPU核会被占用100%.

#3


你对线程的理解完全错位了。并不是说一旦线程运行就必须完全执行完成这个线程CPU才去做其它的事情,每个线程的运行都是由CPU的时间片决定的,有可能线程执行了一半的时候CPU去做其它事情了,windows核心编程第8章有详细介绍,建议楼主去看一看。

#4


CPU操作网卡是通过总线来操作的,除非你收发2个网卡,不然没用,而且收发使用不同的网卡要自己改TCP/IP协议栈底层(IP层以下的)的代码,一般不要那样做,一般提高网络连接的程序是通过修改IP层代码,主要是方法是预先通知应用层做出动作,减少传输层与IP层的逻辑连路冲突等方法来解决的,像你那样搞也可以但代价不是一般高,而且改协议栈过于底层的东西移植,测试,维护都成问题

#5


引用 3 楼 yfqvip 的回复:
你对线程的理解完全错位了。并不是说一旦线程运行就必须完全执行完成这个线程CPU才去做其它的事情,每个线程的运行都是由CPU的时间片决定的,有可能线程执行了一半的时候CPU去做其它事情了,windows核心编程第8章有详细介绍,建议楼主去看一看。

在线程和CPU核数上面, 可以确保一个线程对应有一个CPU核直接使用的, 而且线程均是无锁的形式, 所以不存在你说的问题的.

而且我想知道的问题, 还是为何sendto被多线程调用会比单线程调用效率要高

#6


引用 4 楼 cutxyz 的回复:
CPU操作网卡是通过总线来操作的,除非你收发2个网卡,不然没用,而且收发使用不同的网卡要自己改TCP/IP协议栈底层(IP层以下的)的代码,一般不要那样做,一般提高网络连接的程序是通过修改IP层代码,主要是方法是预先通知应用层做出动作,减少传输层与IP层的逻辑连路冲突等方法来解决的,像你那样搞也可以但代价不是一般高,而且改协议栈过于底层的东西移植,测试,维护都成问题

按你的说法来说, 你也是认同没有必要多个线程操作同一个socket来sendto是吧? 但实际上却是多个线程调用sendto效率几倍

#7


LZ看来你还不太理解多线程,哪个只是个调度方案,而不必然提高效率,线程本身就占一定的资源开多了占资源,开多了频繁的上下文切换影响系统效率,而你想搞网络优化首先知道些硬件跟OS地层的东西,现在网卡能支持到TCP/IP到什么程度,目前网卡按硬件支持TCP/IP协议栈的程度划分主要分为:1 MAC层网卡(只取得物理连路数据,并对数据做NZ,FM0等编解码的,这个一些ARM处理器自带用CAN总线的EMAC模块差不多),2.IP层网卡(网卡能支持到TCP/IP的IP层,ICMP那些协议网卡已经做了硬件上的支持,PC端的协议栈就不用做IP层以下的工作,而交给网卡做),3.全协议网卡(至少支持到传输层的,PC只需给网卡发数据就行,怎么发网卡搞完), 而网卡跟CPU通讯要么通过总线要么通过间接连接(CPU跟内存的连接方式),读上去的数据要么通过DMA中断要么经过某个FIFO将数据交给操作系统(操作系统驱动做的),一般WINDOWS会虚拟个设备地址映射这些数据,也就是说无论你开多少线程最后都是将数据串行后放到那区域,不然的话发送数据出错甚至蓝屏,所以哪个意义不大,网络数据的吞吐量并没有得到改变,只为你的进程争取了点CPU资源而已,但用多线程是可以提高你上层数据调度的效率,不过不加锁显然是不现实的,如果是WINDOWS的话可以参考IOCP那个多线程异步端口的优化是怎么弄的,也可以看下哪个基于驱动消息驱动的libev框架

#8


没看懂

#9


引用 7 楼 cutxyz 的回复:
LZ看来你还不太理解多线程,哪个只是个调度方案,而不必然提高效率,线程本身就占一定的资源开多了占资源,开多了频繁的上下文切换影响系统效率,而你想搞网络优化首先知道些硬件跟OS地层的东西,现在网卡能支持到TCP/IP到什么程度,目前网卡按硬件支持TCP/IP协议栈的程度划分主要分为:1 MAC层网卡(只取得物理连路数据,并对数据做NZ,FM0等编解码的,这个一些ARM处理器自带用CAN总线的EMA……

我指的提高效率, 只单纯是UDP协议的sendto上面的. 具体可以写一个循环, 调用sendto, 发送一个1024的缓冲, 然后计算每秒可以循环多少次, 得出sendto的单线程效率, 这样死循环只得10万左右. 假如开两个线程来跑, 这样, 不单单两个线程的效率没有下降, 反而是一个线程跑10万, 两个线程跑20万. 按道理最终串行起来, 不论多少个线程, 效率不会像这样提高的.
1024B一个包, 那么1M就有1024个包, 10M就有10240个包, 通常100Mb的流量, 换算后就是12.5MB, 也就是指, 用单一个线程死循环来跑, 还跑不赢网卡的速率(虽然不知道个中过程是怎样), 但开启两个线程后, 又轻而易举的跑赢网速的. 
我是想知道到底有没有必要开多个sendto线程, 只要能够让网卡满荷工作, 单线程多线程都可以. 只是不理解个中原因.

#10


通常100M网卡达不到100M的通讯速度,100M只是个接口速度,而不是最大传输数据速度,100M网卡能满荷25M就不错了,还有你那样算很多都是由于你不了解底层的东西才那样搞,操作系统是定期到硬件映射区取数据的,而这些区域大小都不大,如果你线程写的数据多,刚好系统对不上点没发出去,你的数据就堆在那,甚至丢失,还有如果你是用异步的SOCKET的话,WINDOWS SOCKET有自带线程池给你管理那些东西,不用你自己去搞,那样弄只是你个人觉得效率提高了而已,况且WINDOWS系统HAL层的东西很多都不公开,与其自己花那么多时间搞还不如用自带的,LINUX的也有现成类似WINDOWS IOCP的端口并发处理解决方案,也不用自己搞。

#11


游戏本来就属于现实中的一部分啊

#12


引用 10 楼 cutxyz 的回复:
通常100M网卡达不到100M的通讯速度,100M只是个接口速度,而不是最大传输数据速度,100M网卡能满荷25M就不错了,还有你那样算很多都是由于你不了解底层的东西才那样搞,操作系统是定期到硬件映射区取数据的,而这些区域大小都不大,如果你线程写的数据多,刚好系统对不上点没发出去,你的数据就堆在那,甚至丢失,还有如果你是用异步的SOCKET的话,WINDOWS SOCKET有自带线程池给你管理那……

也就是说你认为多线程得出来的效率是假的, 似乎是快了, 但实际被硬件访问成功的不变, 也就是网卡效率不会变高也不会变低的意思了? 那我还是使单线程收发好了.

对于用不用自带功能或者用其他现成的库, 看需求吧, 现在时间多, 自己开发就可以了. IOCP和EPOLL说得那么神, 其实用起来很麻烦. 所以还是自己开发了.

IOCP, EPOLL这类说其效率高, 很多人的理解上, 只是单纯的收发速度快, 仅此而已. 但作为实际应用的一个服务器, 从接收, 到可读, 然后处理, 最后回复, 这个是一个完整的过程, 效率是要按这种完整, 具有一般性的数据来测试得出的, 很多服务器说什么1K有几万次之类的, 说老实都是废的. 1K的数据, 不具有可比性之余, 也不具有一般性, 1K, TCP就直接变成UDP来用了, TCP和UDP的分别就看不出来了. 1K, 客户端给服务器的会是1K以下, 但服务器给客户端的, 通常就不会是1K, echo 1K根本没用. 网上很多的说法都是有问题的. 

IOCP, EPOLL这类只能够说IO效率很好, 但对应用层来说, 要配合这种高效的IO, 要做很多事情. 按一般情况下, IOCP对于接收完了, 处理, 然后回复, 很简单, 但假如流程上加上取消, 处理进度, IOCP就有点痛苦的(这代表一个连接被两个处理线程并行访问), 假如要提高处理速度, 要使用内存池管理缓冲, IOCP就更痛苦了(貌似用个临界区就可以了, 但效率还是不够的). EPOLL, 虽然机制来说不错, 但没有IOCP那么方便, 同样是什么东西也得要自己写的. 而且假如是采用UDP的话, 我相信什么模型都不会比死循环接收和发送来得更快.