📜 ⬆️ ⬇️

Differences network calls Windows and Linux

image
In many ways, compatible at the source level, the socket models from Berkeley and Microsoft, in practice, are not that cross-platform.

Consider some tricky differences in their implementation, which were revealed when writing a cross-platform RPC to redirect network calls of a process in one OS to another OS.


Socket type

As long as the processor has 32 bits, there are no problems with interconnection. On 64 bits in Windows, the SOCKET type turns out to be 2 times larger.
')
The BSD socket descriptor is no different from a file descriptor, which means that some system calls accept descriptors of both sockets and files at the same time (for example, such rarely used calls like close (), fcntl () and ioctl ()).
There is also a side effect that manifests itself in rather dastardly situations, namely, in systems with Berkeley model support, the numeric value of the socket descriptor is usually a small number (less than 100), and the descriptors that are created in a row differ by 1. In the Microsoft model, such a descriptor is immediately more than 200 (approximately), and successively created descriptors differ in sizeof (SOCKET).


Error processing

The errno constants and Windows error codes have completely different meanings.


Creating sockets:

socket (int af, int type, int protocol);

The constants for the first argument have completely different values ​​on BSD and Windows. For the second, they are the same.


Socket configuration

The flag constants for the second and third arguments have completely different values ​​on BSD and Windows.


Socket Setup 2

The only fully correct mapping: fcnlt (descriptor, F_SETFL, O_NONBLOCK) -> ioctlsocket (descriptor, FIONBIO, the address of the variable with the value O_NONBLOCK). Numeric values ​​of flags should be perceived relative to the target system (they are different on BSD and Windows).
In this case, the query type fcnlt (descriptor, F_GETFL), you can return 0 or O_RDWR.


Setting Sockets 3

No cases of actual use of ioctl () with a socket as a first argument have yet been identified.


Work with DNS

getaddrinfo (char const * node, char const * service, struct addrinfo const * hints, struct addrinfo ** res)

Look carefully at the invariants of these structures. ai_addr and ai_canonname have different offsets relative to the beginning of the structure. The developers simply reversed them (mixed up?).


Data transfer

The flags for the fourth argument have completely different meanings on BSD and Windows.


Waiting for operations

Flag constants for the second and third pollfd structure invariants have completely different values ​​for BSD and Windows. WSAPoll () is available only in Windows 6th version (Vista) and later.


Pending operations 2

The problem in select occurs when the fd_set structure is mapped. Recall how select () works. This call accepts three sets of sockets: for checking read, write, and errors for some time. You can add your socket for checking into one of these sets by using the macro FD_SET (socket, set), check for installed - FD_ISSET (socket, set), remove one socket from the set - FD_CLR (socket, set), delete all - FD_ZERO (set) . After the call, select () leaves in the corresponding sets only those sockets which, during the timeout specified by the last argument, acquired the expected state.

For BSD, placing a socket in a certain set consists in cocking in the last such bit, the sequence number of which is numerically equal to the socket descriptor. FD_SETSIZE is usually 1024. The first select () argument is the maximum numeric value of the socket descriptor included in any of the three sets plus one. Considering that setting the bit in the fds_bits array is done exclusively without checking the range, it becomes clear that when the socket descriptor is> = FD_SETSIZE, the program's behavior is undefined. Such a somewhat unreliable implementation of select is a relic of computers stingy with memory. By the way, this is where the indirect conversion int -> SOCKET and back is important.

For Windows, placing a socket in a set consists of inserting it into the fd_array array at the fd_count index and further increasing the latter. FD_SETSIZE is usually 64. In this case, the first select () argument is ignored altogether.

Source: https://habr.com/ru/post/105918/


All Articles