我需要你的帮助,因为我必须在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
答案 0 :(得分:2)
由于您没有考虑the documentation中的以下段落,因此您未在第一个之后收到FD_WRITE
次通知:
FD_WRITE网络事件的处理方式略有不同。一个 首次连接套接字时会记录FD_WRITE网络事件 调用connect,ConnectEx,WSAConnect,WSAConnectByList, 或WSAConnectByName函数或当接受套接字时接受, AcceptEx,或WSAAccept函数,然后发送失败后 WSAEWOULDBLOCK和缓冲区空间变得可用。因此,一个 应用程序可以假设从第一个开始可以发送 FD_WRITE网络事件设置并持续到发送返回 WSAEWOULDBLOCK。在这样的失败后,应用程序会发现 当FD_WRITE网络事件发生时,再次发送 记录并关联事件对象。
第一次调用send()
后,套接字仍然可写,因为它的出站缓冲区未满。只要您还有要发送的数据,请继续调用send()
,直到它报告WSAWOULDBLOCK
错误,指示缓冲区已满。此时,您必须跟踪剩余数据,直到收到FD_WRITE
通知,表明套接字再次可写,以便您可以继续从中断的位置发送剩余数据。
答案 1 :(得分:1)
我建议首先查看常规select()
的非阻塞I / O.这里有几个链接供您开始使用: