jjzjj

c++ - 服务器/客户端 TCP 异步(winsock)//FD_WRITE 问题

coder 2023-09-19 原文

我需要你的帮助,因为我必须用 C++ 制作两个控制台应用程序:一个客户端能够向服务器发送尽可能多的字符串(以便发送坐标)。我成功地制作了一个阻塞套接字,但是因为我必须在开发平台(3D VIA Virtools)中集成它之后在每个帧调用我的脚本,所以除了使用异步套接字之外我没有其他解决方案。

*我的问题是我只能发送一次字符串,并且在我不再收到 FD_WRITE 之后...*

这开始让我发疯所以任何帮助将不胜感激(我是编程的初学者),在此先感谢所有对我的问题感到有点担心的人

这是我的代码,

服务器

#include <winsock2.h> 
#include <Windows.h> 
#include <conio.h> 

#pragma comment(lib, "ws2_32.lib") 

#define   SOCKET_ERRNO   WSAGetLastError() 
#define ADDRESS "127.0.0.1" 
#define PORT 1234 

static SOCKET ListenFirstFreePort() 
{ 
   struct sockaddr_in addr; 
   int len = sizeof(addr);    
   SOCKET hSocket; 

   // Create socket 
   hSocket = socket( PF_INET, SOCK_STREAM, 0 ); 
   if( hSocket == INVALID_SOCKET ) 
   { 
      printf( "socket() error %d\n", SOCKET_ERRNO ); 
      exit(1); 
   } 

   // Connexion setting for local connexion 
   addr.sin_family = AF_INET ; 
   addr.sin_addr.s_addr = inet_addr(ADDRESS); 
   addr.sin_port = htons (PORT); 

   // bind socket 
   if ( bind( hSocket, (struct sockaddr *)&addr, sizeof(addr)) == SOCKET_ERROR ) 
   { 
      printf( "bind() error %d\n", SOCKET_ERRNO ); 
      exit(1); 
   } 

   // listen 
   if ( listen( hSocket, 100) == SOCKET_ERROR ) 
   { 
      printf( "listen() error %d\n", SOCKET_ERRNO ); 
      exit(1); 
   } 

   return hSocket; 
} 



void main() 
{ 
   WSADATA stack_info; 
   SOCKET ahSocket[2]; 
   WSAEVENT ahEvents[2]; 
   DWORD dwEvent; 
   WSANETWORKEVENTS NetworkEvents; 
   int rc; 

   // Initialize Winsock 
   WSAStartup(MAKEWORD(2,0), &stack_info) ; 

   // Create events 
   ahEvents[0] = WSACreateEvent(); 
   ahEvents[1] = WSACreateEvent(); 


   // Create listening socket 
   ahSocket[0] = ListenFirstFreePort(); 
   rc = WSAEventSelect(ahSocket[0], ahEvents[0], FD_ACCEPT ); 
   if( rc == SOCKET_ERROR ) 
   { 
      printf( "WSAEventSelect() error %d\n", SOCKET_ERRNO ); 
      exit(1); 
   } 

   while (TRUE) 
   { 
      // Waiting for so;ething to happen 
      // Basically we'll firstly receive the connexion of the client socket 
      // and then we'll be notificated when there will be some data to read 

      // look for events 
      dwEvent = WSAWaitForMultipleEvents( 2, ahEvents, FALSE, WSA_INFINITE, FALSE); 

      switch (dwEvent) 
      { 
      case WSA_WAIT_FAILED: 
         printf("WSAEventSelect: %d\n", WSAGetLastError()); 
         break; 
      case WAIT_IO_COMPLETION: 
      case WSA_WAIT_TIMEOUT: 
         break; 

      default: 

         //if there is one dwEvent-WSA_WAIT_EVENT_0 has to be substracted so as to dwEvent correspond to the index of the concerned socket 
         dwEvent -= WSA_WAIT_EVENT_0; 

         // enumeration of the events on the socket[dwEvent] 
         if (SOCKET_ERROR == WSAEnumNetworkEvents(ahSocket[dwEvent], ahEvents[dwEvent], &NetworkEvents)) 
         { 
            printf("WSAEnumNetworkEvent: %d lNetworkEvent %X\n",  
               WSAGetLastError(), NetworkEvents.lNetworkEvents); 
            NetworkEvents.lNetworkEvents = 0; 
         } 
         else  
         { 

            if (FD_CLOSE   & NetworkEvents.lNetworkEvents) 
            { 

               printf( "FD_CLOSE ok (dwEvent=%d)\n", dwEvent ); 
               printf( "press a key to exit\n" ); 
               getch(); // require conio.h 

               WSACloseEvent( ahEvents[0] ); 
               WSACloseEvent( ahEvents[1] ); 
               exit(0); 
            } 
            if (FD_READ & NetworkEvents.lNetworkEvents) 
            { 
               char szBuffer[256]; int cbRecv; 

               // Only the second socket expect to receive data 
               printf( "FD_READ ok (dwEvent=%d)\n", dwEvent ); 

               // read data 
               cbRecv = recv( ahSocket[dwEvent], szBuffer, sizeof(szBuffer) - 1, 0 ); 
               if( cbRecv <= 0 ) 
               { 
                  printf( "recv() error %d\n", SOCKET_ERRNO ); 
                  exit(1); 
               } 

               // On ecrit ce paquet (On remet le 0 au cas ou le paquet 
               // ait ete coupe en 2 - je sais, ca n'arrivera jamais en local) 
               // we put the 0 in case it has been cut - unlikey to happen on local network 
               szBuffer[cbRecv] = 0; 

               // write data in console window 
               printf( "socket %d : '%s'\n", dwEvent, szBuffer ); 
            } 
         } 
         if (FD_ACCEPT & NetworkEvents.lNetworkEvents) 
         { 
            struct sockaddr_in addrAccept; 
            int lenAccept; 
            lenAccept = sizeof( addrAccept ); 

            // we should have dwEvent=0 
            printf( "accept ok (dwEvent=%d)\n", dwEvent ); 

            // we create another socket to accept the connexion with the client socket 
            ahSocket[1] = accept(ahSocket[dwEvent], (struct sockaddr *)&addrAccept, &lenAccept); 

            // we want to be informed on when we'll be able read data from it 
            rc = WSAEventSelect(ahSocket[1], ahEvents[1], FD_READ|FD_CLOSE  ); 
            if( rc == SOCKET_ERROR ) 
            { 
               printf( "WSAEventSelect() error %d\n", SOCKET_ERRNO ); 
               exit(1); 
            } 
         } 
      } 
   } 
} 

客户端

#include <winsock2.h> 
#include <conio.h> 
#include <time.h> 

#pragma comment(lib, "ws2_32.lib") 

#define   SOCKET_ERRNO   WSAGetLastError() 
#define ADDRESS "127.0.0.1" 
#define PORT 1234 

SOCKET ConnectToPort() 
{ 
   struct sockaddr_in addr; 
   SOCKET hSocket; 
   u_long arg; int err; 

   // Create socket 
   hSocket = socket( PF_INET, SOCK_STREAM, 0 ); 
   if( hSocket == INVALID_SOCKET ) 
   { 
      printf( "socket() error %d\n", SOCKET_ERRNO ); 
      exit(1); 
   } 

   // Connexion setting for local connexion 
   addr.sin_family = AF_INET ; 
   addr.sin_addr.s_addr = inet_addr(ADDRESS); 
   addr.sin_port = htons (PORT); 

   // Connect 
   if( connect( hSocket, (struct sockaddr *)&addr, sizeof(addr) ) == SOCKET_ERROR ) 
   { 
      // As we are in non-blocking mode we'll always have the error 
      // WSAEWOULDBLOCK whichis actually not one 
      if( SOCKET_ERRNO != WSAEWOULDBLOCK ) 
      { 
         printf( "connect() error (%d)\n", SOCKET_ERRNO ); 
         exit(1); 
      } 
   } 

   return hSocket; 
} 



void main() 
{ 
   int initClockTime; 
   WSADATA stack_info; 
   SOCKET ahSocket[1]; 
   WSAEVENT ahEvents[1]; 
   DWORD dwEvent; 
   WSANETWORKEVENTS NetworkEvents; 
   int rc; 

   // Initialize Winsock 
   WSAStartup(MAKEWORD(2,0), &stack_info) ; 

   // Create event 
   ahEvents[0] = WSACreateEvent(); 

   // Create and connect a socket on the server socket 
   ahSocket[0]= ConnectToPort(); 

   // not sure if I have to use or not 
   /*u_long arg = 1; 
   ioctlsocket( ahSocket[0] , FIONBIO, &arg );*/ 

   // the application wants to receive notification of a completed connection 
   rc = WSAEventSelect(ahSocket[0], ahEvents[0], FD_CONNECT  ); 
   if( rc == SOCKET_ERROR ) 
   { 
      printf( "WSAEventSelect() error %d\n", SOCKET_ERRNO ); 
      exit(1); 
   } 

   while (TRUE) 
   { 
      // look for events 
      dwEvent = WSAWaitForMultipleEvents( 1, ahEvents, FALSE, 1000, FALSE); 

      switch (dwEvent) 
      { 
      case WSA_WAIT_FAILED: 
         printf("WSAEventSelect: %d\n", WSAGetLastError()); 
         break; 
      case WAIT_IO_COMPLETION: 
      case WSA_WAIT_TIMEOUT: 
         break; 

      default: 

         printf("while\n"); 

         //if there is one dwEvent-WSA_WAIT_EVENT_0 has to be substracted so as to dwEvent correspond to the index of the concerned socket 
         dwEvent -= WSA_WAIT_EVENT_0; 

         // enumeration of the events on the socket[dwEvent] 
         if (SOCKET_ERROR == WSAEnumNetworkEvents(ahSocket[dwEvent], ahEvents[dwEvent], &NetworkEvents)) 
         { 
            printf("WSAEnumNetworkEvent: %d lNetworkEvent %X\n", WSAGetLastError(), NetworkEvents.lNetworkEvents); 
            NetworkEvents.lNetworkEvents = 0; 
         } 
         else  
         { 


            if (FD_CONNECT & NetworkEvents.lNetworkEvents) 
            { 
               //connexion is OK 
               printf( "FD_CONNECT ok (dwEvent=%d)\n", dwEvent ); 

               // now that we are connected we want to send data or be aware when the other socket is disconnected 
               rc = WSAEventSelect(ahSocket[dwEvent], ahEvents[dwEvent], FD_CLOSE | FD_WRITE ); 
               if( rc == SOCKET_ERROR ) 
               { 
                  printf( "WSAEventSelect() error %d\n", SOCKET_ERRNO ); 
                  exit(1); 
               } 
            } 
            if (FD_CLOSE   & NetworkEvents.lNetworkEvents) 
            { 
               printf( "FD_CLOSE ok (dwEvent=%d)\n", dwEvent ); 
               printf( "press a key to exit\n" ); 
               getch(); 

               WSACloseEvent( ahEvents[0] ); 
               exit(0); 
            } 

            if (FD_WRITE & NetworkEvents.lNetworkEvents) 
            { 
               char szBuffer[256]; int cbBuffer; 

               printf( "FD_WRITE ok (dwEvent=%d)\n", dwEvent ); 

               // create string and return the size 
               cbBuffer = sprintf( szBuffer, "Coucou", dwEvent ); 


               // send the string with 0 at the end 
               rc = send( ahSocket[dwEvent], szBuffer, cbBuffer + 1, 0 ); 
               if (SOCKET_ERROR ==  rc) 
               { 
                  printf("WSAEnumNetworkEvent: %d lNetworkEvent %X\n",  WSAGetLastError(), NetworkEvents.lNetworkEvents); 
               } 

               // not sure if I have to use it 
               //WSAResetEvent(ahEvents[0]); 

            } 

         } 
      } 
   } 
} 

下载.cpp 文件:https://www.dropbox.com/s/pjuipz7v4iwr5ea/Clientserver%20TCP.zip

最佳答案

在第一个通知之后您没有收到 FD_WRITE 通知,因为您没有考虑 the documentation 中的以下段落:

The FD_WRITE network event is handled slightly differently. An FD_WRITE network event is recorded when a socket is first connected with a call to the connect, ConnectEx, WSAConnect, WSAConnectByList, or WSAConnectByName function or when a socket is accepted with accept, AcceptEx, or WSAAccept function and then after a send fails with WSAEWOULDBLOCK and buffer space becomes available. Therefore, an application can assume that sends are possible starting from the first FD_WRITE network event setting and lasting until a send returns WSAEWOULDBLOCK. After such a failure the application will find out that sends are again possible when an FD_WRITE network event is recorded and the associated event object is set.

在您第一次调用 send() 后,套接字仍然是可写的,因为它的出站缓冲区没有满。只要您仍有数据要发送,请继续调用 send() 直到它报告 WSAWOULDBLOCK 错误,指示缓冲区已满。那时,您必须跟踪剩余数据,直到收到指示套接字再次可写的 FD_WRITE 通知,以便您可以从中断处继续发送剩余数据。

关于c++ - 服务器/客户端 TCP 异步(winsock)//FD_WRITE 问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12205393/

有关c++ - 服务器/客户端 TCP 异步(winsock)//FD_WRITE 问题的更多相关文章

  1. ruby - 使用 ruby​​ 和 savon 的 SOAP 服务 - 2

    我正在尝试使用ruby​​和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我

  2. ruby - 具有身份验证的私有(private) Ruby Gem 服务器 - 2

    我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..

  3. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  4. ruby-on-rails - 启动 Rails 服务器时 ImageMagick 的警告 - 2

    最近,当我启动我的Rails服务器时,我收到了一长串警告。虽然它不影响我的应用程序,但我想知道如何解决这些警告。我的估计是imagemagick以某种方式被调用了两次?当我在警告前后检查我的git日志时。我想知道如何解决这个问题。-bcrypt-ruby(3.1.2)-better_errors(1.0.1)+bcrypt(3.1.7)+bcrypt-ruby(3.1.5)-bcrypt(>=3.1.3)+better_errors(1.1.0)bcrypt和imagemagick有关系吗?/Users/rbchris/.rbenv/versions/2.0.0-p247/lib/ru

  5. ruby-on-rails - s3_direct_upload 在生产服务器中不工作 - 2

    在Rails4.0.2中,我使用s3_direct_upload和aws-sdkgems直接为s3存储桶上传文件。在开发环境中它工作正常,但在生产环境中它会抛出如下错误,ActionView::Template::Error(noimplicitconversionofnilintoString)在View中,create_cv_url,:id=>"s3_uploader",:key=>"cv_uploads/{unique_id}/${filename}",:key_starts_with=>"cv_uploads/",:callback_param=>"cv[direct_uplo

  6. ruby - 用 Ruby 编写一个简单的网络服务器 - 2

    我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b

  7. ruby-on-rails - 在 Rails 中调试生产服务器 - 2

    您如何在Rails中的实时服务器上进行有效调试,无论是在测试版/生产服务器上?我试过直接在服务器上修改文件,然后重启应用,但是修改好像没有生效,或者需要很长时间(缓存?)我也试过在本地做“脚本/服务器生产”,但是那很慢另一种选择是编码和部署,但效率很低。有人对他们如何有效地做到这一点有任何见解吗? 最佳答案 我会回答你的问题,即使我不同意这种热修补服务器代码的方式:)首先,你真的确定你已经重启了服务器吗?您可以通过跟踪日志文件来检查它。您更改的代码显示的View可能会被缓存。缓存页面位于tmp/cache文件夹下。您可以尝试手动删除

  8. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

  9. ruby - 如何计算 Liquid 中的变量 +1 - 2

    我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我

  10. ruby - 我的 Ruby IRC 机器人没有连接到 IRC 服务器。我究竟做错了什么? - 2

    require"socket"server="irc.rizon.net"port="6667"nick="RubyIRCBot"channel="#0x40"s=TCPSocket.open(server,port)s.print("USERTesting",0)s.print("NICK#{nick}",0)s.print("JOIN#{channel}",0)这个IRC机器人没有连接到IRC服务器,我做错了什么? 最佳答案 失败并显示此消息::irc.shakeababy.net461*USER:Notenoughparame

随机推荐