I am writing a set of tests that depend on two applications communicating (locally) with a UDP socket. These UDP sockets are originally bound using port 0, then the actual bound port is queried using getsockname and shared between the two applications. Occasionally, the subsequent recv calls fail to return any data without any error reporting during binding etc.
I've simplified my implementation to a bare-bones test an included it below. It fails with "Received failed, got -1 expeccted 6" about 1 of 10 runs. What am I missing in the socket setup to reliable use an ephemeral, OS-assigned port?
#include <arpa/inet.h>
#include <errno.h>
#include <netdb.h>
#include <sys/socket.h>
#include <unistd.h>
#include <cstring>
#include <thread>
int32_t createSocket(uint16_t& rBoundPort)
{
int32_t s = socket(AF_INET, SOCK_DGRAM, 0);
if (s >= 0)
{
timeval timeout;
timeout.tv_sec = 1;
timeout.tv_usec = 0;
if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeval)) < 0)
{
::close(s);
return -1;
}
int32_t r(1);
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &r, sizeof(int)) < 0)
{
close(s);
return -1;
}
int32_t bufSize(50000);
if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &bufSize, sizeof(bufSize)) < 0)
{
close(s);
return -1;
}
// Setup local listening port
sockaddr_in listenAddress;
memset(reinterpret_cast<char*>(&listenAddress), 0, sizeof(listenAddress));
listenAddress.sin_family = AF_INET;
inet_aton("127.0.0.1", &listenAddress.sin_addr);
listenAddress.sin_port = htons(0);
if (bind(s, (struct sockaddr*)&listenAddress, sizeof(listenAddress)) != 0)
{
close(s);
return -1;
}
// Update the bound listen port
socklen_t boundAddrLen = sizeof(sockaddr_in);
getsockname(s, (struct sockaddr*)&listenAddress, &boundAddrLen);
rBoundPort = ntohs(listenAddress.sin_port);
}
return s;
}
void mysockettest(int32_t s, uint16_t destPort)
{
const int32_t dataSize = 6;
char aWrite[dataSize] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 };
char aRx[dataSize];
sockaddr_in dest;
memset((char*)&dest, 0, sizeof(dest));
dest.sin_family = AF_INET;
inet_aton("127.0.0.1", &dest.sin_addr);
dest.sin_port = htons(destPort);
sendto(s, aWrite, dataSize, 0, (struct sockaddr*)&dest, sizeof(sockaddr_in));
sockaddr_in src;
socklen_t srcSize = sizeof(sockaddr_in);
memset((char*)&src, 0, srcSize);
int32_t bytesReceived = recvfrom(s, aRx, dataSize, 0, (struct sockaddr*)&src, &srcSize);
if (bytesReceived != dataSize)
{
printf("Received failed, got %d expected %d\n", bytesReceived, dataSize);
}
}
int main(int argc, char** pargv)
{
uint16_t s1Port(0);
int32_t s1 = createSocket(s1Port);
if (s1 < 0)
{
printf("FAILED TO OPEN SOCKET 1\n");
return -1;
}
if (s1Port < 1)
{
printf("FAILED TO BIND SOCKET 1 TO PORT\n");
return -1;
}
uint16_t s2Port(0);
int32_t s2 = createSocket(s2Port);
if (s2 < 0)
{
printf("FAILED TOOPEN SOCKE 2\n");
}
if (s2Port < 1)
{
printf("FAILED TO BIND SOCKET 2 TO PORT\n");
}
std::thread t1(mysockettest, s1, s2Port);
std::thread t2(mysockettest, s2, s1Port);
t1.join();
t2.join();
close(s1);
close(s2);
}
```
Unforntunately I can't explain the details of they "why" (perhaps someone else can), but I found that adding a global mutex lock to protect the send corrected the instability on the send. The global lock isn't practical for my situation, so instead I implemeneted a simply retry loop on the send (if send() == -1, retry 10 times). While this solution is not ideal, it has proven stable.
Once that stability was resolved, I also saw occasional failures on the read side. With SO_REUSEADDR and binding with port 0 (OS-assignment), it was possible to have both sockets be assigned and bind to the same port, so I removed the SO_REUSEADDR when binding to port 0.
Related
I'm learning c++ and coming from a Network Engineer background, and it is fun for me to program something that I'm familiar with it on the network side. I started creating a BGP speaker.
Here is my environment:
[mybgp]<------------TCP-Port-179------------->[bird]
Here is my current code.
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#define BIND_ADDR INADDR_ANY
#define BIND_PORT htons(179)
int createServerSocket(){
//Create Socket
int serverSocket = socket(AF_INET,SOCK_STREAM, IPPROTO_TCP);
if (serverSocket < 0) {
fprintf(stderr, "socket(): %s\n", strerror(errno));
return 1;
}
return serverSocket;
}
int createBind(const int &serverSocket, sockaddr_in &serverAddr){
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = BIND_PORT;
serverAddr.sin_addr.s_addr = INADDR_ANY;
int bind_ret = bind(serverSocket, (sockaddr *) &serverAddr, sizeof(serverAddr));
// if (bind(serverSocket, (struct sockaddr*) &serverAddr, sizeof(serverAddr)) == -1) {
// std::cerr << "Can't bind to ip/port";
// return -2;
// }
if (bind_ret < 0) {
fprintf(stderr, "bind(): %s\n", strerror(errno));
close(serverSocket);
return 1;
}
return bind_ret;
}
int createListener(const int &serverSocket){
int amount;
int listen_ret = listen(serverSocket, 3);
if (listen_ret < 0) {
fprintf(stderr, "listen(): %s\n", strerror(errno));
close(serverSocket);
return 1;
}
return listen_ret;
}
int acceptConnect(const int &serverSocket, sockaddr_in &clientAddr){
fprintf(stderr, "waiting for any client...\n");
char ip_str[INET_ADDRSTRLEN];
socklen_t caddr_len = sizeof(clientAddr);
int serverConn = accept(serverSocket, (sockaddr *) &clientAddr, &caddr_len);
if (serverConn < 0) {
fprintf(stderr, "accept(): %s\n", strerror(errno));
close(serverSocket);
close(serverConn);
return 1;
}
inet_ntop(AF_INET, &(clientAddr.sin_addr), ip_str, INET_ADDRSTRLEN);
fprintf(stderr, "accept(): new client from %s.\n", ip_str);
return serverConn;
}
int main(){
//Create Socket
int serverSocket = createServerSocket();
// declare server and client address struct
// bind socket.
sockaddr_in serverAddr, clientAddr;
memset(&serverAddr, 0, sizeof(serverAddr));
int bind_ret = createBind(serverSocket, serverAddr);
// listening for connection
int listen_ret = createListener(serverSocket);
int serverConn = acceptConnect(serverSocket, clientAddr);
// we only do one at a time, no new client.
char buffer[4048] = {0};
char valread;
valread = read( serverConn , buffer, 4048);
printf("%s\n",buffer );
close(serverSocket);
return 0;
}
My current state is that I'm able to:
Create Socket
Bind the Socket to IP
Listening
Accept a Single Connection(Single thread for now)
Received Data
At step 5, I received a gibberish TCP Message from the BGP speaker(BIRD). Knowing how the BGP Protocol works, this TCP message is in an OPEN Message Format. To establish BGP peering, mybgp and bird need to go back and for with different types of messages and agree.
For me to be able to accomplish this peering, I have to:
Decode[Received Package]
Encode[send package]
How can I decode/encode the TCP packet so I can start the process of BGP peering.
I am learning linux socket programming, I expect that server can read data, even I add a delay but it just drops the buffer data, and receive the recent data, that is why, Thanks. The code has been presented.
By the way, Could you show a common practice to deal with this kind of situation?
Server side C/C++ program to demonstrate Socket programming
#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <string.h>
#define PORT 8080
int main(int argc, char const *argv[])
{
int server_fd, new_socket, valread;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
char buffer[1024] = {0};
const char hello[] = "Hello from server";
// Creating socket file descriptor
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0)
{
perror("socket failed");
exit(EXIT_FAILURE);
}
// Forcefully attaching socket to the port 8080
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT,
&opt, sizeof(opt)))
{
perror("setsockopt");
exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);
// Forcefully attaching socket to the port 8080
if (bind(server_fd, (struct sockaddr *)&address,
sizeof(address)) < 0)
{
perror("bind failed");
exit(EXIT_FAILURE);
}
if (listen(server_fd, 3) < 0)
{
perror("listen");
exit(EXIT_FAILURE);
}
if ((new_socket = accept(server_fd, (struct sockaddr *)&address,
(socklen_t *)&addrlen)) < 0)
{
perror("accept");
exit(EXIT_FAILURE);
}
for (int i = 0;; i++)
{
sleep(5);
valread = read(new_socket, buffer, 1024);
printf("%s\n", buffer);
}
send(new_socket, hello, strlen(hello), 0);
printf("Hello message sent\n");
return 0;
}
Client side C/C++ program to demonstrate Socket programming
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string>
#include <string.h>
#define PORT 8080
int main(int argc, char const *argv[])
{
int sock = 0, valread;
struct sockaddr_in serv_addr;
const char data[] = "Hello from client";
char buffer[1024] = {0};
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("\n Socket creation error \n");
return -1;
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
// Convert IPv4 and IPv6 addresses from text to binary form
if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0)
{
printf("\nInvalid address/ Address not supported \n");
return -1;
}
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
{
printf("\nConnection Failed \n");
return -1;
}
for (int i = 0;; i++)
{
sleep(1);
std::string hello = std::string(data) + std::to_string(i);
if (send(sock, hello.c_str(), hello.length() + 1, 0) != hello.length() + 1)
{
printf("error send %d \n", i);
}
printf("Hello message sent %d\n", i);
}
valread = read(sock, buffer, 1024);
printf("%s\n", buffer);
return 0;
}
The problem is, that the messages get concatenated in the socket. The socket represents a byte stream. Your sender puts bytes into the stream every second. On the first iteration, it writes "Hello from client0\0" (19 bytes) to the stream.
After one second, it writes "Hello from client1\0", and then "Hello from client2\0", "Hello from client3\0" and "Hello from client4\0", Now, after 5 Seconds, 5*19 = 95 bytes are written to the byte stream.
Now, the receiver calls valread = read(new_socket, buffer, 1024);. Guess what, it reads all 95 bytes (because you specified 1024 as buffer size) and sets valread to 95.
Then you call printf("%s\n", buffer);, which only prints the first 18 bytes of buffer, because there is a '\0' as 19th byte, which terminates '%s' format. Allthough 95 bytes are received, 76 bytes are missing in the output of your program.
If you use '\n' instead of '\0' as message separator and use write(1, buffer, valread) instead of printf("%s\n") on the receiving side, you will see all your data.
std::string hello = std::string(data) + std::to_string(i) + "\n";
if (send(sock, hello.c_str(), hello.length(), 0) != hello.length()) ...
Conclusion:
Stream sockets realize byte sreams, the do not preserve message boundaries.
If message bounaries must be preserved, you need to use a protocol on top of the stream to mark your message boundaries. The proptocol could be as simple as using '\n' as a message seaparator, as long as '\n' is not part of your message payload (e.g. when unsign a simple text protocol).
You block the server for 5 seconds and it cannot receive some messages from the client.
for (int i = 0;; i++)
{
sleep(5);
valread = read(new_socket, buffer, 1024);
printf("%s\n", buffer);
}
How can a client check if the server is receiving a message? I think this was discussed in Linux socket: How to make send() wait for recv()
P.S. It looks like there is a synchronizing piece of code, but you pulled it out of the loop.
Server:
}
send(new_socket, hello, strlen(hello), 0);
Client:
}
valread = read(sock, buffer, 1024);
#include <stdio.h>
#include <time.h>
#include <WinSock2.h>
#include <WS2tcpip.h>
#pragma comment(lib, "WS2_32.lib")
#define IP_ADDRESS "127.0.0.1"
#define PORT 20000
#define BUF_SIZE 64
#undef FD_SETSIZE
#define FD_SETSIZE 10000
void shuffle_buffer(char* buf, size_t size);
SOCKET create_socket();
void send_data(SOCKET sock);
int main()
{
WSADATA ws;
if (WSAStartup(MAKEWORD(2, 2), &ws) != 0)
{
printf("Init Windows Socket Failed::%d\n", GetLastError());
return -1;
}
const int CLIENT_SIZE = 1;
SOCKET socks[CLIENT_SIZE];
struct timeval tv = { 0, 10 };
fd_set fd_read, fd_write;
FD_ZERO(&fd_read);
FD_ZERO(&fd_write);
for (int i = 0; i < CLIENT_SIZE; i++) {
SOCKET sock = create_socket();
socks[i] = sock;
FD_SET(sock, &fd_write);
FD_SET(sock, &fd_read);
}
Sleep(1000);
int number_to_recv = CLIENT_SIZE;
while (number_to_recv > 0) {
int ret = select(CLIENT_SIZE, &fd_read, &fd_write, NULL, &tv);
for (int i = 0; i < CLIENT_SIZE; i++) {
if (FD_ISSET(socks[i], &fd_read)) {
char buf[BUF_SIZE];
int n = recv(socks[i], buf, BUF_SIZE, 0);
buf[n] = 0;
printf("%s\n", buf);
number_to_recv--;
}
if (FD_ISSET(socks[i], &fd_write)) {
send_data(socks[i]);
FD_CLR(socks[i], &fd_write);
//Sleep(1);
}
}
//printf("ret and number : %d, %d\n", ret, number_to_recv);
}
for (int i = 0; i < CLIENT_SIZE; i++) {
closesocket(socks[i]);
}
WSACleanup();
}
SOCKET create_socket()
{
SOCKET cli_sock;
struct sockaddr_in addr;
if ((cli_sock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
{
printf("Create Socket Failed::%d\n", GetLastError());
return -1;
}
//inet_pton
memset(addr.sin_zero, 0x00, 8);
addr.sin_family = AF_INET;
inet_pton(AF_INET, IP_ADDRESS, (void*)(&addr.sin_addr.s_addr));
addr.sin_port = htons(PORT);
if (connect(cli_sock, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR)
{
printf("Connect Error::%d\n", GetLastError());
return -1;
}
return cli_sock;
}
void send_data(SOCKET sock)
{
const int SEND_SIZE = BUF_SIZE / 2;
char buf[SEND_SIZE] = { 0 };
memset(buf, 'a', SEND_SIZE);
shuffle_buffer(buf, SEND_SIZE);
if (send(sock, buf, SEND_SIZE, 0) == SOCKET_ERROR)
{
printf("Send Info Error::%d\n", GetLastError());
}
}
void shuffle_buffer(char* buf, size_t size)
{
for (int i = 0; i < size; i++) {
buf[i] += int(rand() % 26);
}
}
Code above is a socket client using select model run on Win10, the problem is after I send data, but I can not receive data(I am sure that server has sent back data), this code below doesn`t run, so what is the problem? Thanks
The first parameter in select is maxfdp, and I know the difference between Win and Unix, so on Windows, this parameter seems not necessary, and I can write data,
but can not receive it.
if (FD_ISSET(socks[i], &fd_read)) {
char buf[BUF_SIZE];
int n = recv(socks[i], buf, BUF_SIZE, 0);
buf[n] = 0;
printf("%s\n", buf);
number_to_recv--;
}
select removes the sockets from the fd_set if they are not readable/writable. You need to add them back in before the next time you call select.
The reason your code can write data is because sockets start out being writable, so they will still be set in fd_write and your code will write data. They don't start out being readable, if no data has been received yet, so they'll be removed from the fd_read set and then your code stops checking whether they are readable.
I am trying to get a very basic hello world UDP sender and UDP multicast listener to work. I have a PC but have a virtual machine with the Linux OS CentOS. It has no problems connecting to the internet. The sender and listener are two separate programs, Eclipse is my environment.
The Sender...
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#define UDP_PORT 5403
#define UDP_GROUP "225.0.0.1" // 127.0.0.1
int main(int argc, char *argv[])
{
struct sockaddr_in addr;
int fd;
struct ip_mreq mreq;
char *message="Hello, World!";
int message_size = strlen(message) + 1;
// Create a UDP socket
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0)
{
perror("socket(...) ");
return -1;
}
// allow multiple sockets to use the same PORT number
u_int reuse_port = 1;
if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &reuse_port, sizeof(reuse_port)) < 0)
{
perror("setsockopt(...) ");
return -1;
}
// set up destination address
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(UDP_GROUP);
addr.sin_port = htons(UDP_PORT);
printf("Begin sendto(...) infinite loop\n");
while (true)
{
printf("Sending message: %s, of size: %d\n", message, message_size);
if (sendto(fd, message, message_size, 0, (struct sockaddr *)&addr, sizeof(addr)) < 0)
{
perror("sendto(...): ");
return -1;
}
// printf("message sent: %s\n", message);
sleep(1);
}
return 1;
}
The Listener...
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#include <string.h>
#include <stdio.h>
#define UDP_PORT 5403
#define UDP_GROUP "225.0.0.1"
#define MAX_BUFFER_SIZE 256
int main(int argc, char *argv[])
{
struct sockaddr_in addr;
int fd, nbytes;
socklen_t addrlen;
struct ip_mreq mreq;
char msgbuf[MAX_BUFFER_SIZE];
u_int reuse_port = 1;
// Create a socket
fd = socket(AF_INET,SOCK_DGRAM,0);
if (fd < 0)
{
perror("create socket failed");
return -1;
}
// allow multiple sockets to use the same PORT number
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse_port, sizeof(reuse_port)) < 0)
{
perror("Reusing port number failed");
return -1;
}
// set up destination address
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(UDP_PORT);
if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
{
perror("bind");
return -1;
}
// Set the recvfrom timeout after 1 s
struct timeval tv;
tv.tv_sec = 2;
tv.tv_usec = 0;
if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0)
{
perror("Error setting recvfrom timeout\n");
return -1;
}
// use setsockopt() to request that the kernel join a multicast group
mreq.imr_multiaddr.s_addr = inet_addr(UDP_GROUP);
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
{
perror("setsockopt");
return -1;
}
addrlen = sizeof(addr);
printf("Begin recvfrom(...) infinite loop\n");
while (true)
{
nbytes = recvfrom(fd, msgbuf, MAX_BUFFER_SIZE, 0, (struct sockaddr *)&addr, &addrlen);
if (nbytes < 0)
{
printf("recvfrom timeout\n");
}
else
{
printf("message received: %s\n", msgbuf);
}
}
return 1;
}
Every second, the sender program printf's "Sending message: Hello, World!, of size: 14" and every two seconds the receiver printf's "recvfrom timeout". I have set Wireshark to look at UDP traffic and I definitely see the sento data. The recvfrom is not getting any data. I have tried using many different Group IP's from 255.0.0.0 to 239.255.255.255, no change. I have tried many different ports, no change. Is their a special setup I need to do on my network card? I'm not sure what else to do. Small edit, the recvfrom and sendto message should not have "&".
In Windows platform I can check if a socket is connected by this code:
bool isConnected(int socket)
{
int err;
int len = sizeof (err);
getsockopt(socket, SOL_SOCKET, SO_ERROR, (char*)&err, &len);
return err == 0;
}
In OS X this function always returns true.
What is the official way to do it?
This function works for both Windows and Mac platforms:
bool isConnected(int socket)
{
int err;
#ifdef __APPLE__
uint32_t len;
#elif _Windows
int len;
#endif
sockaddr_in addr;
len = sizeof (addr);
err = getpeername(socket, (sockaddr*)&addr, &len);
return err == 0;
}
The only way I know to determine if a blocking socket is connected is to perform an actual send/recv operation and check the result for error. On Windows, a non-blocking socket can also issue asynchronous notifications on socket activity, like FD_CLOSE for disconnects.
The one place SOL_ERROR comes in handy is for a non-blocking connect() call. After select() reports the connect operation is finished, SOL_ERROR can be used to determine whether connect() was successful or why it failed.
This - if (connect(s, (struct sockaddr *)&sa, sizeof sa) < 0) is close enough to winsock's isConnected.
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main() {
register int s;
register int bytes;
struct sockaddr_in sa;
char buffer[BUFSIZ+1];
if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket");
return 1;
}
bzero(&sa, sizeof sa);
sa.sin_family = AF_INET;
sa.sin_port = htons(13);
sa.sin_addr.s_addr = htonl((((((192 << 8) | 43) << 8) | 244) << 8) | 18);
if (connect(s, (struct sockaddr *)&sa, sizeof sa) < 0) {
perror("connect");
close(s);
return 2;
}
while ((bytes = read(s, buffer, BUFSIZ)) > 0)
write(1, buffer, bytes);
close(s);
return 0;
}