I'wm writing an app, which transmits video and obviously uses UDP protocol fot this purpose.
So I am wondering how can I increase a size of send/recieve buffer, cause currently the maximal size of data, which I can send is 65000 bytes.
I already tried to do it in following way:
int option = 262144;
if(setsockopt(m_SocketHandle,SOL_SOCKET,SO_RCVBUF ,(char*)&option,sizeof(option)) < 0)
{
printf("setsockopt failed\n");
}
But it did not work. So how can I do it?
How can I do it?
You can't. The maximum size of an IPv4 UDP datagram is 65535-20-8=65507 bytes. Increasing the buffer size cannot change that. Datagrams larger than the path MTU (< 1500 bytes) will be fragmented, and fragmented datagrams are more likely to be lost, statistically, so using datagram sizes up around 64k is contra-indicated anyway.
Related
I have C++ classes that handles sending and receiving UDP packets. So far I used those to send signals (PING, WAKEUP, ...) in other words, very small packets and never had a problem.
Now I'd like to send large blocks of data (i.e. 0.5Mb), but to optimize the possibility of packet losses, I want to be able to do my own fragmentation. First I wrote a function that gives me the MTU size:
int udp_server::get_mtu_size() const
{
if(f_mtu_size == 0)
{
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, "eth0", sizeof(ifr.ifr_name));
if(ioctl(f_socket, SIOCGIFMTU, &ifr) == 0)
{
f_mtu_size = ifr.ifr_mtu;
}
else
{
f_mtu_size = -1;
}
}
return f_mtu_size;
}
Note: I know about PMTUD which this function ignores. As mentioned below, this is to work on a controlled network so the MTU path won't just change on us.
This function is likely to return 1500 under Linux.
What is really not clear and seems contradictory between many answers is that this 1,500 bytes size would not be just my payload. It would possibly include some headers over which I have no control (i.e. Ethernet header + footer, IPv4 header, UDP header.)
From some other questions and answers, it feels like I can send 1,500 bytes of data without fragmentation, assuming all my MTUs are 1,500.
So... Which is true?
My data buffer can have a size equal to MTU
My data buffer must be MTU - sizeof(various-headers/footers)
P.S. The network is a LAN that we control 100%. The packets will travel from one main computer to a set of slave computers using UDP multicast. There is only one 1Gbps switch in between. Nothing more.
The size is very clearly defined in RFC-8085: UDP Usage Guidelines.
https://www.rfc-editor.org/rfc/rfc8085#section-3.2
There is the relevant bit about the size calculation for the payload.
To determine an appropriate UDP payload size, applications MUST subtract the size of the IP header (which includes any IPv4 optional headers or IPv6 extension headers) as well as the length of the UDP header (8 bytes) from the PMTU size. This size, known as the Maximum Segment Size (MSS), can be obtained from the TCP/IP stack [RFC1122].
So in C/C++, this becomes:
#include <netinet/ip.h> // for iphdr
#include <netinet/udp.h> // for udphdr
int mss(udp.get_mtu_size());
mss -= sizeof(iphdr);
mss -= sizeof(udphdr);
WARNING: The size of the IP header varies depending on options. If you use options that will increase the size, your MSS computation must take that in account.
The size of the Ethernet header and footer are not included here because those are transparent to the UDP packet.
Could anyone please help me on improving the performance of a udp receiver. I am only able to get 1Mb/s but need to enhance the performance to almost 5Mb/s. There are also missing of Logs because the receiver is not able to receive all the messages due to this less performance. Is there any tips on how we could we increase the performance. I am using socket calls to get the data packets.
#define MAX_PACKET_SIZE 65535
#define UPD_DATAGRAM_BUFFER_SIZE 1536
m_nSocket = socket(AF_INET, SOCK_DGRAM, 0);
/* Set socket buffer size */
int buffer_size = m_nBufferSize;
ret = setsockopt(m_nSocket, SOL_SOCKET, SO_RCVBUF, (char*) &buffer_size, sizeof(buffer_size));
ret = setsockopt(m_nSocket6, SOL_SOCKET, SO_RCVBUF, (char*) &buffer_size, sizeof(buffer_size));
/* Set socket timeout */
#if defined (WIN32) || defined (WIN64)
int timeout = m_nTimeout;
ret = setsockopt(m_nSocket, SOL_SOCKET, SO_RCVTIMEO, (char*) &timeout, sizeof(timeout));
ret = setsockopt(m_nSocket6, SOL_SOCKET, SO_RCVTIMEO, (char*) &timeout, sizeof(timeout));
#else
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = m_nTimeout * 1000; //must be in microseconds
ret = setsockopt(m_nSocket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
ret = setsockopt(m_nSocket6, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
#endif
//bind
m_address.sin_family = AF_INET;
m_address.sin_addr.s_addr = htonl(INADDR_ANY);
m_address.sin_port = htons(m_nPort);
ret = bind(m_nSocket, (struct sockaddr*) &m_address, sizeof(m_address));
//receive data
recvfrom(m_nSocket, m_sBuffer, UPD_DATAGRAM_BUFFER_SIZE, 0, (struct sockaddr*) &m_address, &server_length);
Does increase in buffer size increase udp performance? What else could we do to increase udp performance?
Increasing the RCVBUF size will not make it faster but more reliable. If the RCVBUF is full, next incoming packets are dropped.
Details:
A recvfrom() call receives exactly one UDP packet which has - for IPv4 - a maximum size of 65535 bytes. UDP packets may be split into fragments, but this is hidden from the user.
If your sendto() call sends more bytes than a single recvfrom() receive buffer accepts, the remaining data will be dropped.
The SO_RCVBUF is accepting packets while no recvfrom() call is running. If you call recvfrom() it checks for a packet in the RCVBUF and only blocks and waits for a new packet if the receive buffer is empty.
If you have a sender sending huge amounts of data for example within a for loop, then it is likely you loose some data if your RCVBUF is not large enough and your recvfrom() calls are not fast enough (i.e. when processing packets between revfrom() calls).
UDP is not made for burst transfers. It even does not guarantee the delivery of a packet and the packet receive order may differ from the send order.
Maybe you should use TCP/IP ?
If you try to implement your own UDP based stream communication you could do the following:
1) Send UDP packets with a maximum size of about 1400 Bytes.
2) Add a 32 or 64-Bit Header to your UDP packets containing the stream offset where this packet belongs to (i.e. the first 1400 byte packet has a stream offset of 0 and the second one has an offset of 1400, the third one 2800 and so on)
3) The client allocates a buffer huge enough to store the whole transmission. Every packet is copied into this buffer at the location specified within the first 32 or 64 bit of the packet. (This sorts your packet)
4) The server sends only loads of - for example - 10 MiB and the client requests more data while it reads from the RCVBUF (using recvfrom()). So the server does not fill the RCVBUF and no packets are being dropped by the receiving machine. For maximum performance the client should request the next load while still receiving data from the previous load. (This makes sure the receive buffer does not overflow)
5) The client request the retransmission of any missing packets (this can be combined with the request in step 4) (This makes sure the transmission is complete and no packets are lost)
Why only 1400 bytes? Because you don't want to fragment your packets on high speed networks. (On fast networks the 16-bit packet-id can overflow within the reassembly timeframe and - if the checksum matches or is not set - fragments of different packets could be reassembled. Took me hours to find out why)
Im trying to send a TCP packet from client to server, I want to be able to select the size of the packet. The problem Im facĂng is that the packet size is not correct if I dont put a Sleep(45) (milliseconds). Im using Wireshark to see the size of the packets.
To make sure u guys are with me Im going to explain as clear as possible.
I have tried to do like this..
First I select the amount a data I want to send. For example say 1Mb or "1000000ish"bytes. I allocate an array with so much space.
To be able to send a specific packet size I have allocate a sendbuffer which contains the size of the packet I want (my case 64, 512, 1024 and 1514 bytes). I fill the sendbuffer with letters. Say I want to send with 64 as packet size.
for (int i = 0; i < packetSize; i++){
sendbuf[i] = 'a';
}
To know how many times I have to send a packet to reach 1Mb I done this math.
nrOfTimes = (dataSize / packetSize).
Then to send it with a loop.
for (int i = 0; i < nrOfTimes; i++) {
rc = send(sConnect, sendbuf, packetSize, 0); //rc and sConnect contains information where to send the data, if you wonder
Sleep(45); // If i dont use this the packet size gets 1514.
}
If I use the sleep(45) its working but its takes years to finished and Im supposed to measure the time so its incorrect to do like this. if I go lower then Sleep(45) then my network card ignors the packets size and put it to 1514 size.
Is there anyone who has some clear ideas what to do? I can only assume it might have something to do with the network card buffer.
TCP is a byte streaming protocol so it is incorrect to think of transmission in terms of discrete packets of bytes. It is likely you are interacting with the Nagle algorithm by injecting the 45 ms delays.
How do I set the MTU value pragmatically?
Apparently there are three candidates: IP_MTU, TCP_MSS, and TCP_MAXSEG
I tried:
uint16_t tcp_mss = 2048 * 8;
setsockopt(sock_descriptor, IPPROTO_TCP, TCP_MSS, &tcp_mss, sizeof(tcp_mss));
My intention is to increase the amount of data the TCP buffer should have before sending the packet. TCP_CORK is in use.
Also helps if you know how to change the time from the apparent default of 200ms.
Based on my understanding, each socket is associated with two buffers, a send buffer and a receive buffer, so when I call the send() function, what happens is that the data to send will be placed into the send buffer, and it is the responsibility of Windows now to send the content of this send buffer to the other end.
In a blocking socket, the send() function does not return until the entire data supplied to it has been placed into the send buffer.
So what is the size of the send buffer?
I performed the following test (sending 1 GB worth of data):
#include <stdio.h>
#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib")
#include <Windows.h>
int main()
{
// Initialize Winsock
WSADATA wsa;
WSAStartup(MAKEWORD(2, 2), &wsa);
// Create socket
SOCKET s = socket(AF_INET, SOCK_STREAM, 0);
//----------------------
// Connect to 192.168.1.7:12345
sockaddr_in address;
address.sin_family = AF_INET;
address.sin_addr.s_addr = inet_addr("192.168.1.7");
address.sin_port = htons(12345);
connect(s, (sockaddr*)&address, sizeof(address));
//----------------------
// Create 1 GB buffer ("AAAAAA...A")
char *buffer = new char[1073741824];
memset(buffer, 0x41, 1073741824);
// Send buffer
int i = send(s, buffer, 1073741824, 0);
printf("send() has returned\nReturn value: %d\nWSAGetLastError(): %d\n", i, WSAGetLastError());
//----------------------
getchar();
return 0;
}
Output:
send() has returned
Return value: 1073741824
WSAGetLastError(): 0
send() has returned immediately, does this means that the send buffer has a size of at least 1 GB?
This is some information about the test:
I am using a TCP blocking socket.
I have connected to a LAN machine.
Client Windows version: Windows 7 Ultimate 64-bit.
Server Windows version: Windows XP SP2 32-bit (installed on Virtual Box).
Edit: I have also attempted to connect to Google (173.194.116.18:80) and I got the same results.
Edit 2: I have discovered something strange, setting the send buffer to a value between 64 KB and 130 KB will make send() work as expected!
int send_buffer = 64 * 1024; // 64 KB
int send_buffer_sizeof = sizeof(int);
setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char*)send_buffer, send_buffer_sizeof);
Edit 3: It turned out (thanks to Harry Johnston) that I have used setsockopt() in an incorrect way, this is how it is used:
setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char*)&send_buffer, send_buffer_sizeof);
Setting the send buffer to a value between 64 KB and 130 KB does not make send() work as expected, but rather setting the send buffer to 0 makes it block (this is what I noticed anyway, I don't have any documentation for this behavior).
So my question now is: where can I find a documentation on how send() (and maybe other socket operations) work under Windows?
After investigating on this subject. This is what I believe to be the correct answer:
When calling send(), there are two things that could happen:
If there are pending data which are below SO_SNDBUF, then send() would return immediately (and it does not matter whether you are sending 5 KB or you are sending 500 MB).
If there are pending data which are above or equal SO_SNDBUF, then send() would block until enough data has been sent to restore the pending data to below SO_SNDBUF.
Note that this behavior is only applicable to Windows sockets, and not to POSIX sockets. I think that POSIX sockets only use one fixed sized send buffer (correct me if I'm wrong).
Now back to your main question "What is the size of a socket send buffer in Windows?". I guess if you have enough memory it could grow beyond 1 GB if necessary (not sure what is the maximum limit though).
I can reproduce this behaviour, and using Resource Monitor it is easy to see that Windows does indeed allocate 1GB of buffer space when the send() occurs.
An interesting feature is that if you do a second send immediately after the first one, that call does not return until both sends have completed. The buffer space from the first send is released once that send has completed, but the second send() continues to block until all the data has been transferred.
I suspect the difference in behaviour is because the second call to send() was already blocking when the first send completed. The third call to send() returns immediately (and 1GB of buffer space is allocated) just as the first one did, and so on, alternating.
So I conclude that the answer to the question ("how large are the send buffers?") is "as large as Windows sees fit". The upshot is that, in order to avoid exhausting the system memory, you should probably restrict blocking sends to no more than a few hundred megabytes.
Your call to setsockopt() is incorrect; the fourth argument is supposed to be a pointer to an integer, not an integer converted to a pointer. Once this is corrected, it turns out that setting the buffer size to zero causes send() to always block.
To summarize, the observed behaviour is that send() will return immediately provided:
there is enough memory to buffer all the provided data
there is not a send already in progress
the buffer size is not set to zero
Otherwise, it will return once the data has been sent.
KB214397 describes some of this - thanks Hans! In particular it describes that setting the buffer size to zero disables Winsock buffering, and comments that "If necessary, Winsock can buffer significantly more than the SO_SNDBUF buffer size."
(The completion notification described does not quite match up to the observed behaviour, depending I guess on how you interpret "previously buffered send". But it's close.)
Note that apart from the risk of inadvertently exhausting the system memory, none of this should matter. If you really need to know whether the code at the other end has received all your data yet, the only reliable way to do that is to get it to tell you.
In a blocking socket, the send() function does not return until the entire data supplied to it has been placed into the send buffer.
That is not guaranteed. If there is available buffer space, but not enough space for the entire data, the socket can (and usually will) accept whatever data it can and ignore the rest. The return value of send() tells you how many bytes were actually accepted. You have to call send() again to send the remaining data.
So what is the size of the send buffer?
Use getsockopt() with the SO_SNDBUF option to find out.
Use setsockopt() with the SO_SNDBUF option to specify your own buffer size. However, the socket may impose a max cap on the value you specify. Use getsockopt() to find out what size was actually assigned.