I have a socket connect function, the issue is that if the client is started before the server, the connection shows as connected, but for some reason returns fails. I am not sure where the failure is and would really appreciate any help:
The function is:
bool IPV4Socket::Connect( std::string hostname
, unsigned short remotePort
, TimeoutValue *timeout )
{
AddrInfo getResults;
AddrInfo getaddrinfoHints;
int connReturn = 0;
SockAddr_In *addrData;
bool connectSuccess = false;
std::string service = std::to_string( remotePort );
getaddrinfoHints.ai_family = AddressFamily_inet;
getaddrinfoHints.ai_socktype = SockType_stream;
if ( m_socketAdaptor->getaddrinfo( hostname
, service
, &getaddrinfoHints
, &getResults ) != 0 )
{
return false;
}
addrData = (SockAddr_In *)&( *getResults.ai_addr.begin() );
connReturn = m_socketAdaptor->connect( m_socket
, (const Sockaddr *)addrData
, (int)getResults.ai_addrlen );
if ( connReturn == SocketError)
{
int m_lastErrorCode = m_socketAdaptor->GetLastError();
// Connection error : FATAL
if ( ( m_lastErrorCode != SockErr_EWOULDBLOCK) &&
( m_lastErrorCode != SockErr_EALREADY ) )
{
connectSuccess = false;
}
else
{
SocketSet writeFDS;
SocketSet exceptFDS;
int selectReturn = 0;
// Clear all the socket FDS structures
SocketSet_ZERO( &writeFDS );
SocketSet_ZERO( &exceptFDS );
// Put the socket into the FDS structures
SocketSet_SET( m_socket, &writeFDS );
SocketSet_SET( m_socket, &exceptFDS );
selectReturn = m_socketAdaptor->select( -1
, NULL
, &writeFDS
, &exceptFDS
, timeout );
if ( selectReturn == SocketError )
{
// Any errors are bad
connectSuccess = false;
}
else if ( selectReturn > 0 )
{
// Check for error (exception) first
if ( m_socketAdaptor->SocketSet_ISSET( m_socket, &exceptFDS ) )
{
connectSuccess = false;
}
else if ( m_socketAdaptor->SocketSet_ISSET( m_socket, &writeFDS ) )
{
// Select returned 'writable', we're connected!
connectSuccess = true;
m_isConnected = true;
}
}
}
}
else
{
connectSuccess = true;
m_isConnected = true;
}
return connectSuccess;
}
I am not sure if I am missing the point, or if I have overly complicated the function.
Helllp :)
Notes:
* By the way, m_socketAdaptor-> functions are just wrappers.
* If you start server and then client, it works...
You can't reconnect a socket which has already failed to connect. You have to close it and create a new socket. Therefore that should be done in the connect method, not wherever it is done now.
Related
How boost::asio::ip::tcp::acceptor work with TCP_DEFER_ACCEPT and TCP_FASTOPEN,
When I use pure C, I can set some param like TCP_DEFER_ACCEPT and TCP_FASTOPEN, to improve tcp/ip performance,like this:
setsockopt(serverSock, IPPROTO_TCP, TCP_DEFER_ACCEPT, &soValue, sizeof(soValue));
setsockopt( serverSock, SOL_TCP, TCP_FASTOPEN, &qlen, sizeof(qlen));
But when I use boost::asio,Icould not find any way to set TCP_DEFER_ACCEPT and TCP_FASTOPEN, who can tell me how to solve it? Thank you.
I got it. use boost::asio::ip::tcp::acceptor::native_handle().
example:
std::shared_ptr<boost::asio::ip::tcp::acceptor> sp_acceptor( std::make_shared<boost::asio::ip::tcp::acceptor>( d_ios_last0 ) );
sp_acceptor->open( server_endpoint.protocol() );
sp_acceptor->set_option( boost::asio::ip::tcp::acceptor::reuse_address( true ) );
if( bool_tcp_defer_accept ) {
int soValue = 1;
if( 0 == setsockopt( sp_acceptor->native_handle(), IPPROTO_TCP, TCP_DEFER_ACCEPT, &soValue, sizeof(soValue)) ) {
log_message( "TCP_DEFER_ACCEPT set success." );
}
else {
warn_message( "TCP_DEFER_ACCEPT set failed." );
}
}
sp_acceptor->bind( server_endpoint );
if( bool_tcp_fastopen ) {
int qlen=5;
if( 0==setsockopt( sp_acceptor->native_handle(), SOL_TCP, TCP_FASTOPEN, &qlen, sizeof(qlen)) ) {
log_message( "TCP_FASTOPEN set success." );
}
else {
warn_message( "TCP_FASTOPEN set failed." );
}
}
sp_acceptor->listen( 8192 );
I have the following simplified IO Completion Port server C++ code:
int main(..)
{
startCompletionPortThreadProc();
// Await client connection
sockaddr_in clientAddress;
int clientAddressSize = sizeof( clientAddress );
SOCKET acceptSocket = WSAAccept( serverSocket, (SOCKADDR*)&clientAddress, &clientAddressSize, NULL, NULL);
// Connected
CreateIoCompletionPort( (HANDLE)acceptSocket, completionPort, 0, 0 );
// Issue initial read
read( acceptSocket );
}
DWORD WINAPI completionPortThreadProc( LPVOID param )
{
DWORD bytesTransferred = 0;
ULONG_PTR completionKey = NULL;
LPPER_IO_DATA perIoData = NULL;
while( GetQueuedCompletionStatus( completionPort, &bytesTransferred, &completionKey, (LPOVERLAPPED*)&perIoData, INFINITE ) )
{
if( WaitForSingleObject( exitEvent, 0 ) == WAIT_OBJECT_0 )
{
break;
}
if( !perIoData )
continue;
if( bytesTransferred == 0 )
{
//TODO
}
switch( perIoData->operation )
{
case OPERATION_READ:
{
// Bytes have been received
if( bytesTransferred < perIoData->WSABuf.len )
{
// Terminate string
perIoData->WSABuf.buf[bytesTransferred] = '\0';
perIoData->WSABuf.buf[bytesTransferred+1] = '\0';
}
// Add data to message build
message += std::tstring( (TCHAR*)perIoData->WSABuf.buf );
// Perform next read
perIoData->WSABuf.len = sizeof( perIoData->inOutBuffer );
perIoData->flags = 0;
if( WSARecv( perIoData->socket, &( perIoData->WSABuf ), 1, &bytesTransferred, &( perIoData->flags ), &( perIoData->overlapped ), NULL ) == 0 )
{
// Part message
continue;
}
if( WSAGetLastError() == WSA_IO_PENDING )
{
// End of message
//TODO: Process message here
continue;
}
}
}
break;
case OPERATION_WRITE:
{
perIoData->bytesSent += bytesTransferred;
if( perIoData->bytesSent < perIoData->bytesToSend )
{
perIoData->WSABuf.buf = (char*)&( perIoData->inOutBuffer[perIoData->bytesSent] );
perIoData->WSABuf.len = ( perIoData->bytesToSend - perIoData->bytesSent);
}
else
{
perIoData->WSABuf.buf = (char*)perIoData->inOutBuffer;
perIoData->WSABuf.len = _tcslen( perIoData->inOutBuffer ) * sizeof( TCHAR );
perIoData->bytesSent = 0;
perIoData->bytesToSend = perIoData->WSABuf.len;
}
if( perIoData->bytesToSend )
{
if( WSASend( perIoData->socket, &( perIoData->WSABuf ), 1, &bytesTransferred, 0, &( perIoData->overlapped ), NULL ) == 0 )
continue;
if( WSAGetLastError() == WSA_IO_PENDING )
continue;
}
}
break;
}
}
return 0;
}
bool SocketServer::read( SOCKET socket, HANDLE completionPort )
{
PER_IO_DATA* perIoData = new PER_IO_DATA;
ZeroMemory( perIoData, sizeof( PER_IO_DATA ) );
perIoData->socket = socket;
perIoData->operation = OPERATION_READ;
perIoData->WSABuf.buf = (char*)perIoData->inOutBuffer;
perIoData->WSABuf.len = sizeof( perIoData->inOutBuffer );
perIoData->overlapped.hEvent = WSACreateEvent();
DWORD bytesReceived = 0;
if( WSARecv( perIoData->socket, &( perIoData->WSABuf ), 1, &bytesReceived, &( perIoData->flags ), &( perIoData->overlapped ), NULL ) == SOCKET_ERROR )
{
int gle = WSAGetLastError();
if( WSAGetLastError() != WSA_IO_PENDING )
{
delete perIoData;
return false;
}
}
return true;
}
bool SocketServer::write( SOCKET socket, std::tstring& data )
{
PER_IO_DATA* perIoData = new PER_IO_DATA;
ZeroMemory( perIoData, sizeof( PER_IO_DATA ) );
perIoData->socket = socket;
perIoData->operation = OPERATION_WRITE;
perIoData->WSABuf.buf = (char*)data.c_str();
perIoData->WSABuf.len = _tcslen( data.c_str() ) * sizeof( TCHAR );
perIoData->bytesToSend = perIoData->WSABuf.len;
perIoData->overlapped.hEvent = WSACreateEvent();
DWORD bytesSent = 0;
if( WSASend( perIoData->socket, &( perIoData->WSABuf ), 1, &bytesSent, 0, &( perIoData->overlapped ), NULL ) == SOCKET_ERROR )
{
if( WSAGetLastError() != WSA_IO_PENDING )
{
delete perIoData;
return false;
}
}
return true;
}
1) The first issue I have is with the initial read.
On client connection (accept), I issue a read. As the client hasn't sent any data yet, WSAGetLastError() is WSA_IO_PENDING and the read method returns.
When the client then sends data, the thread remains stuck in the GetQueuedCompletionStatus call (as I assume I need another WSARecv call?).
Am I supposed to keep looping the read method until data arrives? That doesn't seem logical, I thought by issuing the initial read GetQueuedCompletionStatus would complete when data arrived.
2) I need to read and write data bi-directional without acknowledgements. Therefore I've also created a client with the IOCP thread. Is it actually possible to do this with completion ports or does a read have to be followed by a write?
Sorry for what feels like basic questions, but after trawling the internet and building IOCP examples I'm still unable to answer the questions.
Many thanks in advance.
On client connection (accept), I issue a read. As the client hasn't sent any data yet, WSAGetLastError() is WSA_IO_PENDING and the read method returns.
That is normal behavior.
When the client then sends data, the thread remains stuck in the GetQueuedCompletionStatus call (as I assume I need another WSARecv call?).
No, you do not need another call. And if it is getting stuck, then you are not associating the read with the I/O Completion Port correctly.
Am I supposed to keep looping the read method until data arrives?
No. You need to call WSARecv() one time for the initial read. The WSA_IO_PENDING error means the read is waiting for data and will signal the I/O Completion Port when data actually arrives. DO NOT call WSARecv() (or any other read function) until that signal actually arrives. Then you can call WSARecv() again to wait for more data. Repeat until the socket is disconnected.
I thought by issuing the initial read GetQueuedCompletionStatus would complete when data arrived.
That is exactly what is supposed to happen.
2) I need to read and write data bi-directional without acknowledgements. Therefore I've also created a client with the IOCP thread. Is it actually possible to do this with completion ports
Yes. Reading and writing are separate operations, they are not dependent on each other.
does a read have to be followed by a write?
Not if your protocol does not require it, no.
Now, with that said, there are some issues with your code.
On a minor note, WSAAccept() is synchronous, you should consider using AcceptEx() instead so it can use the same I/O Completion Port for reporting new connections.
But more importantly, when a pending I/O operation fails, GetQueuedCompletionStatus() returns FALSE, the returned LPOVERLAPPED pointer will be non-NULL, and GetLastError() will report why the I/O operation failed. However, if GetQueuedCompletionStatus() itself fails, the returned LPOVERLAPPED pointer will be NULL, and GetLastError() will report why GetQueuedCompletionStatus() failed. This difference is clearly explained in the documentation, but your while loop is not accounting for it. Use a do..while loop instead and act according to the LPOVERLAPPED pointer:
DWORD WINAPI completionPortThreadProc( LPVOID param )
{
DWORD bytesTransferred = 0;
ULONG_PTR completionKey = NULL;
LPPER_IO_DATA perIoData = NULL;
do
{
if( GetQueuedCompletionStatus( completionPort, &bytesTransferred, &completionKey, (LPOVERLAPPED*)&perIoData, INFINITE ) )
{
// I/O success, handle perIoData based on completionKey as needed...
}
else if( perIoData )
{
// I/O failed, handle perIoData based on completionKey as needed...
}
else
{
// GetQueuedCompletionStatus() failure...
break;
}
}
while( WaitForSingleObject( exitEvent, 0 ) == WAIT_TIMEOUT );
return 0;
}
On a side note, instead of using an event object to signal when completionPortThreadProc() should exit, consider using PostQueuedCompletionionStatus() instead to post a termination completionKey to the I/O Completion Port, then your loop can look for that value:
DWORD WINAPI completionPortThreadProc( LPVOID param )
{
DWORD bytesTransferred = 0;
ULONG_PTR completionKey = NULL;
LPPER_IO_DATA perIoData = NULL;
do
{
if( GetQueuedCompletionStatus( completionPort, &bytesTransferred, &completionKey, (LPOVERLAPPED*)&perIoData, INFINITE ) )
{
if( completionKey == MyTerminateKey )
break;
if( completionKey == MySocketIOKey )
{
// I/O success, handle perIoData as needed...
}
}
else if( perIoData )
{
// I/O failed, handle perIoData based on completionKey as needed...
}
else
{
// GetQueuedCompletionStatus() failure...
break;
}
}
while( true );
return 0;
}
CreateIoCompletionPort( (HANDLE)acceptSocket, completionPort, MySocketIOKey, 0 );
PostQueuedCompletionStatus( completionPort, 0, MyTerminateKey, NULL );
Hi I am using winsock2 and I am trying to get async communication.
I tried with TCP server msdn which is waiting for accept.
I tried with WSAAsyncSelect before the listen function.
WSAAsyncSelect(ListenSocket,
m_hWnd,
WM_SOCKET,
(FD_CLOSE | FD_ACCEPT | FD_READ));
and is showing the following error ..
Error C4996 'WSAAsyncSelect': Use WSAEventSelect() instead or define _WINSOCK_DEPRECATED_NO_WARNINGS to disable deprecated API warnings
How can I solve this .. Thanks in advance ..
To get rid of that warning you can define #define _WINSOCK_DEPRECATED_NO_WARNINGS
at the top of your source file, which will disable there types of warnings.
That said, it might be worth listening to this warning and using WSAEventSelect instead.
Since WSAEventSelect signals an event instead of posting a message to the Window's message queue which is what WSAAsyncSelect does. Posting into the message queue is slower and adds additional processing that isn't really needed.
I have an example laying around which i'll post here for you:
... I may have gone overboard... but it's all useful and relevant.
Setup Listening Socket
if ( socket_name == INVALID_SOCKET )
{
P_ERR( "Could not create socket ( Id: %d ): %d\n", id, WSAGetLastError() );
return INVALID_SOCKET;
}
rerror = bind( socket_name, (SOCKADDR*)&sock_addr, sizeof( sock_addr ) );
if ( rerror != SOCKET_ERROR )
{
rerror = listen( socket_name, MAX_LISTEN_QUEUE );
if ( rerror != SOCKET_ERROR )
{
/* Selects the events that will trigger the `socket_event` windows event. */
/* socket_event was created using 'CreateEvent( NULL, TRUE, FALSE, NULL );' */
WSAEventSelect( socket_name, socket_event, FD_ALL_EVENTS );
if ( !startStatusThread() )
{
rerror = 1;
P_ERR( "Status thread failed: %d\n", id );
}
}
else
{
P_ERR( "listen() error %d : Error %d\n", id, WSAGetLastError() );
closesocket( socket_name );
}
}
else
{
P_ERR( "bind() error ( Id: %d ): %d\n", id, WSAGetLastError() );
closesocket( socket_name );
}
Process Events From Socket
/* waits 10ms for events in the event array ( in this case just 1 event as socket_event ). */
rerror = WSAWaitForMultipleEvents( 1, &socket_event, FALSE, 10, FALSE );
if ( rerror == WSA_WAIT_TIMEOUT )
{
continue; /* this block of code runs in a while loop. */
}
index = rerror - WSA_WAIT_EVENT_0; /* get the smallest index of a triggered event */
if ( rerror != WSA_WAIT_TIMEOUT && rerror != WSA_WAIT_FAILED )
{
/* returns a list of the events that occured. */
rerror = WSAEnumNetworkEvents( socket_name, socket_event, &events );
if ( rerror == SOCKET_ERROR )
{
P_ERR( "WSAEnumNetworkEvents Error %d: Id: %d\n", WSAGetLastError(), pThis->id );
continue;
}
/* look below for this function. */
handleNetworkEvents( events, index );
}
Handling the Events.
void
handleNetworkEvents( WSANETWORKEVENTS e, const int socket_index )
{
int rerror = 0;
/* on accept. */
if ( e.lNetworkEvents & FD_ACCEPT )
{
if ( e.iErrorCode[FD_ACCEPT_BIT] == 0 )
{
onAccept();
}
else
{
P_ERR( "Unknown network event error %d\n", id );
}
}
/* on connect */
if ( e.lNetworkEvents & FD_CONNECT )
{
if ( e.iErrorCode[FD_CONNECT_BIT] == 0 )
{
sendRead(); /* send first read request */
}
else
{
P_ERR( "Unknown network event error %d\n", id );
}
}
/* on successful read */
if ( e.lNetworkEvents & FD_READ )
{
sendRead(); /* get read data and queue another request. */
callback( id, inBuffer.buf, lastReadSize ); /* process data. */
}
/* on close. */
if ( e.lNetworkEvents & FD_CLOSE )
{
/* close the current event and make a new one ready for a new connection. */
onClose( socket_index );
}
}
How do I find the port name for a bluetooth device with a specific device name?
I have this code, which enumerates all bluetooth devices, but doesn't give me their port name:
HBLUETOOTH_DEVICE_FIND founded_device;
BLUETOOTH_DEVICE_INFO device_info;
device_info.dwSize = sizeof(device_info);
BLUETOOTH_DEVICE_SEARCH_PARAMS search_criteria;
search_criteria.dwSize = sizeof(BLUETOOTH_DEVICE_SEARCH_PARAMS);
search_criteria.fReturnAuthenticated = TRUE;
search_criteria.fReturnRemembered = FALSE;
search_criteria.fReturnConnected = FALSE;
search_criteria.fReturnUnknown = FALSE;
search_criteria.fIssueInquiry = FALSE;
search_criteria.cTimeoutMultiplier = 0;
founded_device = BluetoothFindFirstDevice(&search_criteria, &device_info);
if(founded_device == NULL)
return -1;
do {
wstring ws = device_info.szName;
cout << string(ws.begin(), ws.end()) << endl;
} while (BluetoothFindNextDevice(founded_device, &device_info));
And then I have this code, which enumerates all port names but doesn't give me the device name:
DWORD bytesNeeded = 0;
DWORD portCount = 0;
BOOL ret = EnumPorts(nullptr, 2, nullptr, 0, &bytesNeeded, &portCount);
BYTE *ports = new BYTE[bytesNeeded];
if(EnumPorts(nullptr, 2, (LPBYTE)ports, bytesNeeded, &bytesNeeded, &portCount))
{
PORT_INFO_2 *portInfo = (PORT_INFO_2*)ports;
for(DWORD i = 0; i < portCount; ++i)
cout << portInfo[i].pPortName << endl;
}
delete [] ports;
I need to automatically connect to a specific device when my app is started, so I need to either get the port name for the bluetooth device in the first piece of code so I can connect to it, or check each portname in the second piece of code to make sure it's the right device before connecting to it.
How do I do it?
I remember struggling with this in the past.
the only solution i found was to use sockets for communicating with the Bluetooth device using its address, then use the send() and recv() methods for communicating with the device.
// assuming you have the BT device address in blueToothDeviceAddr;
char blueToothDeviceAddr[18];
SOCKET sock;
SOCKADDR_BTH sa = { 0,0,0,0 };
int sa_len = sizeof(sa);
// initialize windows sockets
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD( 2, 0 );
if( WSAStartup( wVersionRequested, &wsaData ) != 0 )
{
ExitProcess(100);
}
// parse the specified Bluetooth address
if( SOCKET_ERROR == WSAStringToAddress( blueToothDeviceAddr, AF_BTH,
NULL, (LPSOCKADDR) &sa, &sa_len ) )
{
ExitProcess(101);
}
// query it for the right port
// create the socket
sock = socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM);
if( SOCKET_ERROR == sock )
{
ExitProcess(102);
}
// fill in the rest of the SOCKADDR_BTH struct
GUID pService = (GUID)SerialPortServiceClass_UUID;
SOCKADDR_BTH outSA;
sa.port = SDPGetPort(blueToothDeviceAddr, (LPGUID) &pService,&outSA);
if( sa.port == 0 )
{
ExitProcess(103);
}
// in case you have a pass code you need to register for authetication callback
// look the web for this part
// connect to the device
if( SOCKET_ERROR == connect( sock, (LPSOCKADDR) &outSA, sa_len ) )
{
int lastError = GetLastError();
ExitProcess(105);
}
Under the key:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\BTHENUM
you can find a subkey which has a list of keys containing the device address.
Under this last key, you can find a subkey named Device Parameters which finally has the PortName value.
The code is written in C++ with MFC libraries and is tested under Windows XP, 7 and 10. I hope it helps you !
// Returns the outgoing COM port of a bluetooth device given by address
int GetBluetoothCOM( CString sAddr )
{
int iPort = 0;
HKEY hKey_1;
DWORD KeyNdx_1 = 0;
DWORD MaxKeyLen_1;
char KeyNam_1[ MAX_PATH + 1 ];
LONG RetVal_1;
sAddr.MakeUpper();
sAddr.Replace( ":", "" );
sAddr.Replace( " ", "" );
// Enumerate keys under: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\BTHENUM
RegOpenKeyEx( HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Enum\\BTHENUM", NULL, KEY_READ | KEY_ENUMERATE_SUB_KEYS, &hKey_1 );
while( true )
{
MaxKeyLen_1 = MAX_PATH;
RetVal_1 = RegEnumKeyEx( hKey_1, KeyNdx_1, KeyNam_1, &MaxKeyLen_1, NULL, NULL, NULL, NULL );
if( RetVal_1 == ERROR_NO_MORE_ITEMS )
{
break;
}
if( RetVal_1 == ERROR_SUCCESS )
{
HKEY hKey_2;
DWORD KeyNdx_2 = 0;
DWORD MaxKeyLen_2;
char KeyNam_2[ MAX_PATH + 1 ];
LONG RetVal_2;
// Enumerate subkeys
RegOpenKeyEx( HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Enum\\BTHENUM\\" + CString( KeyNam_1 ), NULL, KEY_READ | KEY_ENUMERATE_SUB_KEYS, &hKey_2 );
while( true )
{
MaxKeyLen_2 = MAX_PATH;
RetVal_2 = RegEnumKeyEx( hKey_2, KeyNdx_2, KeyNam_2, &MaxKeyLen_2, NULL, NULL, NULL, NULL );
if( RetVal_2 == ERROR_NO_MORE_ITEMS )
{
break;
}
if( RetVal_2 == ERROR_SUCCESS )
{
// Find out if the key name contains &ADDRESS_
CString sKey = "SYSTEM\\CurrentControlSet\\Enum\\BTHENUM\\" + CString( KeyNam_1 ) + "\\" + CString( KeyNam_2 );
sKey.MakeUpper();
if( sKey.Find( "&" + sAddr + "_" ) != -1 )
{
HKEY hKey;
char szPort[ 100 + 1 ];
DWORD dwLen = 100;
// I find out the device
RegOpenKeyEx( HKEY_LOCAL_MACHINE, sKey + "\\Device Parameters", 0, KEY_READ, &hKey );
if( RegQueryValueEx( hKey, "PortName", NULL, NULL, ( LPBYTE ) &szPort, &dwLen ) == ERROR_SUCCESS )
{
szPort[ dwLen ] = 0;
CString sPort = CString( szPort );
sPort.MakeUpper();
if( sPort.Find( "COM" ) == -1 )
{
RegCloseKey( hKey );
continue;
}
sPort.Replace( "COM", "" );
sPort.Trim();
iPort = atoi( sPort.GetBuffer() );
if( iPort != 0 )
{
RegCloseKey( hKey );
break;
}
}
RegCloseKey( hKey );
}
}
++KeyNdx_2;
}
RegCloseKey( hKey_2 );
if( iPort != 0 )
{
break;
}
}
++KeyNdx_1;
};
RegCloseKey( hKey_1 );
return iPort;
}
void CApplication::SendData( const char pBuffer[] )
{
if( pBuffer == NULL )
{
Log()->Write( ELogMessageType_ERROR, "Cannot send NULL message.");
return;
}
// calculate the size of that data
unsigned long messageSize = strlen( pBuffer );
// fix our byte ordering
messageSize = htonl( messageSize );
if( isServer == true )
{
for( unsigned int i = ESocket_CLIENT0; i < ESocket_MAX; ++i )
{
// send the message size
if( m_Socket[ i ] > 0 )
{
if( send( m_Socket[ i ], (char*)&messageSize, sizeof( messageSize ), 0 ) == SOCKET_ERROR )
{
Log()->Write( ELogMessageType_ERROR, "[Application] Send error: %i to socket %i", WSAGetLastError(), m_Socket[ i ] );
continue;
}
// fix our message size back to host ordering
messageSize = ntohl(messageSize);
// send the actual message
if( send( m_Socket[ i ], pBuffer, messageSize, 0 ) == SOCKET_ERROR )
{
Log()->Write( ELogMessageType_ERROR, "[Application] Send error: %i to socket %i", WSAGetLastError(), m_Socket[ i ] );
continue;
}
Log()->Write( ELogMessageType_MESSAGE, "[Application] SEND: %s", pBuffer );
}
}
}
You're not handling the case where send() sends less data than you've asked it to. You need to loop if that is the case, until all data has gone out. You're also not handling errors in general, if a client has disconnected, send() might return -1 for instance.
The typical approach is something like::
for(size_t to_go = messageSize; to_go > 0;)
{
int put = send(sock, buf, to_go);
if(put < 0)
{
perror("Socket send() error");
break;
}
buf += put;
to_go -= put;
}
This attempts to send the entire remaining message, until all of it has been sent. You will of course need to adapt for your specific variable names, do better error-handling, and so on; please view the above as a sketch.