`

IOCP知识点及疑惑

    博客分类:
  • C++
阅读更多
开心codinghttp://www.52iocp.com/
1. IOCP发送大数量的问题











2. IOCP发送大数量的问题
有A,B两块数据,如AB两块数据,如果A数据比较大,异步只发送了一部分就返回了,B数据已经提交,¬这时候再发A剩下的部分就乱顺序了 ,该如何处理。
所有重叠操作可确保按照应用程序投递的顺序执行. 然而, 不能确保从完成端口返回的完成通知也按照上述顺序执行". 由此可见, "操作的执行"和"操作结果的通知"这二者的顺序并不能保证完成一致. 在这种情况下, 在这种情况下, 为每个客户端单独作一个发送队列, 有利于进行发送控制. 对于同一个客户端而言, 前一次发送的结果没有返回之前, 针对于同一socket, 不再发后续数据.当WSASend的时候,你的应用需要把应用缓冲提交给 socket缓冲,然后系统会把socket缓冲提交给TCP缓冲。这就算是一次完成的WSASend过程。 当你的应用把应用缓冲提交给 socket缓冲成功后(注意,这个时候,应用缓冲并没有提交到TCP缓冲),这个时候,Get...就成功回收了(10M)。 你Get..回收成功了,并不代表你的所有的数据都发送出去了。可能他们都在TCP缓冲里(TCP缓冲也是有个最大值的,并不是提交任何大都可以,你可以尝试把¬10M提高继续测试下)。 IOCP是要么提交成功要么提交失败,所以不用考虑发送出去半个包的情况。如何知道实际发送的数据量,这个拍拍脑袋就能想到办法,
接收方收到发送方的包回一个确认包就可以了,发送方收到确认包以后就知道成功发送的数据量。在发送方, 如何知道已经发了多少数据量, 还是依靠GET函数的返回数比较准确. 这个数字, 本身也是来自于底层TCP通信时的协议操作结果.我的意思是制订一个通讯协议以后就可以拆包和组包, 因此在收到一个完整包以后就可以给发送方发包确认。

2. IOCP:GetQueuedCompletionStatus¬返回后,完成事件处理时间相当长。

感觉单独用线程处理请求要好一些,但是从我刚才性能分析来看 在通常情况下I/O线程和处理线程合并起来性能要好一些,同时处理耗时操作的线程比单独用来处理耗时操作的线程个数要多,所以性能当然要好一些。
但是,带来一个负面问题,本来我使用一个队列和一个线程来处理数据包,这样在游戏的逻辑部分不存在线程同步的问题。 如果将处理过程放进GET线程的话,就变成N多个线程处理数据包了,虽然在socket的读取这层性能提高了,但是当数据包到达逻辑层后,
还是需要频繁的加锁的阿~同样是有消耗的,甚至消耗会更大。
IOCP有多个get线程,这些线程互不相关,只负责处理socket上的读取,以及读取后针对不同的session进行拼包。 这个过程不需要做任何的同步锁定。 当拼包完成后,即获取到了一个完整的逻辑包时,将其插进处理队列,这里队列需要锁定一次。 然后,有一个单独的处理线程,从队列中取逻辑包出来处理,这个线程即逻辑层的单线程。 这样做的好处是,尽量将没有逻辑意义的处理放进多个线程处理,将最后的结果(即需要逻辑处理的东西),交给单线程处理。 以最大限度的缩短单线程处理数据包的时间。 关于多线程还是单线程, 这个问题的争论由来已久. 总体而言, 我们需要在这两者之间作个权衡, 我比较赞同的观点是: 主逻辑线程只有一个, 如果主逻辑线程中存在耗时较长的逻辑, 则想办法分离, 分离的方法其中之一就是看这部分逻辑能否并行处理, 如果可以, 就在这里作"分阶段"的多线程. 主逻辑线程为多个时,带来的是调试和纠错等相关的时间开销. 这类服务器的业务逻
辑I/O负载比较大,根本无法单线程处理。在这种环境下开多个GET***
就比通过队列单线程来处理业务逻辑要高效的多。
我认为调用Get***和处理在同一个线程也没关系,只要开个线程足够多,比如10个Thread。
比你开4个Get**线程,6个处理线程的效率肯定要高,因为线程间要通过队列来传递数据,
如果10个线程都是Get***,没有这部分内存拷贝(或者内存池分配释放)和数据加锁的开销。
效率当然要高。1其他请求会排队的。 2,应该是得到事件。 3, WINDOWS会跟踪状态。一个线程阻塞了,其他线程会得到 时间处理。
现在的做法是每次调用wsarecv时投递下去的缓冲区长度就是下次希望收到的完整数
据包的长度, 每次GetQueuedCompletionStatus返回后检查缓冲区是否填满,若没填满则继续下次 GetQueuedCompletionStatus调用, 若填满了就调用数据处理流程。

结论: 同步的操作和不费时的操作,尽量放在多线程中执行,需要异步的,比如数据库操作,可以用单独的线程处理(队列),也可以开多个工作线程来处理。

3. IOCP投递数据包顺序的问题:
问题一:
在一个连接上,我投递一个WSASend发送1K的数据出去,这时候下面的缓冲区里却只剩下512字节的空间,这时候,内核就会把这1K数据里的512字节拷贝¬到缓冲区里发出去,然后在GetQueueCompleteStatus里返回TRUE,并且在参数里指明只发送了512字节,这时候我们需要继续发送剩下的5¬12字节才能保证把整个1K的数据发送完成,如果在GetQueueCompleteStatus返回后,我们继续发送剩下的512字节的这一个间隙,,刚好另¬外一个线程投递了一个WSASend发送1K的数据,而这时候下面的缓冲区空了,这1K的数据将被完全发送出去,然后内核才调度到我们继续发送剩下的512字节¬的这个线程,那么在客户端收到的数据就成了这样: 512字节+1K字节+512字节,而实际上我们希望客户端收到的数据是: 1K(第一次发送的)+1K(第二次WSASend)发送的。 同样,如果多次在一个连接上调用WSARecv,也会出现数据错乱的问题。 使用异步IO的时候,最好能保证上一个IO彻底完成后才继续发出下一个IO。
答:一次只投递一个。在带宽不够的情况下,完成端口没有完成投递给他的-工作就返回了完成包.
问题二:
事情是这样的,网络层使用IOCP,局入网内7千链接,每条链接上每秒平均收发1K数据没问题(每个包的大小小于1K) 当放到外网上,外网服务器是2M带宽独占的,2000个链接,每链接上每秒发送150个字节(已经超过2M的带宽了),IOCP出现数据没有发送完全,却返回了¬完成包的情况,这个时候查看这条链接上有将近100个包还没有发送出去,不知道大家遇到过这样的情况没有,是不是IOCP的正常情况,有什么解决办法

iocp只是把数据重新拷贝到tcp的缓冲中,返回包中的WSABUF结构中的长度是说明了有多少数据拷贝到tcp的缓冲区中,上层根据这个来决定¬哪些数据需要重新发送。

答:iocp只是把数据重新拷贝到tcp的缓冲中,返回包中的WSABUF结构中的长度是说明了有多少数据拷贝到tcp的缓冲区中,上层根据这个来决定¬哪些数据需要重新发送。完成端口是一种通知机制,你向系统提交一个发送请求,之后就不用管了,系统会维护好数据发送的状态,然后数据全部发送(tcp缓冲区大于或等于要发¬送的数据)或部分发送之后(tcp缓冲区小于要发送的数据),通知你,这样做的性能提高在于事情是系统去做,系统能够进行最大的优化,比你自己维护要高效多了。
像你所说的,向iocp提交多次请求,一般很少这样做,一般都是提交一次,完成之后再提交,多次提交可能会出问题的,系统不保证多次提交的操作的序列话。

关闭缓冲区只是对于一些对实时要求很高的情况才会关闭,而且即使关闭,TCP那边还是有缓冲,因为有可能丢包需要TCP重新发送。解决办法很简单,那就是一发一收,收到上个包的回复以后发送下一个包。我的理解是WSASend调用返回,但是WSABuf提交的buffer被锁住,直到Get***返回,这个时候buffer被解锁定。 而不是底层收到ack以后WSASend返回。

假设当前一个tcp连接,底层缓冲还剩余100个字节,这个时候你首先提交一个发送操作,发送1000个字节,然后再提交一个操作¬,50个字节, 能是收到两次发送50个字节的完成通知?
应该是收到一个1000字节返回,和50字节返回的通知,而不是两次50字节的通知。TCP缓冲不够,1000字节发送了100字节出去,Get**不返回,后面提交的100字节请求Get**也不返回,直到socket
buffer有空,即使socket buffer有空,也会先COPY那1000个字节剩下的900字节再COPY后提交的100字节,而不会先COPY后来提交的100字节,关于这个,WIND¬OWS网络编程上有介绍,但是Get返回的两次提交的顺序是没有保证,不保证前提交的请求完成通知先返回。讨论结果就是Get**返回只说明数据COPY到AFD缓冲区里,至于是否发送出去看运气了。假设你刚get**成功,然后网线断开了那就Get**再次返回出错或者你下次调用WSASend/WSARecv出错, TCP有多种机制来控制发送的速度不会超过接收方的处理能力导致网络上大量的包涌塞,滑动窗口机制应该是其中之一。客户端的处理能力也会导致发送方的¬阻塞,不单单是网络带宽。





4. 消息如何分发
象消息分发,如果使用OO特性的调用,是很消耗时间的,但是我们可以在很消耗时间的地方进行优化,函数调用不用进行寻址,直接运行。可以象下列方式
class CMsg
{
int type();
void run(command*);
}
class CMsgMan
{
CMsg::run runlist[10240];
//先注册进来
AddMsg(Msg* pMsg)
{
  runlist[pMsg->type]=pMsg->run;
}
//消息分发
void runCommand(type,command* pCommand)
{
  runlist[type](pCommand);
}
}
大规模的开发中需要分很多层次来做,各个模块尽量独立,相互之间的接口尽量简单,在需要效率的地方进行优化。其实模块独立了,互相之间很少依赖,这也是达到了O¬O的要求


5. 关于完成端口中套接字的关闭问题
想请教一下,如何主动关闭一个关联到完成端口的连接.非常感谢

不管在哪个线程里closesocket之后,GET函数都会返回,RECV失败的事件,表示这个socket断开了。 你可以在这个事件里处理释放对应的session资源,而不是closesocket之后立即释放。closesocket后,GetQueuedComplitionStatus会返回错误,你的某个worker thread会被激活,在这个线程中调用GetLastError,系统会告诉你出错的原因。不要在关闭socket的线程释放与这个socket关联的per io data,而是应该在被唤醒的线程中释放。
我想这里面的焦点问题在于投递的IO请求完成后都是放在一个 completion packet 队列里面等待 GetQueuedCompletionStatus 取出的。你在关闭 socket A 的时候,completion packet
队列里面可能还有和这个 socket A 有关的 completion packet 。如果你在关闭 socket A 的时候把和 socket A 有关的资源,比如session,都释放了,那以后再处理队列里面和 socket A 有关的completion packet的话肯定会出错了。你在关闭 socket A 之后并不要马上把相关资源都释放了,要等到所有的 socket A有关的completion packet 处理完,其中GetQueuedCompletionStatus 返回的最后一个和 socket A 相关的 completion packet 会提示出错,也就是函数的返回值是 ERROR_SUCCESS ,同时 dwNumberOfBytesTransferred 为0。这个时候就可以放心大胆地释放相关的资源了。


6. 数据封包、拚包时遇到的一个简单的问题
基本格式是:前两个字节为逻辑包的大小 + 数据包的类型 + 数据包的内容。 问题是:在正常情况下,包的前两个字节是逻辑包的大小。 但是假如我到了一个数据包,数据包的内容是 "abc". 这个包的前两个字节并不是正常逻辑包的大小信息,在这种情况下,我应该如何判断包的前两个字节是否表示的是逻辑包的大小信息?

不用再另外加包尾标志, 有逻辑包长度信息, 就已经暗含结尾是在何处. 反过来说, 即使你加了这个结尾标志, 我还是可以给你设计一个非法的结尾标志, 所以, 增加结尾标志没有任何意义. 这种情况下, 只能根据你收到的逻辑包大小来收剩下的内容, 由于TCP流传输的特点, 你没法判定后面还会不会给你发剩下的包, 所以, 接收操作是比较被动的, 尽管这个内容可能并不是你想的合法的内容.  而这个包的具体格式和数据内容到底非法还是合法,  并不由网络层来处理, 交给逻辑层处理会更为合适.
对待恶意攻击, 你的网络底层应该足够强壮, 主要是一些边界值处理好就行了, 比如:当确定的逻辑包长度小于你指定的逻辑包最小长度时, 这个包明显是错误的, 要丢弃或关闭当前连接; 同理, 如果你定义了逻辑包最大长度, 那这个边界值也要检验. 网络层只负责处理这些边界值并负责把属于边界值以内的逻辑包接收下来即可, 具体的包内容判断交给上层去作.
逻辑层对每一个解析后的数值进行边界检查是肯定必须的。不过不一定是边界检查,只要是逻辑上正确就行了。 例如client发来一个人物ID(Login时选人),不论这个值是什么,只要这个ID不存在,或这个这个ID不属于这个账号,就是错误的。

传奇用的是字符串协议,用分隔符, 先把2进制数据用类BASE64算法处理一下,然后加上分隔符,
这样即使出现错误的包,只要拆包算法写的好,有重新定位功能,就可以继续收下一个包。如果是2进制
协议,里面的长度字段错误后面,而后面恰好没有发全一个包, 那么整个次序就乱了,因为后面发的半个包有可能和下一个包叠在一起,最 坏情况下完全乱掉。服务器与服务器之间的通讯采用2进制协议,依靠包头的封包长度解包, 而客户端与服务器之间的通讯,采用的是base64编码的方式,通过一个包头字符与一个包尾字符解包。带包对和包尾标志的协议会比只带包长度的协议在重新定位方面表现要好得多,
只带包长度的, 确实如无忌所言容易造成后面的包也连在一起, 而它的重新定位就可能是错了N个包之后的了, 带包头和包尾的却可以只影响当前包.
每一个通信包,都设计一个结构,然后通信时直接把结构发送出去就行了,解包则直接根据包类型强制转换成各种相应的结构。

7. lpCompletionKey问题
GetQueuedCompletionStatus 函数的
PULONG_PTR lpCompletionKey
是用来存放Socket相关的数据的,一般是一个
指针,里面存放 SOCKET 等数据
在我现有的一个服务器中,这个值是一个DWORD 整形值,这个DWORD充当索引,SOCKET相关的数据是查 map 得到的,也就是说每次 Send,Recv 都要访问这个map,通过这个DWORD来查到SOCKET
等数据,然后操作
Send(DWORD dwPerSockID, memblock block, size_t nLength){
    SOCKET socket = 0;
    m_mapPerSocket.find(dwPerSockID, socket);
    WSASend(socket, DataBuf, 1, &NumBytes, 0, &PerIOData->Overlapped,
0);
    ...
}
ThreadProc(void* lpParam){
    DWORD nSocketID = 0;
    GetQueuedCompletionStatus(m_hIOCP, &NumBytes, &nSocketID,
&lpOverlapped, INFINITE);
    ....
    OnSend(nSocketID, PerIoData, NumBytes);
}

答:设计的不合理,不需要map查找。

GetQueuedCompletionStatus函数的原型如下:
  WINBASEAPI
  BOOL
  WINAPI
  GetQueuedCompletionStatus(
      IN  HANDLE CompletionPort,
      OUT LPDWORD lpNumberOfBytesTransferred,
      OUT PULONG_PTR lpCompletionKey,
      OUT LPOVERLAPPED *lpOverlapped,
      IN  DWORD dwMilliseconds
     );
  其中,我们把第三个参数lpCompletionKey称为完成键,由它传递的数据称为单句柄数据。我们把第四个参数lpOverlapped称为重叠结构体,由它传递的数据称为单IO数据。

  以字面的意思来理解,lpCompletionKey内包容的东西应该是与各个socket一一对应的,而lpOverlapped是与每一次的wsarecv或wsasend操作一一对应的。

  在网络模型的常见设计中,当一个客户端连接到服务器后,服务器会通过accept或AcceptEx创建一个socket,而应用层为了保存与此socket相关的其它信息(比如:该socket所对应的sockaddr_in结构体数据,该结构体内含客户端IP等信息,以及为便于客户端的逻辑包整理而准备的数据整理缓冲区等),往往需要创建一个与该socket一一对应的客户端底层通信对象,这个对象可以负责保存仅在网络层需要处理的数据成员和方法,然后我们需要将此客户端底层通信对象放入一个类似于list或map的容器中,待到需要使用的时候,使用容器的查找算法根据socket值找到它所对应的对象然后进行我们所需要的操作。

  让人非常高兴的是,完成端口“体贴入微”,它已经帮我们在每次的完成事件通知时,稍带着把该socket所对应的底层通信对象的指针送给了我们,这个指针就是lpCompletionKey。也就是说,当我们从GetQueuedCompletionStatus函数取得一个数据接收完成的通知,需要将此次收到的数据放到该socket所对应的通信对象整理缓冲区内对数据进行整理时,我们已经不需要去执行list或map等的查找算法,而是可以直接定位这个对象了,当客户端连接量很大时,频繁查表还是很影响效率的。哇哦,太帅了,不是吗?呵呵。

  基于以上的认识,我们的lpCompletionKey对象可以设计如下:
  typedef struct PER_HANDLE_DATA
  {
    SOCKET socket;             //本结构体对应的socket值
    sockaddr_in addr;          //用于存放客户端IP等信息
    char DataBuf[ 2*MAX_BUFFER_SIZE ];  //整理缓冲区,用于存放每次整理时的数据
  }

OVERLAPPED是应用层与核心层交互共享的数据单元,如果要执行一个重叠IO操作,必须带有OVERLAPPED结构。在完成端口中,它允许应用层对OVERLAPPED结构进行扩展和自定义,允许应用层根据自己的需要在OVERLAPPED的基础上形成新的扩展OVERLAPPED结构。一般地,扩展的OVERLAPPED结构中,要求放在第一个的数据成员是原OVERLAPPED结构。我们可以形如以下方式定义自己的扩展OVERLAPPED结构:
  typedef struct PER_IO_DATA
  {
    OVERLAPPED ovl;
    WSABUF           buf;
    char                    RecvDataBuf[ MAX_BUFFER_SIZE ];   //接收缓冲区
    char                    SendDataBuf[ MAX_BUFFER_SIZE ];   //发送缓冲区
    OpType              opType;                                                       //操作类型:发送、接收或关闭等
  }
  
  在执行WSASend和WSARecv操作时,应用层会将扩展OVERLAPPED结构的地址传给核心,核心完成相应的操作后,仍然通过原有的这个结构传递操作结果,比如“接收”操作完成后,RecvDataBuf里存放便是此次接收下来的数据。

个人理解: 其实lpCompletionKey里可以包含socket 和OVERLAPPED信息。这样在投递的时候,绑定的时候, 直接丢指针下去:
hTemp = CreateIoCompletionPort(pClientContext ->m_hSocket(), hPort, (DWORD) pClientContext, m_dwConcurrency);

UINT nRetVal = WSASend(pClientContext->m_Socket,
pClientContext->m_pSendBuffer->GetWSABuffer(),
1,
&dwIoSize,
ulFlags,
&pClientContext->m_pSendBuffer->m_overlap,
NULL);
至于用什么客户ID(自定义格式)和逻辑层交互,可以自己定义结构体,并直接保存pClientContext指针就行了。




8. server中的定时机制(timer)该怎么做
win下的定时器有下面几种。
1,消息机制的,也就是你说的。
2,多媒体定时器 winmm.h中。
3,队列定时器,queue timer
4,CreateWaitableTimer定时器。
这几种我都用过,感觉queue timer最方便。
CreateTimerQueueTimer() win32 api


9. WSASend的时候,是每个客户端一个发送列表,还是所有的客¬户端一个发送列表?
我以前的是逻辑处理完后,就直接WSASend,当然,这样做,需要同步(如果有两次提起WSASend,必须等待第一次WSASend完成),如果发送不成功¬的,需要再次提起WSASend。 我发现这个同步可能是个效率问题。 现在想使用单独的一个发送线程来发送。
1 每个socket都有一个自己的发送队列。 当逻辑处理完后,把需要发送的数据包提交到每个socket的发送队列。(发送队列有n个,和socket的数目相等)
2 所有的socket同用一个发送队列。 当逻辑处理完后,把需要发送的数据包提交到所有的socket的共享的发送队列。(发送队列只有一个) 发送线程就不停地遍历,有发送需求的,就提起WSASend,发送不完全的,再次提起WSASend.

答:完成端口根本没必要维护对列,对与WSASend发出的异步操作操作,由操作系统保持消息队列,由系统保证发送次序。没必要为每个CLIENT的SOCKET维护一个发送队列~
你可以在任何地方直接用WSASend发出一个异步操作就行了,不需要担心同时多步WSASend异步引起的乱序问题,因为有M$的内部保存了异步发送的队列。如果你以同步方式调用WSASend你需要自己保证乱序问题了WSASend可以一次提交多个写操作。
所以,多个提交是没有问题的,但Get...回收的顺序却是不一定的。就是说,你提交的顺序和Get...回收的顺序可能是不同的。
WSASend异步操作的确可以保证每个异步的完整发送,我曾经也为这个问题困扰,但是后来经过多方查阅资料,和自己高密度的测试表明,没有多个线程序并发的发¬出异步WSASend,发送的WSABUF乱序问题。而对于完成端口接受到回应的顺序是不一定的,这点是肯定的,因为完成端口内部本身就不是一个先进先出的队列¬,而是有种类似先进后出的堆栈格式的,也许还有优先级什么的,这点我没有深究。
对此我也觉得不会对程序影响的,反正程序本来就是异步的处理过程,与顺序无关的。

对于完成端口多线程的时候,收包其实也不是乱序的,关键,是线程的执行不确定! 比如线程A收到了包1却没有处理时,线程B收到了包2立即就处理了,这样就会有乱序的问题! 不过也有解决方法,线程A收到了包1后,暂时不投递收包请求, 这样就不会有线程2收到包2了! 不过这个方法串行化了处理, 对于包顺序不敏感的程序,就不要使用这个方法了!



10.UDP丢包的问题
我现在做的项目设计到MPEG4、H.264等视频流网络传输。因为要穿越NAT,所以要用UDP。MPEG4、H.264¬码流如果丢包,就会出现马赛克,严重影响观看效果。重传机制只有在网络条件好的情况下才能完全解决丢包问题。
我测试过在100M局域网内一路3MB/p以上的udp码流会经常丢包,码流越大丢包率越高。
**********但是用抓包软件查看,其实机器已经收到了所有的udp包!!!!!!
我用过以下方法接收3MB/s 数据:
select       平均3minute丢2个包。
WSAAsyncSelect 同上。
50MB/s 数据
select       平均1minute丢10个包。
WSAAsyncSelect 同上。
提高线程优先级可以降低丢包率,但是严重影响其他线程工作。
现在考虑用IOCP是否能显著降低丢包率。
但是在程序阻塞在GetQueuedCompletionStatus()。能否提供一个UDP
SOCKET使用IOCP的例程?
答:你出现的问题是协议底层的问题,和使用什么I/O模型没有关系的。 UDP确实会丢包,你说的3M/s出现丢包是正常的。现在一般的网络视频都是512kbps左右,再高由于网络带宽的问题基本上就没有使用价值。很明显是socket的缓冲暂时满了所以丢包,原因是你的包处理部分太慢了,估计可能做了比较多事情才回来读入下一个包,导致socket缓冲读取不及,如果用¬IOCP,同样会socket缓冲满,唯一不同的是IOCP将要求发送方重发,而UDP则不能。解决办法依据以上,一是提高包处理速度,比如读取包后放入队列由¬别的线程处理,本线程继续读包,一般这样做就已经可解决大多数处理不及,二是设立一个UDP响应机制,比如在UDP包中加入序列号,如果接收的序列号缺少,则发¬送信息到发送方请求重发,这样要改动的地方较多,可以参考rts和rtcp 协议的实现。


11.程序退出和网络断开的区别(检测死连接)

一般是下面2种解决办法。根据具体情况灵活使用。
1,定时向所有用户发送存活包。
2,定时检查全部用户,如果有用户一定时间没有发送有效请求到服务器就关闭连接。

int BX_Acceptor::ChangeSocketModeAfterAccept( SOCKET Socket )
{
        BOOL bError;
        bError = FALSE;
        if ( SOCKET_ERROR == setsockopt(Socket, SOL_SOCKET,
SO_UPDATE_ACCEPT_CONTEXT, (char *)&m_ListeningSocket, sizeof
(m_ListeningSocket)))
        {
                NETWORK_REPORT( "setsockopt is failed. Error code =" );
                NETWORK_REPORT( WSAGetLastError() );
                bError = TRUE;
                goto ErrHand;
        }
        int nKeepAlive = -1;
    int nOptLen = sizeof(nKeepAlive);
        // 第一:获取该SOCKET的KeepAlive设置状态
        if ( getsockopt( Socket, SOL_SOCKET, SO_KEEPALIVE, (char
*)&nKeepAlive, &nOptLen ) == SOCKET_ERROR)
        {
                NETWORK_REPORT( "getsockopt is failed. Error code =" );
                NETWORK_REPORT( WSAGetLastError() );
                bError = 1;
                goto ErrHand;
        }
        // 第二:设置该SOCKET的KeepAlive设置状态
        nKeepAlive = 1;
        if (setsockopt( Socket, SOL_SOCKET, SO_KEEPALIVE, (char *)&nKeepAlive,
nOptLen) == SOCKET_ERROR)
        {
                NETWORK_REPORT( "setsockopt is failed. Error code =" );
                NETWORK_REPORT( WSAGetLastError() );
                bError = 1;
                goto ErrHand;
        }
        // 第三:获取该SOCKET的KeepAlive设置状态
        nKeepAlive = -1;
        if (getsockopt( Socket, SOL_SOCKET, SO_KEEPALIVE, (char *)&nKeepAlive,
&nOptLen) == SOCKET_ERROR)
    {
        NETWORK_REPORT( "getsockopt is failed. Error code =" );
                NETWORK_REPORT( WSAGetLastError() );
                bError = 1;
                goto ErrHand;
    }
        // 第四:设置KEEPALIVE的时间和参数
        if( nKeepAlive == 1 )
        {
                TCP_KEEPALIVE inKeepAlive = {0};
                unsigned long ulInLen = sizeof(TCP_KEEPALIVE);
                TCP_KEEPALIVE outKeepAlive = {0};
                unsigned long ulOutLen = sizeof(TCP_KEEPALIVE);
                unsigned long ulBytesReturn = 0;
                //设置socket的keep alive为10秒,并且发送次数为3次
                inKeepAlive.onoff                         = 1;
                inKeepAlive.keepaliveinterval = 10000;
                inKeepAlive.keepalivetime     = 3;
                //为选定的SOCKET设置Keep Alive,成功后SOCKET可通过Keep
Alive自动检测连接是否断开
                if ( WSAIoctl( Socket, SIO_KEEPALIVE_VALS,
                        (LPVOID)&inKeepAlive, ulInLen,
                        (LPVOID)&outKeepAlive, ulOutLen,
                        &ulBytesReturn, NULL, NULL) == SOCKET_ERROR)
                {
                        NETWORK_REPORT( "WSAIoctl is failed. Error code =" );
                        NETWORK_REPORT( WSAGetLastError() );
                        bError = 1;
                        goto ErrHand;
                }
        }
ErrHand:
        if (TRUE == bError)
        {
                return RET_FAIL;
        }
        return RET_SUCCESS;
1. LAN环境,也就是数据不依靠路由或者是使用NAT之类的网络环境。
    在这种情况下,TCP是不会失效的,因为在其协议实现中,就有保持连接的控制。在客户端和服务器不主动断开的情况下,不会断开,这里指的不会断开,是说在Kee¬palive的有效时间之内,一般情况下,KeepAlive的时间都会很长的。
2. 使用了地址映射的环境,比如常见的从内网通过NAT访问公网服务器的这种情况。
    由于为了节约网络端口资源,所以路由器会在一个链路长时间没有有效数据的情况下,断开这条空闲链路的。通常情况下,路由或者防火墙,检测链路有效的时间间隔要远¬比keepalive的时间短。



12. Server能接收多少TCP连接的问题
如果大多数连接都在睡觉,不如用短连接,有需要的时候再连接上来,比如HTTP。
或者用UDP,自己来做流控,支持个10W用户单机问题不大。
如果需要大量的并发连接,不如换种策略,在应用服务器前面,架上多台网关服务器。
由多台网关服务器将无数连接的数据转发到应用服务器。如果有10台网关,应用服务器也就只需要维护10个连接,局域网内和网关通信,速度可以不考虑。 而每台网关平均2K个连接,那么就2W个并发连接。
而且网关服务器,可以做一些简单的数据过滤,将不同内容的数据,转发到不同的应用服务器。

SOCKET句柄数,一个是port(端口)数,后者65536,代表本机可以在多少个端口上对外部访问进行监听。

13. IOCP资料释放的问题
在GetQueuedCompletionStatus处理循环以外关闭,
你只需要closesocket就行了。
之后GET函数会发现socket被关闭了,然后返回事件给你,你再释放相应的资源。
通过closesocket()主动关闭套接字以后,不能立即释放相关的资源,应该在所有的操作都被GetQueuedCompletionStatus()响¬应之后才能释放。

14. 关于"登陆服务器"的架构设计
为了同时可容纳很多的人"登陆",我在"登陆服务器"中使用IOCP连接和接受客户端请求,单独开一个线程接受"数据库服务器"的数据,同 时又有几个socket连接着"大厅服务器"。 现在的问题就是:在这个"完成端口"的线程池处理函数中,只需要recv一次客户端的信息。而我每次有客户端连接的时候。都要new全局的"句 柄结构"和"数据结构体",把它和IOCP连接起来。等受到了消息后,就通过和"数据库服务器连接的socket"法给"服务器服务器"。这样子,我感 觉没有合理的运用iocp,并贴会new很多的"结构体",然后只能在返回给客户端消息后delete .这样子程序很快就没空间可分配了,尽管我只用 了"对象池"

个人感觉登陆服务器有必要用完成端口或者EPOLL,
1.因为登陆服务器是并发连接多.
2.单连接通讯数据量小。
3.用户不介意等个几秒。
我做过的loginserver并发连接都比较高,经常5k并发连接以上。
我一般的做法是前面2*CPU个数的iocp线程,后面8~12个db线程。基本上够用了。

你的问题是没有做拥塞控制,接受连接的吞吐量超过了后端数据库IO的吞吐量,导致一段时间以后累积了很多后端来¬不及处理的连接,你又不控制拥塞导致内存耗尽。并发连接数意义不大,主要看你的每秒最大能处理的登陆请求数,如果你的后端最大每秒只能处理3000个请求,而你接受10000多个连接在用户层就没什么意义,¬还不如缓冲在网络层,网络层缓冲满了系统就开始拒绝连接,这可以防止风暴连接的时候把服务器压垮。我从来都是单线程non-blocking
select,轮询一遍5000个socket是用不了多少时间的(大约几个ms),,登陆没必要提供很快的响应时间,你可以间隔上百ms才轮询一次,在收到一¬组用户数据后再批量传送给数据库验证,这里可以用线程池来提高IO使用效率。
至于连接则由网络层负责缓冲,你可以把缓冲区设置的大一些改善连接吞吐量,比方说我每隔50ms查询一遍侦听端口,连接缓冲设置为100,这样理论上说我每秒最¬多可以接受2000个新连接,缓冲区满了网络层就不再响应TCP握手消息了,这样最多让用户收到个连接超时的错误,对服务器来说不会造成很大的开销,反之你不加¬控制一股脑全连接进来又来不及处理,时间一长应用层累积了大量未处理连接可能导致资源耗尽,如果遇到DDOS攻击那么情况就更严重了。一旦接收到用户登陆数据就不再需要读取了,当然没必要再select了,一般来说连接建立到select会有一定的间隔时间,这点时间足够用户把登陆数据传¬过来了,因此第一次select的时候大部分情况有数据可读。对于长时间没有数据可读的连接做超时处理就可以了。至于DDOS么,最低要求是在被攻击的时候不应¬该把服务器压的资源耗尽而宕机,至于是否还能够正常服务,那另当别论。
所以,服务器之间的数据交互,必须都带上给每个客户端分配的唯一标志,当收到另一个服务器传来的针对某一个客户端的数据时,第一件要做的事情,就是查询这个客户¬端是否还在线。
通常我会将所有在线的用户,按ID、名字、或者临时ID,存进一个hashmap中,下线后就删掉,收到其他服务器传来的消息是,先找在这个map中查询一下,¬user是否在线,再做处理。单线程只该应用在复杂逻辑的服务器应用上,比如游戏的场景服务器,那种情况下,每个客户端连接之间,都存在很多需要共享的数据,多线程处理的话,需要大量的线程¬同步操作。

15. 有关容器和容器中的数据项的同步问题
有一个hash表,多个线程从中查找、插入或者删除对象,并对对象进行访问和修改;请问如何对其加锁,使得性能最高呢?

1.使用一个Mutex
  线程的流程大致如下:
  {
    加锁;
    从hash表中获取对象;
    对对象进行访问和修改;
    解锁.
   }
   很明显,这种方案并发性很低。
2. 使用一个读写锁和一个互斥锁
  hash表访问:
  {
    给hash表加读锁;
   从hash表中获取对象;
   加互斥锁;
   访问对象;
   解互斥锁;
   解hash表的读锁;
  }
  hash表修改
  {
   给hash表加写锁;
   修改hash表;
   解锁
  }
3.给hash表加读写锁,并且hash表中的每个对象拥有一个互斥锁;
  但是这样互斥锁的数量会很庞大。
答:你这么做,会导致需要锁两次。 一个线程,将要删除一个对象,它把整个表锁住了。 另一个线程需要修改一个对象,它单独锁住那个对象是没用的,因为另一个线程将要删除它了,所以这个线程也必须把整个表锁住,这样的效率太低了。 还不如直接只在整个表上加一个锁。

我们目前的一个项目中也有类似问题,由于容器使用的是BerkeleyDB,本身可以多线程操作,但它的HASH类型只有表级锁。我的附加要求是多个线程不能同¬时操作同一条记录,所以自己做了一个"锁池",要操作某条记录时,先用这个记录的key来获取一个锁,这时候其它线程如果使用同一个key来获取这个锁会阻塞,¬不同的key就不会阻塞,相当于实现了一个记录锁的功能。由于获取锁的过程也要同步,所以性能可能不高,但如果为每条记录创建一个锁根本不可行,因为记录数有上¬亿条呢。

16. Accept模式调查

事先投递多个 AcceptEx()然后等线程返回, 我是多个线程调用的,我acceptex返回以后,我接受连接以后立刻再投递一个,因为iocp Get**是多线程里
调用,所以应该是多线程里调用acceptex

TransmitFile 和 TransmitPackets 可以通过指定TF_REUSE_SOCKET和TF_DISCONNECT标志来重用套接字句柄。每当API完成数据的传输工作后,就会在传输层级别断开连接,这样这个套接字就又可以重新提供给AcceptEx()使用。

17. 关于完成端口如何判断与客户端失去连接的一些问题
在利用完成端口写服务器时遇到了一些关于服务器如何判断与客户端失去连接的问题,描述如下: 服务器在初始化的时候在池中创建足够数量的PerHandleData 和PerIoData. 使用的时候在池中取即可。
当一个客户端与服务器取得联系的时候在池中取一个PerHandleData. 失去连接的时候把PerHandleData 放回池中,以备其他客户端使用。 当客户端与服务器连接上以后再PerIoData池中取一个PerIoData,用于 提交一个接收操作,提交收的这个PerIoData, 只有在客户端与服务器失去连接的时候才放回PerIoData 池中。服务器在向客户端发送数据的时候也在PerIoData池中取一个PerIoData,用于提交一个WSASend 操作,发送完成以后便将PerIoData放回池中。 不知道这样的结构是否合理,大家指教一下。
用于投递Recv的OV结构不一定要和客户端端对象绑死~ 如果你的拼包操作没有在GET线程做的话,那么可以收到数据时,直接把这个OV结构,插进拼包队列,避免memcpy。 然后重新取一个OV投递下个recv。 不过如果直接在GET线程做的拼包就无所谓了。这个方面比较灵活。在投递后返回非ERROR_IO_PENDING错误的时候,是直接closesocket的。每当client主动断开的时候,GET函数返回的数据长度是0。
socket是关闭的情况下,recv或send出错是很正常的呀。 投递send如果没有一次发完,会返回发送了多少字节,这时可以考虑将剩下的重发,但是我是直接做closesocket处理的。


分享到:
评论

相关推荐

    ### 制造业上市公司高质量发展研究报告(2023年)

    内容概要:报告由中国信息通信研究院发布,旨在评估制造业上市公司高质量发展,强调制造业高质量发展的重要性,并构建了涵盖创新力、竞争力、影响力、贡献力四大维度的评价体系。通过对3500余家制造业上市公司2022年年报数据的综合评估,评选出百强企业。研究显示,百强企业专注主业,半数以上成长为制造业单项冠军;民营企业在盈利效率、创新发展方面表现优异;东部地区引领发展,装备制造业领先,新能源产业呈现爆发性增长。百强企业在科技创新、质效提升、持续增长、稳定就业等方面发挥重要作用,但也存在品牌建设和创新水平差距、领军企业竞争力提升空间、高端领域龙头企业培育不足等问题。 适用人群:制造业企业管理者、政策制定者、投资者及相关研究人员。 使用场景及目标:①帮助企业管理者了解行业发展趋势,提升企业竞争力;②为政策制定者提供决策参考,推动制造业高质量发展;③为投资者提供投资参考,识别优质企业;④为研究人员提供详实数据,助力学术研究。 其他说明:报告建议从重突破促升级、重创新补短板、重质量树品牌三个方面进一步推进制造业企业高质量发展,以加快建设具有全球竞争力的一流企业。

    异步电机无感矢量控制仿真:关键技术和代码实现技巧

    内容概要:本文详细介绍了异步电机无感矢量控制仿真的关键技术与常见问题解决方案。首先讨论了坐标变换(Clarke和Park变换)的基础操作及其注意事项,强调了正确选择系数的重要性。接下来深入探讨了滑模观测器的设计与优化方法,包括使用查表法替代三角函数计算以提高效率,以及加入低通滤波器减少高频抖振。此外,文章还涉及了速度估算的方法,如频域法和改进型滑模观测器的应用,并提供了具体的Python和Matlab代码片段。最后,针对电流环控制提出了前馈补偿机制,确保在突加负载情况下仍能保持良好的电流跟踪效果。文中多次提到调参技巧,特别是对于PI参数的选择给出了实用建议。 适合人群:从事电机控制系统研究与开发的技术人员,尤其是对异步电机无感矢量控制感兴趣的工程师。 使用场景及目标:适用于希望深入了解并掌握异步电机无感矢量控制仿真技术的研究人员和技术开发者。主要目标是在没有编码器的情况下实现对电机转速和扭矩的精确控制,同时提供详细的代码实现指导和调试经验。 其他说明:文章不仅提供了理论知识,还包括大量实际操作中的经验和教训,帮助读者避免常见的陷阱,快速搭建起有效的仿真环境。

    (源码)基于Arduino的火箭动力学参数监测项目.zip

    # 基于Arduino的火箭动力学参数监测项目 ## 项目简介 这是一个基于Arduino平台的火箭动力学参数监测项目,旨在通过Adafruit BMP280压力传感器和Adafruit LIS3DH加速度传感器收集火箭飞行过程中的环境数据和运动数据。项目结合了Adafruit的BMP280库和LIS3DH库,实现对传感器数据的读取、处理及初步分析。 ## 项目的主要特性和功能 1. 环境数据监测通过BMP280压力传感器,实时监测并记录火箭周围的气压、温度和海拔高度变化。 2. 运动数据监测借助LIS3DH加速度传感器,获取火箭在飞行过程中的加速度、速度及方向变化数据。 3. 数据处理与传输Arduino负责收集和初步处理这些数据,然后通过串行通信或其他方式将数据发送到地面站或飞行控制软件。 4. 安全与警报基于收集的数据,项目可设置警报阈值,当超过预设的安全限制时,触发警报或采取相应的安全措施。 ## 安装使用步骤

    (源码)基于Arduino的EPSleepy智能家居控制系统.zip

    # 基于Arduino的EPSleepy智能家居控制系统 ## 一、项目简介 EPSleepy是一个基于Arduino的智能家居控制系统原型。该项目旨在通过Arduino控制ESP32 WiFi和蓝牙板,结合MP3模块、shiftregister和按钮等硬件,实现智能家居的自动化控制。 ## 二、项目的主要特性和功能 1. 自动化控制通过Arduino代码控制ESP32板,实现家居设备的自动化控制。 2. 多种硬件支持支持MP3模块、shiftregister和按钮等硬件,实现音频播放、灯光控制、SD驱动等功能。 3. 模块化设计代码采用模块化设计,方便测试每个部分的功能,方便维护和调试。 4. 图形化界面可通过按钮和LED等硬件进行图形化操作和控制。 ## 三、安装使用步骤 1. 下载并解压项目源码文件。 2. 打开Arduino IDE,导入项目代码。 3. 连接硬件,包括ESP32板、MP3模块、shiftregister和按钮等。

    Delphi 12.3控件之PowerPDF for Delphi11 FullSource.zip

    Delphi 12.3控件之PowerPDF for Delphi11 FullSource.zip

    电动工具领域中微CMS32M5533 800W角磨机方案的硬件设计与反电动势检测算法详解

    内容概要:本文深入探讨了中微CMS32M5533在800W角磨机方案中的应用,涵盖硬件设计和软件实现的关键技术。硬件方面,介绍了三相桥驱动电路、MOSFET选择、电流检测电阻、PCB布局等细节;软件方面,重点讲解了反电动势检测算法、ADC采样时机、PWM配置以及换相时机的动态补偿。此外,还提供了调试技巧和成本控制方法。 适合人群:从事电动工具开发的技术人员,尤其是对电机控制有一定经验的研发人员。 使用场景及目标:适用于希望深入了解电动工具控制系统的设计和优化,特别是希望通过反电动势检测减少霍尔传感器使用的开发者。目标是提高系统的可靠性和性能,同时降低成本。 其他说明:文中提供的代码片段和硬件设计细节有助于实际项目的开发和调试。建议读者结合提供的GitHub资源进行实践,并关注硬件选型和PCB布局的注意事项。

    2004-2023年 上市公司CEO绿色经历

    CEO的绿色经历是指该首席执行官(CEO)在其个人职业发展过程中,所积累的与环境保护、可持续发展、绿色经济等相关的教育背景、工作经验或社会活动经验。 涵盖了教育背景、工作经验、社会活动与个人价值观等多个方面。这些经历不仅塑造了CEO对环境保护和可持续发展的认知和态度,还可能影响他们在企业决策中优先考虑环保因素的程度,从而对企业的长期发展和环境保护产生重要影响。 根据现有研究(姜付秀和黄继承,2013;许年行和李哲,2016),从高管个人简历数据中查找CEO以前是否接受过“绿色”相关教育或从事过“绿色”相关工作,若企业CEO具有绿色经历,Green取值1,否则,取值0。 数据 Stkcd、年份、D0801c、Green、股票简称、行业名称、行业代码、制造业取两位代码,其他行业用大类、当年ST或PT为1,否则为0、样本区间内ST或PT为1,否则为0、金融业为1,否则为0、制造业为1,否则为0、沪深A股为1,否则为0、第一种重污染行业为1,否则为0、第二种重污染行业为1,否则为0、第三种重污染行业为1,否则为0、产权性质,国企为1,否则为0、所属省份代码、所属城市代码、所在省份、所在地级市

    电动汽车18650电池组蛇形液冷系统的COMSOL多物理场仿真与优化

    内容概要:本文详细介绍了利用COMSOL Multiphysics对18650电池组进行蛇形液冷系统仿真的全过程。首先探讨了快充场景下电池过热的风险及其对电动车安全性和寿命的影响。接着,通过集总电池模型简化电化学反应,重点分析了电池产热方程和温度对产热的影响。随后,深入讨论了蛇形流道几何参数优化,如流道宽度与压降之间的非线性关系,以及流固交界面处理方法。此外,还涉及了多物理场耦合求解技巧,包括流场与传热模块的设置,以及后处理阶段的数据提取和可视化。最终得出优化设计方案,显著降低了电池组的最高温度和温度不均性。 适合人群:从事电动汽车电池管理系统设计的研究人员和技术工程师,尤其是熟悉COMSOL仿真工具的专业人士。 使用场景及目标:适用于需要评估和优化电动汽车电池组热管理系统的场合,旨在提高电池组的安全性和使用寿命,同时减少能量损耗。 其他说明:文中提供了大量具体的代码片段和参数设置建议,有助于读者快速上手并应用于实际工程项目中。

    通信领域CCSDS LDPC译码器设计:基于修正最小和算法的C语言与Vivado实现

    内容概要:本文详细介绍了CCSDS LDPC译码器的设计与实现,主要采用了修正最小和译码算法。该算法通过对传统最小和算法的改进,引入缩放因子α,提高了译码性能。文中具体讨论了(8176,7154)和(1280,1024)两种码组的应用场景及其优劣,并展示了如何通过C语言和Vivado进行仿真和硬件实现。此外,文章还探讨了硬件实现中的关键技术,如定点化处理、校验矩阵的压缩存储、动态阈值机制以及硬件流水线设计等。 适合人群:从事通信系统开发的研究人员和技术人员,尤其是对LDPC编码和译码感兴趣的工程师。 使用场景及目标:①帮助研究人员理解和实现CCSDS LDPC译码器;②为实际工程项目提供高效的译码解决方案;③提高译码性能,减少误码率,提升通信系统的可靠性和效率。 其他说明:文章不仅提供了理论分析,还包括了大量的代码示例和实践经验分享,有助于读者全面掌握CCSDS LDPC译码器的设计与实现。

    (源码)基于Arduino的超声波距离测量系统.zip

    # 基于Arduino的超声波距离测量系统 ## 项目简介 本项目是一个基于Arduino平台的超声波距离测量系统。系统包含四个超声波传感器(SPS)模块,用于测量与前方不同方向物体的距离,并通过蜂鸣器(Buzz)模块根据距离范围给出不同的反应。 ## 项目的主要特性和功能 1. 超声波传感器(SPS)模块每个模块包括一个超声波传感器和一个蜂鸣器。传感器用于发送超声波并接收回波,通过计算超声波旅行时间来确定与物体的距离。 2. 蜂鸣器(Buzz)模块根据超声波传感器测量的距离,蜂鸣器会给出不同的反应,如延时发声。 3. 主控制器(Arduino)负责控制和管理所有传感器和蜂鸣器模块,通过串行通信接收和发送数据。 4. 任务管理通过主控制器(Arduino)的 loop() 函数持续执行传感器任务(Task),包括测距、数据处理和蜂鸣器反应。 ## 安装使用步骤 1. 硬件连接

    主角跑步动作素材图包含6张图片

    主角跑步动作素材图包含6张图片

    2003-2023年 企业数字化转型测算结果

    企业数字化转型是指企业或组织将传统业务转化为数字化业务,利用人工智能、大数据、云计算、区块链、5G等数字技术提升业务效率和质量的过程。 当无形资产明细项包含“软件”“网络”“客户端”“管理系统”“智能平台”等与数字化转型技术相关的关键词以及与此相关的专利时,将该明细项目界定为“数字化技术无形资产”,再对同一公司同年度多项数字化技术无形资产进行加总,计算其占本年度无形资产的比例,即为企业数字化转型程度的代理变量。 本数据包含:原始数据、参考文献、代码do文件、最终结果。 参考文献:张永珅,李小波,邢铭强-企业数字化转型与审计定价[J].审计研究,2021(03):62-71. 数据 证券代码、证券简称、统计截止日期、报表类型、无形资产净额、资产总计、年份、期末余额(元)、数字化转型。

    h5py-3.1.0-cp36-cp36m-win_amd64.whl

    该资源为h5py-3.1.0-cp36-cp36m-win_amd64.whl,欢迎下载使用哦!

    QRBayes-LSTM用于Excel数据的多/单变量时序预测及其应用

    内容概要:本文介绍了一种基于QRBayes-LSTM的多/单变量时序预测方法,适用于不确定性强的场景如股票预测和电力负荷预测。该方法结合了分位数回归和贝叶斯优化,不仅能提供未来的趋势预测,还能给出预测值的置信区间。文中详细解释了数据准备、模型结构、损失函数设计、训练配置以及预测结果的可视化和评估指标。此外,还提供了变量重要性分析的方法,帮助理解哪些特征对预测结果的影响最大。 适合人群:从事数据分析、机器学习研究的专业人士,尤其是关注时序预测和不确定性量化的人群。 使用场景及目标:① 对于需要进行时序预测并希望获得置信区间的用户;② 关注模型性能评估和变量重要性的研究人员;③ 寻求提高预测精度和可靠性的从业者。 其他说明:本文提供的代码可以直接应用于Excel格式的数据,用户只需将数据导入即可运行。需要注意的是,为了获得最佳效果,应该确保数据格式正确并且符合特定的要求。

    ADAS系统核心技术解析:ACC、FCW、AEB、LKA的设计与实现

    内容概要:本文详细介绍了ADAS(高级驾驶辅助系统)中四个主要功能模块的设计与实现,分别是自适应巡航控制系统(ACC)、前向碰撞预警系统(FCW)、自动紧急制动系统(AEB)和车道保持辅助系统(LKA)。文章不仅展示了各个系统的具体算法实现,如ACC中的PID控制、FCW中的TTC计算、AEB中的状态机设计和LKA中的PD控制器,还分享了许多实际开发中的经验和挑战,如参数调校、传感器融合、时间同步等问题。此外,文中还提到了一些有趣的细节,如在暴雨天气下LKA的表现优化,以及AEB系统在测试过程中遇到的各种corner case。 适合人群:汽车电子工程师、自动驾驶研究人员、嵌入式软件开发者。 使用场景及目标:帮助读者深入了解ADAS系统的工作原理和技术细节,掌握关键算法的实现方法,提高在实际项目中的开发和调试能力。 其他说明:文章通过生动的语言和具体的代码示例,使复杂的理论变得通俗易懂,有助于初学者快速入门并深入理解ADAS系统的开发流程。

    【高端制造业】2023年中国上市公司行业与区域分布分析:机械制造、电子、电力设备领头沿海地区优势明显

    内容概要:文章主要阐述了2023年中国高端制造业上市公司的发展概况,包括行业与区域两个维度的分布详情。从行业上看,高端制造业上市公司超过2400家,其中机械制造以628家的数量位居首位,电子(352家)和电力制造(336家)紧随其后,而像航空航天国防等也有一定的占比。从区域分布来看,广东、江苏、浙江三省处于领先地位,分别有410家、342家和199家,这表明东南沿海地区对于高端制造业的发展具有显著优势。数据来源于中国上市公司协会以及Wind。 适合人群:对中国经济结构、产业发展趋势感兴趣的读者,尤其是关注高端制造业发展的投资者、政策制定者及研究人员。 使用场景及目标:①帮助投资者了解中国高端制造业上市公司的行业布局,为投资决策提供参考依据;②为政策制定者提供数据支持,助力优化产业布局和发展规划;③供研究人员分析中国高端制造业的现状与未来发展趋势。 阅读建议:本文提供了丰富的数据和图表,读者应重点关注各行业的具体数据及其背后反映出的产业特点,同时结合区域分布情况,深入理解中国高端制造业的发展格局。

    (源码)基于Python的机器学习算法实践.zip

    # 基于Python的机器学习算法实践 ## 项目简介 本项目旨在通过实践常用机器学习算法,提高数据挖掘和推荐系统的准确性,解决信息过载问题。应用场景包括电商、新闻、视频等网站,帮助用户更高效地获取所需信息。 ## 项目的主要特性和功能 数据挖掘实现多种数据挖掘算法,帮助用户从大量数据中提取有价值的信息。 机器学习算法包括常用的分类、回归、聚类等算法,提供详细的实现和示例程序。 推荐系统通过机器学习算法提高推荐系统的准确性,优化用户体验。 ## 安装使用步骤 1. 下载源码用户已下载本项目的源码文件。 2. 安装依赖 bash pip install r requirements.txt 3. 运行示例程序 bash python main.py 4. 自定义数据根据需要替换数据文件,重新运行程序以应用新的数据。

    基于Springboot+Vue的学生选课系统

    项目运行参考:https://blog.csdn.net/weixin_45393094/article/details/124645254 技术栈Springboot+Vue;此项目的参考文档 内容概要:本文档介绍了一款基于前后端分离架构的学生选课系统的设计与实现。系统采用Java语言作为后端开发语言,运用Spring Boot框架构建后端接口,前端使用Vue框架,设计模式上采用了MVVM模式,确保前后端分离。系统主要分为学生、教师和管理员三大功能模块,涵盖课程选择、成绩管理和信息发布等功能。需求分析部分详细描述了各模块的功能需求及性能需求,包括实用性、易用性和安全性。数据库设计部分详细说明了学生、教师、用户、课程和成绩等信息表的结构。系统实现章节则展示了各个模块的具体实现细节,包括登录验证、教师管理、学生管理、课程管理、公告设置及选课等功能的代码实现。 适合人群:计算机专业学生、有一定编程基础的研发人员或对前后端分离技术有兴趣的开发者。 使用场景及目标:①理解前后端分离架构在实际项目中的应用;②掌握Spring Boot与Vue框架结合开发的具体实现方法;③熟悉学生选课系统的核心功能,如选课、成绩管理、信息发布等;④学习如何设计和实现高效的数据库结构以支持系统功能。 阅读建议:本文档适合希望深入了解前后端分离架构及具体实现的读者。在阅读过程中,建议重点关注各模块的功能需求分析和技术实现细节,特别是代码示例部分,以加深对前后端分离架构的理解。同时,结合自身开发经验,思考如何优化现有系统功能,提高系统的稳定性和用户体验。

    基于Transformer的MATLAB分类预测代码详解及应用

    内容概要:本文详细介绍了如何使用MATLAB实现基于Transformer的分类预测,特别针对初学者提供了完整的代码示例和详细的步骤说明。主要内容涵盖数据读取与预处理、Transformer模型搭建、训练配置、结果可视化等方面。文中不仅展示了如何生成分类效果对比图、训练过程曲线和混淆矩阵,还提供了常见的错误排查方法和优化建议。此外,文章强调了Transformer在处理时序特征方面的优势,并给出了具体的光伏数据预测案例。 适合人群:MATLAB初学者、希望了解Transformer应用于分类任务的新手程序员。 使用场景及目标:适用于需要进行数据分类预测的研究人员和技术人员,特别是那些处理时序数据(如光伏数据、电力负荷数据)的人群。目标是帮助读者快速掌握Transformer的基本原理及其在MATLAB中的具体实现。 其他说明:文章提供了大量实用的代码片段和技巧,如自定义位置编码、数据标准化、模型结构调整等,使得整个过程既直观又易操作。同时,作者分享了一些实践经验,如调整参数以提高准确率、解决常见问题的方法等,有助于读者更好地理解和应用所学知识。

Global site tag (gtag.js) - Google Analytics