lwip board cannot maintain connection to another lwip board - c

I have a strange problem. For some time I've been trying to replace a small protocol converter (basically a two way serial to ethernet ... master and slave) that I've got for something that has more features.
Backstory
After a lot of reverse engineering I found out how the device works and I've been trying to replicate it and I've been successful in connecting my board to the device ... I've tried connecting the original as the master and my board as slave and vice versa and everything works perfectly, it's actually better since at higher speeds there are no more packet losses (connecting 2 original ones would cause packet losses).
However when I tried connecting my device as master and another one of my devices as slave .. running the exact same piece of code it works for 2 or 3 exchanges and then it stops ... eventually SOMETIMES after some minutes it will try again 2 or 3 more times.
How the tests were made
I connected a modbus master and slave (modbustools, two different instances). The master is a serial RTU modbus and the slave is an serial RTU modbus;
I configure one of my devices as master and connect it to the serial port so that it receives the serial modbus and sends the protocol to a device connected to it;
I configure my slave so that it connects via the serial port to the slave modbus. Basically it works by creating a socket and connecting to the master's IP, it then waits for a master transmission via ethernet, sends it via serial to the slave modbus (modbustools), receives a response, sends it its master and then it sends it to the modbus master (modbustools);
I's a bit confusing but that's how it works ... my master awaits a socket connection and then the communication between them starts, because that is how the old ones work.
I've written an echo client now to test the connection. Basically now, my code connects to a server (my master), it receives a packet, then it replies back the same packet that it received. When I try connecting this to my 2 boards they don't work. It's more of the same, 2 or 3 exchanges and then it stops, but when I connect it to the original device it keeps running without a hitch.
Sources
Here is my TCP master (server actually) initialization:
void initClient() {
if(tcp_modbus == NULL) {
tcp_modbus = tcp_new();
previousPort = port;
tcp_bind(tcp_modbus, IP_ADDR_ANY, port);
tcp_sent(tcp_modbus, sent);
tcp_poll(tcp_modbus, poll, 2);
tcp_setprio(tcp_modbus, 128);
tcp_err(tcp_modbus, error);
tcp_modbus = tcp_listen(tcp_modbus);
tcp_modbus->so_options |= SOF_KEEPALIVE; // enable keep-alive
tcp_modbus->keep_intvl = 1000; // sends keep-alive every second
tcp_accept(tcp_modbus, acceptmodbus);
isListening = true;
}
}
static err_t acceptmodbus(void *arg, struct tcp_pcb *pcb, err_t err) {
tcp_arg(pcb, pcb);
/* Set up the various callback functions */
tcp_recv(pcb, modbusrcv);
tcp_err(pcb, error);
tcp_accepted(pcb);
gb_ClientHasConnected = true;
}
//receives the packet, puts it in an array "ptransparentmessage->data"
//states which PCB to use in order to reply and the length that was received
static err_t modbusrcv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) {
if(p == NULL) {
return ERR_OK;
} else if(err != ERR_OK) {
return err;
}
tcp_recved(pcb, p->len);
memcpy(ptransparent.data, p->payload,p->len);
ptransparent->pcb = pcb;
ptransparent->len = p->len;
}
The serial reception is basically this:
detect one byte received, start timeout, when timeout ends send whatever was received via a TCP socket that was already connected to the server .. it then receives the packet via the acceptmodbus function and sends it via serial port.
This is my client's (slave) code:
void init_slave() {
if(tcp_client == NULL) {
tcp_client = tcp_new();
tcp_bind(tcp_client, IP_ADDR_ANY, 0);
tcp_arg(tcp_client, NULL);
tcp_recv(tcp_client, modbusrcv);
tcp_sent(tcp_client, sent);
tcp_client->so_options |= SOF_KEEPALIVE; // enable keep-alive
tcp_client->keep_intvl = 100; // sends keep-alive every 100 mili seconds
tcp_err(tcp_client, error);
err_t ret = tcp_connect(tcp_client, &addr, portCnt, connected);
}
}
The rest of the code is the identical. The only thing that changes is the flow of operation.
Connect to server
Wait for packet
send it via serial
wait for response timeout (same timeout as the server, it justs starts counting in a different way ... server starts after receiving one byte and client after it sent something via the serial port)
get response and send it to the server
Observation:
No error is detected in the communication. After some testing it doesn't seem to be the number of exchanges that causes the hang. It happens after some time. In my opinion this sounds like a disconnection problem or timeout error, but no disconnection occurs and no more packets are received. When I stop debugging and check the sockets nothing out of the ordinary is detected.

If I understood your question the right way, you have a computer with two serial ports, each running a Modbus client and server instance. From each of these ends, you then go to your STM32 boards that receive data on their serial ports and forward to TCP on an Ethernet network connecting them to each other.
Not easy to say but based on the symptoms you describe it certainly looks like you are having one or several timeout issues, likely on the serial sides. I think it won't be easy to help you pinpoint what is exactly wrong with your code without testing it and certainly not if you can't show a complete functional piece.
But what you can improve a lot is the way you debug on the end sides.
You can try replacing modbustools with something that gives you more details.
The easiest solution to get additional debugging info is to use pymodbus, you just need to install the library with pip and use the client and server provided with the examples. The only modification you need is to change them to the serial interface commenting and uncommenting a couple of lines. This will give you very useful details for debugging.
If you have a C development environment on your computer better go for libmodbus. This library has a fantastic set of unit tests. Again, you just have to edit the code to set the name of your serial ports and run server and client.
Lastly, I don't know to what extent this might be useful for you but you might want to take a look at SerialPCAP. With this tool, you can tap on an RS-485 bus and see all queries and responses running on it. I imagine you have RS-232, which is point-to-point and will not work with three devices on the bus. If so, you can try port forwarding.
EDIT: Reading your question more carefully I find this sentence particularly troublesome:
...detect one byte received, start timeout, when timeout ends send whatever was received via a TCP socket that was already connected to the server...
Why would you need to introduce this artificial delay? In Modbus, you have very well defined packages that you can identify by the minimum 3.5 frame spacing, is that what you mean by timeout?
Unrelated, but I've also remembered there is a serial forwarder example inluded with pymodbus that might somehow help you (maybe you can use it to emulate one of your boards?).

Related

STM32 Interrupt driven UART receival fails after several flawless receives

Please note the clarification and update at the end of the post
TL;DR: An STM32 has 3 UART connections, 1 for debugging and 2 for actual communication which use the interrupt-driven HAL_UART_Receive_IT. Initially, interrupt driven UART receive works fine, though over time the receive callback for one of the UARTs fires less and less until eventually the STM32 doesn't receive any packets on that one UART at all (despite me being able to verify that they were sent). I suspect the issue to be timing related.
Situation: As part of my thesis, I developed a novel protocol which now has to be implemented and tested. It involves two classes of actors, a server and devices. A device consists of an STM32, ESP32 and a UART to Ethernet bridge. The STM32 is connected via UART to the bridge and via UART to the ESP32. The bridge connects the STM32 to the server by converting serial data sent by the STM32 to TCP packets which it forwards to the server (and vice versa). The ESP32 receives framed packets from the STM32, broadcasts them via BLE and forwards all received and well-formed BLE packets to the STM32. I.e. the ESP32 is just a BLE bridge. The server and ESP32 seem to be working flawlessly.
In a nutshell, the server tries to find out which devices D_j can hear BLE advertisements from device D_i. The server does that by periodically iterating over all devices D_1, ..., D_n and sends them nonces Y_1, ..., Y_n encrypted as X_1, ..., X_n. Upon D_i receiving X_i, it decrypts it to get Y_i, which it then forwards to the ESP32 to be broadcasted via BLE. Conversely, whenever the STM32 receives a packet from the ESP32 (i.e. a packet broadcasted via BLE), it extracts some data, encrypts it and forwards it to the server.
After the server has iterated over all devices, it looks at all the messages it received during that round. If it e.g. received a message with value Y_i sent by D_j, it can deduce that D_i's broadcast somehow arrived at D_j.
Problem: The way I have it set up right now, each STM32 seems to occasionally "miss" messages sent by the ESP32. The more such devices I have in my setup, the worse it gets! With just two devices, the protocol works 100% of the time. With three devices, it also seems to work fine. However, with four devices the STM32's UART receive callback for the ESP32 works fine initially, but after a couple of such rounds it doesn't trigger all the time until eventually it doesn't trigger at all.
Visualization:
The below picture shows a sample topology of n devices. Not drawn here, but if e.g. D_1 was to receive Y_2, it would encrypt it to X_2' and send it across the bridge to the server.
N.B.:
Encryption and Decryption each take ca. 130ms
Average one way delay for one ESP32 receiving packet, broadcasting it and another ESP32 receiving is ca. 15ms
I am aware that UART is not a reliable protocol per se and that one should use framing in a real setting. Nevertheless, I was instructed to just assume that UART is perfect and doesn't drop anything.
Due to the larger scope of the project, using an RTOS is not an option
Code:
#define LEN_SERVER_FRAMED_PACKET 35
#define LEN_BLE_PACKET 24
volatile bool_t new_server_msg;
volatile bool_t new_ble_msg;
byte_t s_rx_framed_buf[LEN_SERVER_FRAMED_PACKET]; // Receive buffer to be used in all subsequent Server send operations
ble_packet_t ble_rx_struct; // A struct. The whole struct is then interpreted as uint8_t ptr. when being sent to the ESP32 over UART
Init:
< set up some stuff>
err = HAL_UART_Receive_IT(&SERVER_UART, s_rx_framed_buf, LEN_SERVER_FRAMED_PACKET);
if (!check_success_hal("Init, setting Server ISR", __LINE__, err)){
print_string("Init after Signup: Was NOT able to set SERVER_UART ISR");
}else{
print_string("Init after Signup: Was able to set SERVER_UART ISR");
}
err = HAL_UART_Receive_IT(&BLE_UART, &ble_rx_struct, LEN_BLE_PACKET);
if(!check_success_hal("Init, setting BLE ISR", __LINE__, err)){
print_string("Init after Signup: Was NOT able to set BLE_UART ISR");
}else{
print_string("Init after Signup: Was able to set BLE_UART ISR");
}
Main loop:
while (1)
{
// (2) Go over all 3 cases: New local alert, new BLE message and new Server message and handle them accordingly
// (2.1) Check whether a new local alert has come in
if (<something irrelevant happens>)
{
<do something irrelevant>
}
// (2.2) Check for new ble packet. Technically it checks for packets from the UART to the ESP32.
if (new_ble_msg)
{
new_ble_msg = FALSE;
int ble_rx_type_code = ble_parse_packet(&ble_rx_nonce, &ble_rx_struct);
HAL_UART_Receive_IT(&BLE_UART, &ble_rx_struct, LEN_BLE_PACKET); // Listen for new BLE messages.
<compute some stuff, rather quick> server_tx_encrypted(<stuff computed>, &c_write, "BLE", __LINE__); // Encrypts <stuff computed> and sends it to the server using a BLOCKING HAL_UART_Transmit(...).
// Encryption takes ca. 130ms.
}
// (2.3) Check for new server packet
if (new_server_msg)
{
new_server_msg = FALSE; // Set flag to false
memcpy(s_wx_framed_buf, s_rx_framed_buf, LEN_SERVER_FRAMED_PACKET); // Copy from framed receive buffer to framed working buffer.
// This is done such that we can process the current message while also being able to receive new messages
HAL_UART_Receive_IT(&SERVER_UART, s_rx_framed_buf, LEN_SERVER_FRAMED_PACKET); // Listen for new server messages.
<decrypt it, takes ca.130 - 150ms. results in buffer ble_tx_struct>
err = HAL_UART_Transmit(&BLE_UART, ble_tx_struct,
LEN_BLE_PACKET, UART_TX_TIMEOUT);
check_success_hal(err); // If unsuccessful, print that to debug UART
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
UART receive callback function:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart == &SERVER_UART)
{ // One should technically compate huart -> Instance, but that works aswell...
new_server_msg = TRUE;
print_string("UART Callback: Server ISR happened!\r\n"); // Blocking write to debug UART. I know that this is typically considered bad form,
// but as the callback function is only called once per receive and because that's the only way of letting me know that the callback has occurred,
// I chose to keep the print in.
}
else if (huart == &BLE_UART)
{
new_ble_msg = TRUE;
print_string("UART Callback: BLE ISR happened!\r\n");
}
else
{
print_string("UART Callback: ISR triggered by unknown UART_HandleTypeDef!\r\n");
}
}
What I have tried so far:
I wrote a client implementation in Go and ran it on my computer, where clients would just directly send UDP messages to each other instead of BLE. As that version functioned flawlessly even with many "devices", I am confident that the problem lies squarely at the STM32 and its STM32 <-> ESP32 UART connection.
To get it working with 3 devices, I simply removed most of the debugging statements of the STM32 and made the server wait 250ms between sending X_i to D_{i} and X_{i + 1} to D_{i + 1}. As this seems to have at least made the problem so infrequent that I haven't noticed it anymore, I reckon that the core issue is timing related.
Through drawing execution traces, I have already found an inherent weakness to my approach: if an STM32 calls HAL_UART_Receive_it(&BLE_UART, ble_rx_buf, LEN_BLE_PACKET) while the ESP32 is currently transmitting a packet to the STM and has already sent k bytes, the STM32 will only receive LEN_BLE_PACKET - k bytes. This causes the BLE_UART.RxXferCount to be wrong for when the next packet is sent by the ESP32.
On a more theoretical front, I first considered doing DMA instead of interrupt driven receive. I then refrained however, as in the STM32 DMA doesn't use descriptor rings like in more powerful systems but instead really just removes the overhead from having to receive LEN_BLE_PACKET (resp LEN_SERVER_FRAMED_PACKET) interrupts.
I have also already of course checked stackoverflow, several people seem to have experienced similar issues. E.g. UART receive interrupt stops triggering after several hours of successful receive, "Uart dma receive interrupt stops receiving data after several minutes" .
Questions:
Given what I have described above, how is it possible for the STM32's callback of BLE_UART to simply stop triggering after some time without any apparent reason?
Does it seem plausible that the issue I raised in the last paragraph of "What I have tried so far" is actually the cause of the problem?
How can I fix this issue?
Clarification:
After the server sends a request to a device D_i, the server waits for 250ms before sending the next request to D_{i + 1}. Hence, the D_i has a 250ms transmission window in which no D_j can transmit anything. I.e. when it's D_i's turn to broadcast its nonce, the other devices have to simply receive one UART message.
As the receival from the server is typically rather fast, the decryption takes 130ms and the UART transmit with a baud of 115200 is also quick, this window should be long enough.
UPDATE:
After posting the question, I changed the ESP32 such that BLE packets are not immediately forwarded over UART to the STM32. Instead, they are enqueued and a dedicated task in the ESP32 dequeues them with a minimum 5ms delay between packets. Hence, the STM32 should now have a guaranteed 5ms between each BLE packet. This was done to reduce the burstiness (despite there not actually being any bursts due to what is mentioned in the clarification... I was just desperate). Nevertheless, this seems to have made the STM32 "survive" for longer before the UART receiver locking up.
You need to be very careful especially when using STM32 HAL library for production, the libraries isn't reliable when receiving fast and continuous data from the server or anywhere else.
I will suggest a solution to this problem based on what I did when implementing for similar application. This works well for my Firmware-Over-The-Air(FOTA) project and helps to eliminate any possible UART failures when using STM32 HAL library.
Steps are listed below:
Ensure you reset the UART by calling MX_USARTx_UART_Init()
Reconfigure the callback either for HAL_UART_Receive_IT() or HAL_UART_Receive_DMA()
This two settings would eliminate any UART failure for receive interrupt using STM32 HAL.

Sending SKB's for transmission from kernel space

I am currently writing a kernel module that modifies packet payloads as a learning experience. I have the packet modifications done, but now I want to send out this new modified packet after the original (I don't want to drop the original). I can't seem to find a kernel function that sends SKB's for transmission. I've tried dev_queue_xmit(nskb) but that causes a kernel panic, I also tried skb->next = nskb but that does nothing. Do I have to implement the SKB list handling? I am unsure of how to do that since this article seems to be outdated .
EDIT:
So I was able to fix the kernel panic when calling dev_queue_xmit(nskb), I was accidentally doing dev_queue_xmit(skb) which would delete skb and cause a panic from net filter. The problem is now that everything works, but I'm not seeing duplicate packets being sent out, there is no trace of the second packet ever being sent. TCPDump on the machine doesn't see anything and TPCDump on the target doesn't see anything either, the following is my code.
unsigned int in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) {
struct sk_buff *nskb = skb_copy(skb, GFP_KERNEL);
/* Various other variables not relevant to the problem */
__u32 saddr, daddr;
saddr = ntohl(iph->saddr);
if (saddr == ipToInt(10,0,2,12) || saddr == ipToInt(10,0,2,13)) {
/*For loop that saves the payload contents into a variable */
/* Here is where the problem is,
I have this if statement to prevent a feedback loop
then if the ip matches, I call dev_queue_xmit(nskb)
which is supposed to send out sk_buff's, but TCPDump doesn't
show anything on any computer */
if (saddr == ipToInt(10,0,2,13)) {
dev_queue_xmit(nskb);
}
/* Rest of the code that isn't relevant to sending packets */
}
return NF_ACCEPT;
}
My network setup is as follows, it's 3 Ubuntu Server VM's, all of them are being SSH'd into from the host computer (macOS if it matters, I don't know at this point). The computer running the above kernel module spoofs bidirectionally the other two VM's. The other two VM's then talk to each other via a netcat session. I'm hoping that when I send one message from the VM with ip 10.0.2.13, that 10.0.2.12 sees two of the same message. I know the acknowledgement number mishap will break the connection, but I'm not getting that. TCPDump on any of the 3 computers doesn't show anything besides the packets that are supposed to be sent.
I have so far tried dev_queue_xmit(nskb) as well as nskb->dev->netdev_ops->ndo_start_xmit(nskb, skb->dev).
As far as I remember dev_queue_xmit() is the right procedure for sending. The question is how do you prepared the skb you want to send? Also give us the calltrace from dmesg when the kernel panic occured. Do you set skb->dev?
I figured it out, skb_copy doesn't copy the ethernet header of an skb, so the sent packet never reaches its destination.

TCP Sockets in C with bad network

I am doing some test with TCP client application in a Raspberry Pi (server in the PC), with PPP (Point to Point Protocol) using a LTE Modem. I have used C program with sockets, checking system call's response. I wanted to test how socket works in a bad coverage area so I did some test removing the antenna.
I have followed the next steps:
Connect to server --> OK
Start sending data (write system call) --> OK (I also check in the server)
I removed the LTE modem's antenna (There is no network, it can't do ping)
Continue sending data (write system call) --> OK (server does not receive anything!!!)
It finished sending data and closed socket --> OK (connection is still opened and there is no data since the antenna was removed)
Program was finished
I put the antenna again
Some time later, the data has been uploaded and the connection closed. But I did another test following this steps but with more data, and it did not upload this data...
I do not know if there any way to ensure that the data written to TCP server is received by the server (I thought that TCP layer ensured this..). I could do it manually using an ACK but I guess that it has to be a better way to do.
Sending part code:
while(i<100)
{
sprintf(buf, "Message %d\n", i);
Return = write(Sock_Fd, buf, strlen(buf));
if(Return!=strlen(buf))
{
printf("Error sending data to TCP server. \n");
printf("Error str: %s \n", strerror(errno));
}
else
{
printf("write successful %d\n", i);
i++;
}
sleep(2);
}
Many thanks for your help.
The write()-syscall returns true, since the kernel buffers the data and puts it in the out-queue of the socket. It is removed from this queue when the data was sent and acked from the peer. When the OutQueue is full, the write-syscall will block.
To determine, if data has not been acked by the peer, you have to look at the size of the outqueue. With linux, you can use an ioctl() for this:
ioctl(fd, SIOCOUTQ, &outqlen);
However, it would be more clean and portable to use an inband method for determining if the data has been received.
TCP/IP is rather primitive technology. Internet may sound newish, but this is really antique stuff. TCP is needed because IP gives almost no guarantees, but TCP doesn't actually add that many guarantees. Its chief function is to turn a packet protocol into a stream protocol. That means TCP guarantees a byte order; no bytes will arrive out of order. Don't count on more than that.
You see that protocols on top of TCP add extra checks. E.g. HTTP has the famous HTTP error codes, precisely because it can't rely on the error state from TCP. You probably have to do the same - or you can consider implementing your service as a HTTP service. "RESTful" refers to an API design methodology which closely follows the HTTP philosophy; this might be relevant to you.
The short answer to your 4th and 5th topics was taken as a shortcut from this answer (read the whole answer to get more info)
A socket has a send buffer and if a call to the send() function succeeds, it does not mean that the requested data has actually really been sent out, it only means the data has been added to the send buffer. For UDP sockets, the data is usually sent pretty soon, if not immediately, but for TCP sockets, there can be a relatively long delay between adding data to the send buffer and having the TCP implementation really send that data. As a result, when you close a TCP socket, there may still be pending data in the send buffer, which has not been sent yet but your code considers it as sent, since the send() call succeeded. If the TCP implementation was closing the socket immediately on your request, all of this data would be lost and your code wouldn't even know about that. TCP is said to be a reliable protocol and losing data just like that is not very reliable. That's why a socket that still has data to send will go into a state called TIME_WAIT when you close it. In that state it will wait until all pending data has been successfully sent or until a timeout is hit, in which case the socket is closed forcefully.
The amount of time the kernel will wait before it closes the socket,
regardless if it still has pending send data or not, is called the
Linger Time.
BTW: that answer also refers to the docs where you can see more detailed info

First UDP message to a specific remote ip gets lost

I am working on a LAN based solution with a "server" that has to control a number of "players"
My protocol of choice is UDP because its easy, I do not need connections, my traffic consists only of short commands from time to time and I want to use a mix of broadcast messages for syncing and single target messages for player individual commands.
Multicast TCP would be an alternative, but its more complicated, not exactly suited for the task and often not well supported by hardware.
Unfortunately I am running into a strange problem:
The first datagram which is sent to a specific ip using "sendto" is lost.
Any datagram sent short time afterwards to the same ip is received.
But if i wait some time (a few minutes) the first "sendto" is lost again.
Broadcast datagrams always work.
Local sends (to the same computer) always work.
I presume the operating system or the router/switch has some translation table from IP to MAC addresses which gets forgotten when not being used for some minutes and that unfortunately causes datagrams to be lost.
I could observe that behaviour with different router/switch hardware, so my suspect is the windows networking layer.
I know that UDP is by definition "unreliable" but I cannot believe that this goes so far that even if the physical connection is working and everything is well defined packets can get lost. Then it would be literally worthless.
Technically I am opening an UDP Socket,
bind it to a port and INADRR_ANY.
Then I am using "sendto" and "recvfrom".
I never do a connect - I dont want to because I have several players. As far as I know UDP should work without connect.
My current workaround is that I regularly send dummy datagrams to all specific player ips - that solves the problem but its somehow "unsatisfying"
Question: Does anybody know that problem? Where does it come from? How can I solve it?
Edit:
I boiled it down to the following test program:
int _tmain(int argc, _TCHAR* argv[])
{
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
SOCKET Sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
SOCKADDR_IN Local = {0};
Local.sin_family = AF_INET;
Local.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
Local.sin_port = htons(1234);
bind(Sock, (SOCKADDR*)&Local, sizeof(Local));
printf("Press any key to send...\n");
int Ret, i = 0;
char Buf[4096];
SOCKADDR_IN Remote = {0};
Remote.sin_family = AF_INET;
Remote.sin_addr.S_un.S_addr = inet_addr("192.168.1.12"); // Replace this with a valid LAN IP which is not the hosts one
Remote.sin_port = htons(1235);
while(true) {
_getch();
sprintf(Buf, "ping %d", ++i);
printf("Multiple sending \"%s\"\n", Buf);
// Ret = connect(Sock, (SOCKADDR*)&Remote, sizeof(Remote));
// if (Ret == SOCKET_ERROR) printf("Connect Error!\n", Buf);
Ret = sendto(Sock, Buf, strlen(Buf), 0, (SOCKADDR*)&Remote, sizeof(Remote));
if (Ret != strlen(Buf)) printf("Send Error!\n", Buf);
Ret = sendto(Sock, Buf, strlen(Buf), 0, (SOCKADDR*)&Remote, sizeof(Remote));
if (Ret != strlen(Buf)) printf("Send Error!\n", Buf);
Ret = sendto(Sock, Buf, strlen(Buf), 0, (SOCKADDR*)&Remote, sizeof(Remote));
if (Ret != strlen(Buf)) printf("Send Error!\n", Buf);
}
return 0;
The Program opens an UDP Socket, and sends 3 datagrams in a row on every keystroke to a specific IP.
Run that whith wireshark observing your UDP traffic, press a key, wait a while and press a key again.
You do not need a receiver on the remote IP, makes no difference, except you wont get the black marked "not reachable" packets.
This is what you get:
As you can see the first sending initiated a ARP search for the IP. While that search was pending the first 2 of the 3 successive sends were lost.
The second keystroke (after the IP search was complete) properly sent 3 messages.
You may now repeat sending messages and it will work until you wait (about a minute until the adress translation gets lost again) then you will see dropouts again.
That means: There is no send buffer when sending UDP messages and there are ARP requests pending! All messages get lost except the last one.
Also "sendto" does not block until it successfully delivered, and there is no error return!
Well, that surprises me and makes me a little bit sad, because it means that I have to live with my current workaround or implement an ACK system that only sends one message at a time and then waits for reply - which would not be easy any more and imply many difficulties.
I'm posting this long after it's been answered by others, but it's directly related.
Winsock drops UDP packets if there's no ARP entry for the destination address (or the gateway for the destination).
Thus it's quite likely some of the first UDP packet gets dropped as at that time there's no ARP entry - and unlike most other operating systems, winsock only queues 1 packet while the the ARP request completes.
This is documented here:
ARP queues only one outbound IP datagram for a specified destination
address while that IP address is being resolved to a MAC address. If a
UDP-based application sends multiple IP datagrams to a single
destination address without any pauses between them, some of the
datagrams may be dropped if there is no ARP cache entry already
present. An application can compensate for this by calling the
Iphlpapi.dll routine SendArp() to establish an ARP cache entry, before
sending the stream of packets.
The same behavior can be observed on Mac OS X and FreeBSD:
When an interface requests a mapping for an address not
in the cache, ARP queues the message which requires the
mapping and broadcasts a message on the associated associated
network requesting the address mapping. If a response is
provided, the new mapping is cached and any pending message is
transmitted. ARP will queue at most one packet while waiting
for a response to a mapping request; only the most recently
``transmitted'' packet is kept.
UDP packets are supposed to be buffered on receipt, but a UDP packet (or the ethernet frame holding it) can be dropped at several points on a given machine:
network card does not have enough space to accept it,
OS network stack does not have enough buffer memory to copy it to,
firewall/packet filtering drop-rule match,
no application is listening on destination IP and port,
receive buffer of the listening application socket is full.
First two points are about too much traffic, which is not likely the case here. Then I trust that point 4. is not applicable and your software is waiting for the data. Point 5. is about your application not processing network data fast enough - also does not seem like the case.
Translation between MAC and IP addresses is done via Address Resolution Protocol. This does not cause packet drop if your network is properly configured.
I would disable Windows firewall and any anti-virus/deep packet inspection software and check what's on the wire with wireshark. This will most likely point you into right direction - if you can sniff those first packets on the "sent-to" machines then check local configuration (firewall, etc.); if you don't, then check your network - something in the path is interfering with your traffic.
Hope this helps.
erm ..... Its your computer doing ARP request. When you first start sending, your com does not know the receiver mac address, hence its unable to send any packets. It uses the ip address of the receiver to do an ARP request to get the mac address. During this process, any udp packets you try to send cannot be send out as the destination mac address is still not known.
Once your com receive the mac address it can start sending. However, the mac address will only remain in you com's ARP cache for 2mins (if no further activities is detected between you and the receiver) or 10 mins (full clear of ARP cache, even if connection is active). That is why you experience this problem every few mins.
Does anybody know that problem?
The real problem is that you assumed that UDP packet sending is reliable. It isn't.
The fact that you lose the first packet with your current network configuration is really a secondary issue. You might be able to fix that, but at any point you are still vulnerable to packet losses.
If packet loss is a problem for you, then you really should use TCP. You can build a reliable protocol on UDP, but unless you have a very good reason to do so, it's no recommended.

Streaming on UDP

I have the following issue here: I want to write a server streaming data on a UDP socket on a specific port, and clients should be able to connect to it and receive the data that is being sent out without too much hassle: they just connect, and from the moment they start they should get data using recvfrom from the server.
I have some problems with setting up the network related parts. So, here is a rough piece of code that I try to make work:
int udpSock = socket(AF_INET, SOCK_DGRAM, 0);
if(udpSock == -1)
{
perror("Could not create audio output socket");
exit(1);
}
struct sockaddr_in *sin = (struct sockaddr_in*)&gOutgoingAddr;
sin->sin_port = htons(40200);
if(bind(udpSock, (const sockaddr*)sin, sizeof(struct sockaddr_in)) == -1)
{
perror("Cannot bind audio socket");
exit(1);
}
int buffer_size = 0;
char* data = get_next_buffer(&buffer_size);
while(buffer_size > 0)
{
if(sendto(udpSock, (const void*)(data), buffer_size, 0, NULL, 0) == -1)
{
perror("sendto failure");
}
data = get_next_buffer(&buffer_size);
}
Do not worry about the gOutgoingAddr variable, it is obtained correctly using getifaddrs, it is valid. I am troubled regarding the parametrization of the sendto method, becasue right now the output of the application is:
sendto failure: Destination address required
That's true, because I don't have a destination address, since all the examples I have found till now show when the server gets a client connection, and there is the address. But since I don't have a client yet connected, I'd still want to stream out.
I appreciate all help, I have no idea what Ishould put for the parameters of sendto:
The gOutgoingAddress which is the address where I create the socket? I have tried this, but if I use the tcpdump linux command on the specified port, I get nothing.
Should I create a multicast socket? This somehow makes no sense...
Something else?
Thanks,
frc
You cannot stream out to "nowhere". Streaming data via UDP is not multicast. That means if you have 100 clients connected, you must send exactly the same data 100-times, once to each of the clients that shall receive it. Multicast was not really part of the initial IPv4 design. It has been added later on and is not widely supported. This is contrary to IPv6, where multicast has been part of the initial design. The only thing you could do it broadcast the traffic within your local network. This will only work if all clients are in your local network segment. To broadcast your server would simply send the data to 255.255.255.255 and to a fixed UDP port. All clients then have to listen on that specific port and will receive the data. Please note, that on most systems you need special permissions for broadcasting (e.g. it is not common that only programs running with root privileges are allowed to broadcast traffic, as broadcasts pollute your network, since all broadcast packets are sent to all clients in the network, whether they care for them or not). Without broadcasts, you have only unicast and unicast means one sender, one receiver. For one sender multiple receiver, you must send out the same data multiple times to multiple addresses.
What is audioUdpSock by the way?
Shoudn't you be using udpSock instead?
Do a recvfrom in your server, and have the client send a message (with whatever content you want, this is just a way to establish the connection, a greeting). Then the server will have the client address from the recvfrom, and can send packet to it.
As UDP socket are connection-less (there is no need for accept and connect when using UDP socket), you need to have another way to inform the server of the existence of the client (and the client need to have an out-of-bound way to know the server address, generally, the user give it, or it is hard-coded).
If you can have multiple clients, then you'll have to use select, poll, ... on the socket to know when it is safe to call recvfrom without blocking (or you could configure your socket to be non-blocking).
Edit: I highly recommend Beej's Guide to Network Programming to everyone, and for your question, you can directly go to the sample usage of Datagram Socket.

Resources