I have two machines in the same network :
The first machine binds to a socket on its own IP address (120.0.0.1) and receives any data coming to the socket .bind()-ed on port 5555:
zmq::context_t context{1};
zmq::socket_t socket{context, ZMQ_SUB};
socket.setsockopt(ZMQ_SUBSCRIBE, "lidar");
socket.bind("tcp://120.0.0.1:5555");
while(true)
{
zmq::message_t message;
auto recv = socket.recv(message);
ROS_INFO("Value: %d", recv.value());
}
The second machine, having an IP address 120.0.0.248, connects to the first machine and sends the messages to it:
sock.connect("tcp://120.0.0.1:5555");
while (1) {
double nodes[8192];
sock.send(zmq::buffer("lidar") , zmq::send_flags::sndmore);
sock.send(zmq::buffer(nodes, (((int)(count)) * 8)));
}
But for some reason, I cannot receive any messages on the first machine and it gets stuck on auto recv = socket.recv(message);.
What is a correct way for such communication?
Related
I’m looking to inject IPv6 datagrams available in the user space (and received through a scheme that first requires some unwrapping that's performed in the user space) to a suitable raw socket for further processing by the Linux kernel. This is fairly simple to do with IPv4 using the following code:
int fd=socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
struct sockaddr_ll sa;
memset(sa, 0, sizeof(sa));
// ip4h is the IPv4 datagram unwrapped in the user space and ready to be
// sent to the kernel
if (sendto(fd, iph, iplen, 0, (struct sockaddr *)&sa, sizeof(sa)) != iplen) {
// Error processing.
}
The above injects full IPv4 packets (including the IPv4 headers), and the IPv4 payload gets processed appropriately by the Linux stack. How should the above be modified for use with IPv6 packets? The following adjustments I tried did not work:
int fd=socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL));
sa.sll_family=AF_PACKET;
sa.sll_protocol=htons(ETH_P_IPV6);
sa.sll_halen=ETH_ALEN;
sa.sll_ifindex=2; // <index of eth0>
if (sendto(fd, iph, iplen, 0, (struct sockaddr *)&sa, sizeof(sa)) != iplen) {
// Error processing.
}
Any thoughts on why the above doesn't work with raw IPv6 datagrams? 'tcpdump ip6' does show the IPv6 packets I'm inserting, which suggests the kernel sees them! It just happens to be ignoring them as well.
Test Scenario
I had written a windows program which I simply called it "simpleServer.exe". This program is just a simulation of a very basic server application. It listens on a port, and wait for incoming messages. The listening Socket was defined to be a TCP Stream Socket. that's all that this program is doing.
I had been deploying this exact same program on 2 different machines, both running on windows 7 professional 64bit. This machine will act as a host. and they are stationed in the same network area.
then, using the program "nmap", I used another machine on the same network, to act as a client. using the "-sS" parameter on "nmap", I do a Syn Scan, to the IP and Port of the listening simpleServer on both machine (one attempt at a time).
(note that the 2 hosts already had "wireshark" started, and is monitoring on tcp packets from the client's IP and to the listening port.)
In the "wireshark" entry, on both machine, I saw the expected tcp packet for Syn Scan:
client ----(SYN)----> host
client <--(SYN/ACK)-- host
client ----(RST)----> host
the above packet exchange suggests that the connection was not established.
But on the "simpleServer.exe", only one of it had "new incoming connection" printed in the logs, while the other instance was not alerted of any new incoming connection, hence no logs at all.
Code Snippets
iRetVal = WSAEventSelect (m_Socket, m_hSocketEvent, FD_ACCEPT);
if (SOCKET_ERROR == iRetVal)
{
if (WSAGetLastError()==WSAENOTSOCK)
{
return E_SOCKET_INVALID;
}
CHKLOGGER (m_pLogger->Log (LOGGER_LOG_ERROR,"GHLSocket::OnAccept() Error while WSAEventSelect(). Error code: ", WSAGetLastError() ));
#if defined GHLSOCKET_DEBUG_VERSION
printf ("Error while WSAEventSelect(). Error code: %ld\n", WSAGetLastError() );
#endif
return E_FAILED_RECV_DATA;
}
// Wait for Network Events to occcur
dwRetVal = WSAWaitForMultipleEvents ( 1,
&m_hSocketEvent,
FALSE,
lTimeout,
TRUE);
if ( WSA_WAIT_TIMEOUT == dwRetVal )
{
return E_TIMEOUT;
goto CleanUp;
}
if ( WSA_WAIT_FAILED == dwRetVal)
{
CHKLOGGER (m_pLogger->Log (LOGGER_LOG_ERROR,"GHLSocket::OnAccept() WSAWaitForMultipleEvents() failed. Error code: ", WSAGetLastError() ));
#if defined GHLSOCKET_DEBUG_VERSION
printf ("Error in WSAWaitForMultipleEvents() failed. Error code: %ld\n", WSAGetLastError() );
#endif
dwReturn = E_FAILED_RECV_DATA;
goto CleanUp;
}
// Parse the Results from the Network Events.
iRetVal = WSAEnumNetworkEvents (m_Socket, m_hSocketEvent, &mEvents);
if (SOCKET_ERROR == iRetVal)
{
CHKLOGGER (m_pLogger->Log (LOGGER_LOG_ERROR,"GHLSocket::OnAccept() Error while WSAEnumNetworkEvents(). Error code: ", WSAGetLastError() ));
#if defined GHLSOCKET_DEBUG_VERSION
printf ("Error while WSAEnumNetworkEvents(). Error code: %ld\n", WSAGetLastError() );
#endif
dwReturn = E_FAILED_RECV_DATA;
goto CleanUp;
}
// ACCEPT event Detected.
if (mEvents.lNetworkEvents & FD_ACCEPT)
{
// Perform accept operation.
*p_SOCKET = accept (m_Socket, NULL,NULL);
}
Help That I Needed
why is the different behavior from the 2 same of the same application on a different machine with the same OS?
I'm writing a small application with a client and a server - the client sends a question and the server answers.
I managed to do the first part - the server gets the question from the client, do some work and sends back an answer. I just can't figure out how to tell the client to wait for a response from the server.
This is my client code:
char* ipAddress = (char*)malloc(15);
wcstombs(ipAddress, (TCHAR*)argv[1], 15);
DWORD port = wcstod(argv[2], _T('\0'));
DWORD numOfThreads = wcstod(argv[3], _T('\0;'));
DWORD method = wcstod(argv[4], _T('\0;'));
//initialize windows sockets service
WSADATA wsaData;
int iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
assert(iResult==NO_ERROR);
//prepare server address
sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(ipAddress);
server_addr.sin_port = htons(port);
//create socket
SOCKET hClientSocket= socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
assert(hClientSocket!=INVALID_SOCKET);
//connect to server
int nRes=connect(hClientSocket, (SOCKADDR*)&server_addr, sizeof(server_addr));
assert(nRes!=SOCKET_ERROR);
char* buf = "GET /count.htm HTTP/1.1\r\nHost: 127.0.0.1:666\r\nAccept: text/html,application/xhtml+xml\r\nAccept-Language: en-us\r\nAccept-Encoding: gzip, deflate\r\nUser-Agent: Mozilla/5.0\r\n\r\n";
int nBytesToSend= strlen(buf);
int iPos=0;
while(nBytesToSend)
{
int nSent=send(hClientSocket,buf,nBytesToSend,0);
assert(nSent!=SOCKET_ERROR);
nBytesToSend-=nSent;
iPos+=nSent;
}
closesocket(hClientSocket);
int nLen = sizeof(server_addr);
SOCKET hRecvSocket=accept(hClientSocket,(SOCKADDR*)&server_addr, &nLen);
assert(hRecvSocket!=INVALID_SOCKET);
//prepare buffer for incoming data
char serverBuff[256];
int nLeft=sizeof(serverBuff);
iPos=0;
do //loop till there are no more data
{
int nNumBytes=recv(hRecvSocket,serverBuff+iPos,nLeft,0);
//check if cleint closed connection
if(!nNumBytes)
break;
assert(nNumBytes!=SOCKET_ERROR);
//update free space and pointer to next byte
nLeft-=nNumBytes;
iPos+=nNumBytes;
}while(1);
The assertion after the SOCKET hRecvSocket=accept(hClientSocket,(SOCKADDR*)&server_addr, &nLen); line fails.
The closesocket and accept call after your "send" loop - remove those calls. accept is for servers listening for incoming connections, not for clients that are already connected.
After your send() loop completes, go straight into your recv() loop. That should solve your immediate problem:
Also, your send loop is forgetting to referenece iPos on the buffer like I think you intended to. This is what you wanted:
int nSent=send(hClientSocket,buf+iPos,nBytesToSend,0);
In network programming, sockets will fail due to network conditions beyond your control. So "asserts" on network calls are not always appropriate. Better to just expect failure and be prepared to handle it. Typically, closing the socket and the active connection is the way to handle most errors.
I have an asynchronous socket and call to connect() + GetLastError() which returns WSA_WOULD_BLOCK, as expected. So I start "receiving/reading" thread and subscribe Event to FD_READ and FD_CLOSE.
The story is: connect will sequentially fail, since Server is not up and running. My understanding that my receiving thread should get FD_CLOSE soon and I need to follow-up with cleaning.
It does not happen. How soon should I receive FD_CLOSE? Is it proper approach? Is there any other way to understand that connect() failed? Shoul I ever receive FD_CLOSE if socket isn't connected?
I do start my receiving thread and subscribe event after successful call to DoConnect() and I am afraid that racing condition prevents me from getting FD_CLOSE.
Here is some code:
int RecvSocketThread::WaitForData()
{
int retVal = 0
while (!retVal)
{
// sockets to pool can be added on other threads.
// please validate that all of them in the pool are connected
// before doing any reading on them
retVal = DoWaitForData();
}
}
int RecvSocketThread::DoWaitForData()
{
// before waiting for incoming data, check if all sockets are connected
WaitForPendingConnection_DoForAllSocketsInThePool();
// other routine to read (FD_READ) or react to FD_CLOSE
// create array of event (each per socket) and wait
}
void RecvSocketThread::WaitForPendingConnection_DoForAllSocketsInThePool()
{
// create array and set it for events associated with pending connect sockets
HANDLE* EventArray = NULL;
int counter = 0;
EventArray = new HANDLE[m_RecvSocketInfoPool.size()];
// add those event whose associated socket is still not connected
// and wait for FD_WRITE and FD_CLOSE. At the end of this function
// don't forget to switch them to FD_READ and FD_CLOSE
while (it != m_RecvSocketInfoPool.end())
{
RecvSocketInfo* recvSocketInfo = it->second;
if (!IsEventSet(recvSocketInfo->m_Connected, &retVal2))
{
::WSAEventSelect(recvSocketInfo->m_WorkerSocket, recvSocketInfo->m_Event, FD_WRITE | FD_CLOSE);
EventArray[counter++] = recvSocketInfo->m_Event;
}
++it;
}
if (counter)
{
DWORD indexSignaled = WaitForMultipleObjects(counter, EventArray, WaitAtLeastOneEvent, INFINITE);
// no matter what is further Wait doen't return for failed to connect socket
if (WAIT_OBJECT_0 <= indexSignaled &&
indexSignaled < (WAIT_OBJECT_0 + counter))
{
it = m_RecvSocketInfoPool.begin();
while (it != m_RecvSocketInfoPool.end())
{
RecvSocketInfo* recvSocketInfo = it->second;
if (IsEventSet(recvSocketInfo->m_Event, NULL))
{
rc = WSAEnumNetworkEvents(recvSocketInfo->m_WorkerSocket,
recvSocketInfo->m_Event, &networkEvents);
// Check recvSocketInfo->m_Event using WSAEnumnetworkevents
// for FD_CLOSE using FD_CLOSE_BIT
if ((networkEvents.lNetworkEvents & FD_CLOSE))
{
recvSocketInfo->m_FD_CLOSE_Recieved = 1;
*retVal = networkEvents.iErrorCode[FD_CLOSE_BIT];
}
if ((networkEvents.lNetworkEvents & FD_WRITE))
{
WSASetEvent(recvSocketInfo->m_Connected);
*retVal = networkEvents.iErrorCode[FD_WRITE_BIT];
}
}
++it;
}
}
// if error - DoClean, if FD_WRITE (socket is writable) check if m_Connected
// before do any sending
}
}
You will not receive an FD_CLOSE notification if connect() fails. You must subscribe to FD_CONNECT to detect that. This is clearly stated in the connect() documentation:
With a nonblocking socket, the connection attempt cannot be completed
immediately. In this case, connect will return SOCKET_ERROR, and
WSAGetLastError will return WSAEWOULDBLOCK. In this case, there are
three possible scenarios:
•Use the select function to determine the completion of the
connection request by checking to see if the socket is writeable.
•If the application is using WSAAsyncSelect to indicate interest in
connection events, then the application will receive an FD_CONNECT
notification indicating that the connect operation is complete
(successfully or not).
•If the application is using WSAEventSelect to indicate interest in
connection events, then the associated event object will be signaled
indicating that the connect operation is complete (successfully or
not).
The result code of connect() will be in the event's HIWORD(lParam) value when LOWORD(lParam) is FD_CONNECT. If the result code is 0, connect() was successful, otherwise it will be a WinSock error code.
If you call connect() and get a blocking notification you have to write more code to monitor for connect() completion (success or failure) via one of three methods as described here.
With a nonblocking socket, the connection attempt cannot be completed
immediately. In this case, connect will return SOCKET_ERROR, and
WSAGetLastError will return WSAEWOULDBLOCK. In this case, there are
three possible scenarios:
•Use the select function to determine the completion of the connection
request by checking to see if the socket is writeable.
•If the
application is using WSAAsyncSelect to indicate interest in connection
events, then the application will receive an FD_CONNECT notification
indicating that the connect operation is complete (successfully or
not).
•If the application is using WSAEventSelect to indicate interest
in connection events, then the associated event object will be
signaled indicating that the connect operation is complete
(successfully or not).
I think I need to start Receving thread once socket handle is created, but before connect is called. It is too late to create it after connect was called on asynchronous socket.
For synchronous socket those two calls createsocket() and connect() was just two consequitive lines. Does not work for non-blocking.
In this case at the beginning of receiving thread I need to check for FD_CONNECT and/or FD_WRITE in order be informed of connect attempt status.
Is there a reliable way in Windows, apart from changing the routing table, to force a newly created socket to use a specific network interface? I understand that bind() to the interface's IP address does not guarantee this.
(Ok second time lucky..)
FYI there's another question here perform connect() on specific network adapter along the same lines...
According to The Cable Guy
Windows XP and Windows Server® 2003
use the weak host model for sends and
receives for all IPv4 interfaces and
the strong host model for sends and
receives for all IPv6 interfaces. You
cannot configure this behavior. The
Next Generation TCP/IP stack in
Windows Vista and Windows Server 2008
supports strong host sends and
receives for both IPv4 and IPv6 by
default on all interfaces except the
Teredo tunneling interface for a
Teredo host-specific relay.
So to answer your question (properly, this time) in Windows XP and Windows Server 2003 IP4 no, but for IP6 yes. And for Windows Vista and Windows 2008 yes (except for certain circumstances).
Also from http://www.codeguru.com/forum/showthread.php?t=487139
On Windows, a call to bind() affects
card selection only incoming traffic,
not outgoing traffic. Thus, on a
client running in a multi-homed system
(i.e., more than one interface card),
it's the network stack that selects
the card to use, and it makes its
selection based solely on the
destination IP, which in turn is based
on the routing table. A call to bind()
will not affect the choice of the card
in any way.
It's got something to do with
something called a "Weak End System"
("Weak E/S") model. Vista changed to a
strong E/S model, so the issue might
not arise under Vista. But all prior
versions of Windows used the weak E/S
model.
With a weak E/S model, it's the
routing table that decides which card
is used for outgoing traffic in a
multihomed system.
See if these threads offer some
insight:
"Local socket binding on multihomed
host in Windows XP does not work" at
http://www.codeguru.com/forum/showthread.php?t=452337
"How to connect a port to a specified
Networkcard?" at
http://www.codeguru.com/forum/showthread.php?t=451117.
This thread mentions the
CreateIpForwardEntry() function, which
(I think) can be used to create an
entry in the routing table so that all
outgoing IP traffic with a specified
server is routed via a specified
adapter.
"Working with 2 Ethernet cards" at
http://www.codeguru.com/forum/showthread.php?t=448863
"Strange bind behavior on multihomed
system" at
http://www.codeguru.com/forum/showthread.php?t=452368
Hope that helps!
I'm not sure why you say bind is not working reliably. Granted I have not done exhaustive testing, but the following solution worked for me (Win10, Visual Studio 2019). I needed to send a broadcast message via a particular NIC, where multiple NICs might be present on a computer. In the snippet below, I want the broadcast message to go out on the NIC with IP of .202.106.
In summary:
create a socket
create a sockaddr_in address with the IP address of the NIC you want to send FROM
bind the socket to that FROM sockaddr_in
create another sockaddr_in with the IP of your broadcast address (255.255.255.255)
do a sendto, passing the socket created is step 1, and the sockaddr of the broadcast address.
`
static WSADATA wsaData;
static int ServoSendPort = 8888;
static char ServoSendNetwork[] = "192.168.202.106";
static char ServoSendBroadcast[] = "192.168.255.255";
`
... < snip >
if ( WSAStartup(MAKEWORD(2,2), &wsaData) != NO_ERROR )
return false;
// Make a UDP socket
SOCKET ServoSendSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
int iOptVal = TRUE;
int iOptLen = sizeof(int);
int RetVal = setsockopt(ServoSendSocket, SOL_SOCKET, SO_BROADCAST, (char*)&iOptVal, iOptLen);
// Bind it to a particular interface
sockaddr_in ServoBindAddr={0};
ServoBindAddr.sin_family = AF_INET;
ServoBindAddr.sin_addr.s_addr = inet_addr( ServoSendNetwork ); // target NIC
ServoBindAddr.sin_port = htons( ServoSendPort );
int bindRetVal = bind( ServoSendSocket, (sockaddr*) &ServoBindAddr, sizeof(ServoBindAddr) );
if (bindRetVal == SOCKET_ERROR )
{
int ErrorCode = WSAGetLastError();
CString errMsg;
errMsg.Format ( _T("rats! bind() didn't work! Error code %d\n"), ErrorCode );
OutputDebugString( errMsg );
}
// now create the address to send to...
sockaddr_in ServoSendAddr={0};
ServoSendAddr.sin_family = AF_INET;
ServoSendAddr.sin_addr.s_addr = inet_addr( ServoSendBroadcast ); //
ServoSendAddr.sin_port = htons( ServoSendPort );
...
#define NUM_BYTES_SERVO_SEND 20
unsigned char sendBuf[NUM_BYTES_SERVO_SEND];
int BufLen = NUM_BYTES_SERVO_SEND;
ServoSocketStatus = sendto(ServoSendSocket, (char*)sendBuf, BufLen, 0, (SOCKADDR *) &ServoSendAddr, sizeof(ServoSendAddr));
if(ServoSocketStatus == SOCKET_ERROR)
{
ServoUdpSendBytes = WSAGetLastError();
CString message;
message.Format(_T("Error transmitting UDP message to Servo Controller: %d."), ServoSocketStatus);
OutputDebugString(message);
return false;
}