Implementing Multi-threading concurrent server by C++ (UDP case) - c++

I used "Create-new-thread method" to deal with new clients and met problem in UDP.
First I used main thread to create one TCP thread to deal with new accept()
and create one UDP thread to deal with new recvfrom(). (TCP cases are OK)
Once the first recvfrom() callback, i try to pass the client to the new UDP thread and keep the current one to deal with next new UDP client
It should be work by passing the address and new-created datagram socket to the new thread to continue do recvfrom(), right ?
During experiment, I found that the OLD UDP thread keeps receiving datagram from the client while the new thread just doing while loop and recvfrom() turns to non-blocking manner and returns -1 as a result.
Therefore what is the problem of my code....thanks~
Do I understand something wrongly..?
PassToUDPThread is the typeof struct i made for data passing to new thread:
typedef struct {
sockaddr_in ReceiverSocket;
SOCKET sendSocket;
char* recvbuf;
long packet_size;
}PassToUDPThread;
Here is my code for the FIRST UDP thread function (dealing with first recvfrom() ONLY) :
sockaddr_in *ReceiverSocket = new sockaddr_in;
ReceiverSocket->sin_family = AF_INET;
if (strcmp(recv_hostname, "INADDR_ANY") != 0)
inet_pton(AF_INET, ip_addr_char, &ReceiverSocket->sin_addr.s_addr);
else ReceiverSocket->sin_addr.s_addr = INADDR_ANY;
ReceiverSocket->sin_port = htons((u_short)recv_port);
//*** Create the socket
SOCKET s;
s = socket(AF_INET, SOCK_DGRAM, 0);
printf("Receiver: Socket Created - UDP connection\n");
bind(s, (struct sockaddr *)ReceiverSocket, sizeof(struct sockaddr_in));
do
{
int fromlen = (int)sizeof(struct sockaddr_in);
retVal = recvfrom(s, recvbuf, packet_size, 0, (struct sockaddr*)ReceiverSocket, &fromlen);
std::cout << "UDP recv by MAIN "<<std::endl;
if (retVal == SOCKET_ERROR) {
std::cout << "UDP Socket error : " + WSAGetLastError()<<std::endl;
return 0;
}
else {
SOCKET sendSocket = socket(AF_INET, SOCK_DGRAM, 0);
PassToUDPThread _p;
PassToUDPThread *p = &_p;
p->sendSocket = sendSocket;
p->ReceiverSocket = *ReceiverSocket;
p->recvbuf = recvbuf;
p->packet_size = packet_size;
thrd_t UDPthread;
if (thrd_create(&UDPthread, ThreadUDPReceiver, (void*)p) == thrd_success) {
std::cout<<"success create new UDP thread"<<std::endl;
}
}
} while (retVal>0);
closesocket(s);
UDPReceiver(arg);
return 1;
}
Here is the code for the function on NEW-CREATED UDP thread:
int ThreadUDPReceiver(void *arg) {
int retVal;
SOCKET s = ((PassToUDPThread*)arg)->sendSocket;
char* recvbuf = ((PassToUDPThread*)arg)->recvbuf;
long packet_size = ((PassToUDPThread*)arg)->packet_size;
sockaddr_in *ReceiverSocket = new sockaddr_in;
memcpy(ReceiverSocket, &(((PassToUDPThread*)arg)->ReceiverSocket), sizeof sockaddr_in);
bind(s, (struct sockaddr *)ReceiverSocket, sizeof(struct sockaddr_in));
do {
int fromlen = (int)sizeof(struct sockaddr_in);
retVal = recvfrom(s, recvbuf, packet_size, 0, (struct sockaddr *)ReceiverSocket, &fromlen);
std::cout << "UDP recv by thread "<< std::endl;
} while (true);
return 0;
}

Use a thread pool.
Get rid of the extra UDP sockets. Use the same one to send the response that the request arrived on. Simpler for the client that way too.

Related

Simple UDP socket code, sending and receiving messages

I am just learning UDP sockets and this is my first code involving it. I have two programs which send and receive messages back and forth. My question is it seems I have to declare which IP address I am sending/receiving from multiple times throughout the code as it changes but I feel there is a better way to do this without changing the inet_addr manually within the codes. From my reading it looks like sendto and recvfrom may be able to help but I am unsure how to use them in this context. If anyone could show me how to fix my simple problem I would greatly appreciate it! Thanks
CODE 1: Send then Receive
int main(int argc, char *argv[])
{
//initialize socket and structure
int socket_info;
struct sockaddr_in server;
char message[100];
char incoming_message[100];
printf("Input Message: ");
fgets(message, 100, stdin);
//create socket
socket_info = socket(AF_INET, SOCK_DGRAM, 0);
if (socket_info == -1) {
printf("Could not create socket");
}
//assign local values
server.sin_addr.s_addr = inet_addr("172.21.8.178");
server.sin_family = AF_INET;
server.sin_port = htons( 1100 );
//binds connection
if (bind(socket_info, (struct sockaddr *)&server, sizeof(server)) < 0) {
perror("Connection error");
return 1;
}
puts("Bind");
//assign new value to connect to
server.sin_addr.s_addr = inet_addr("172.21.8.179");
//checks connection
if (connect(socket_info, (struct sockaddr *)&server, sizeof(server)) < 0) {
perror("Connection error");
return 1;
}
puts("Connected");
//sends message
if(send(socket_info, message, strlen(message), 0) <0) {
perror("Send failed");
return 1;
}
puts("Message Sent");
//receives message back
if(recv(socket_info, incoming_message, sizeof(incoming_message), 0) <0) {
puts("Received failed");
return 1;
}
puts("Message received");
puts(incoming_message);
close(socket_info);
}
CODE 2: Receive then Send
int main(int argc, char *argv[])
{
//initialize socket and structure
int socket_info;
struct sockaddr_in server;
char incoming_message[100];
//create socket
socket_info = socket(AF_INET, SOCK_DGRAM, 0);
if (socket_info == -1) {
printf("Could not create socket");
}
//assign values
server.sin_addr.s_addr = inet_addr("172.21.8.179");
server.sin_family = AF_INET;
server.sin_port = htons( 1100 );
//checks connection
if (bind(socket_info, (struct sockaddr *)&server, sizeof(server)) < 0) {
perror("Connection error");
return 1;
}
puts("Bind");
//Receive an incoming message
if( recv(socket_info, incoming_message, sizeof(incoming_message), 0) < 0) {
puts("Received failed");
return 1;
}
puts("Message received");
puts(incoming_message);
server.sin_addr.s_addr = inet_addr("172.21.8.178");
if (connect(socket_info, (struct sockaddr *)&server, sizeof(server)) < 0) {
perror("Connection error");
return 1;
}
puts("Connected");
//Sends message back
char message[100];
printf("Input Message: ");
fgets(message, 100, stdin);
if(send(socket_info, message, strlen(message), 0) <0) {
perror("Send failed");
return 1;
}
puts("Message Sent");
close(socket_info);
}
If you use the function recvfrom()
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
What this function does is it fills a structure of sockaddr with the IP and port information of the packet that it has just received. For example, if your code that sends first then receives sends a packet to the receiver, the receiver should be able to fill the structure values of sin_addr and sin_port with the correct values. You can then make a call of sendto() with this information in order to send it to the correct machine.
Here's the man pages for these functions:
https://linux.die.net/man/2/recvfrom
https://linux.die.net/man/2/sendto
Try using this:
inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr)
127.0.0.1 is the loopback IP. The address is used to establish an IP connection to the same machine, which seems to be your case.
A detailed way to solve the problem can be found here

Serving multiple clients: accept() socket call in a loop to accept incoming connections

Here is my code:
void error(const char *msg)
{
perror(msg);
exit(1);
}
void sServer::acceptClientConnections(int listenerSocket)
{
struct sockaddr clientAddress;
socklen_t sizeOfClientAddress = sizeof(clientAddress);
while (true)
{
int newConnection = accept(listenerSocket, &clientAddress, &sizeOfClientAddress);
std::cout << "Someone connected ... " <<std::endl;
liveConnections.push_back(newConnection);
}
}
int sServer::getServerListenerSocket()
{
return serverListenerSocket;
}
sServer::sServer(int port)
{
int serverListenerSocket;
struct sockaddr_in serv_addr;
serverListenerSocket = socket(AF_INET, SOCK_STREAM, 0);
if (serverListenerSocket < 0)
error("ERROR opening a socket. Cannot start sever. Exiting ...");
memset((char *) &serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(port);
std::cout << "Host ip: " << serv_addr.sin_addr.s_addr << "\n";
if ( bind(serverListenerSocket, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0 )
error("ERROR on binding server socket. Are you running another instance of this server ?");
listen(serverListenerSocket, 45);
}
Called like this:
int main()
{
sServer superServer(9889);
std::thread handleConnections( &sServer::acceptClientConnections, superServer, superServer.getServerListenerSocket() );
}
I was expecting accept to wait for incoming connections, and when a connection comes in, become active.
But instead, when i run the program, it continuously prints "Someone connected ..." even though no connections are being made.
Ironically, if I start the thread at the end of the constructor, it works well.
Can you please explain why this is happening and what would be the correct way to accept a connection ?
Now that we've determined the error, the problem is the local variable in the constructor:
sServer::sServer(int port)
{
int serverListenerSocket;
Just remove it. It is shadowing the class member of the same name, so the latter is never getting set, so accept() is getting EBADF, returning -1, never displaying an accepted connection, smoking the CPU, etc.
The reason it works when you start the thread inside the constructor is that the local variable is still in scope.

Raw Socket send TCP SYN-FIN-.. in c++

My teacher want us to do an exercise on raw socket in c ++ on Windows (for learning tcp communication).
I have got a problem with it. I saw a lot of documentation but I don't know how to solve it.
int raw()
{
WSADATA WSAData;
SOCKET sock;
SOCKADDR_IN sin,din;
WSAStartup(MAKEWORD(2, 2), &WSAData);
char datagram[MAX_PACKET_SIZE];
struct iphdr *iph = (struct iphdr *)datagram;
struct tcphdr *tcph = (struct tcphdr *)((UCHAR *)iph + sizeof(tcphdr));
char new_ip[sizeof "255.255.255.255"];
sock = socket(PF_INET, SOCK_RAW, IPPROTO_TCP);
if (sock == INVALID_SOCKET)
cout << "failled init socket" << endl ;
else{
memset(datagram, 0, MAX_PACKET_SIZE); // Clear the data
setup_ip_header(iph);
setup_tcp_header(tcph);
sin.sin_family = AF_INET;
sin.sin_port = htons(8888);
sin.sin_addr.s_addr = inet_addr("192.168.1.10"); //source ip
din.sin_family = AF_INET;
din.sin_port = htons(DEST_PORT);
din.sin_addr.s_addr = inet_addr(TARGET_SERV_IP); //ip serv to connect
tcph->port_dest = htons(DEST_PORT);
iph->ip_dest = din.sin_addr.s_addr;
iph->ip_source = sin.sin_addr.s_addr;
iph->ip_dest = inet_addr(TARGET_SERV_IP); //ip serv to connect
iph->ip_source = inet_addr("192.168.1.10"); //source ip
//iph->checksum = csum((unsigned short *)datagram, iph->tot_len >> 1);
iph->checksum = csum((unsigned short *)datagram, sizeof(struct iphdr));
int one = 1;
const int *val = &one;
if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, (char *)val, sizeof(one)) < 0)
printf("failled set socket option IP_HDRINCL");
else{
if (sendto(sock, /* our socket */
datagram, /* the buffer containing headers and data */
ntohs( iph->tot_len), /* total length of our datagram */
0, /* routing flags, normally always 0 */
(struct sockaddr *) &sin, /* socket addr, just like in */
sizeof(sin)) < 0) /* a normal send() */
cout << stderr << "sendto() error!!!.\n " << WSAGetLastError() << endl;
else
cout << "packet send\n" << endl;
}
closesocket(sock);
}
}
My error occurs at the sendto(). it return 10022 error = WSAEINVAL
I saw that can be a new windows protection?
Have you any idea to fix my problem or bypass the protection (go deeper, driver, etc)
You don't set iph->tot_len in your code.
My recommendation for networking code using c++ would be to use std::string or std::vector:
std::vector<uint8_t> packet(MAX_PACKET_SIZE, 0);
...
packet.resize(real_size);
then use the address (&packet[0]) for your pointer manipulations.

Socket does not accept connections

I have a server socket accepting client socket connections. Accept is in a thread
socket creation
int ServerSocket::CreateSocket(int port)
{
listenfd = 0;
struct sockaddr_in serv_addr;
unsigned long iMode = 1;
listenfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&serv_addr, '0', sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(port);
ioctlsocket(listenfd, FIONBIO, &iMode);
if (bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0)
{
return 0;
}
if (listen(listenfd, 20) < 0)
{
return 0;
}
return listenfd;
}
Socket Accept
void ServerSocket::AcceptClients_1(void * p)
{
struct sockaddr_in cli_addr;
// get a pointer to the ServerSocket object
ServerSocket * pThis = (ServerSocket *)p;
int iResult, cli_len;
cli_len = sizeof(cli_addr);
struct timeval tv = { 0, 1000 };
SOCKET s = pThis->GetSocket();
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(s, &rfds);
while (!pThis->ShutDownRequested)
{
iResult = select(s+1, &rfds, (fd_set *) 0, (fd_set *) 0, &tv);
if(iResult > 0)
{
// never comes here
SOCKET sclient = accept(s, (struct sockaddr *)&cli_addr,
&cli_len);
}
else if (iResult == 0) /// timeout
{
continue;
}
// error comes here are going to accept 2nd time
DWORD dwError = GetLastError();
return;
}
}
The code comes on select(). Returns 0 the first time but second time always returns -1 with error 10022. I don't understand why. Please help.
Make sure your pThis->GetSocket() is correctly returning the listenfd. Also, you should reinitialize cli_len = sizeof(cli_addr); before each call to accept (it's a value-result argument).
iResult=0 does not always mean timeout, for non-blocking sockets, you need to check WSAGetLastError and deal with some error codes, for example WSAEWOULDBLOCK means you need to wait next event on this socket.
http://msdn.microsoft.com/en-us/library/windows/desktop/ms740668(v=vs.85).aspx

trying to broadcast through udp socket

I've bumped into a problem with my broadcasting server. basically, I want it to send broadcasts continuously from the moment I launch it. for some reason it will not start until it receives a connection from somewhere. I must have messed up something but I can't realise what.
here is my code:
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
SOCKET sock;
sock = socket(AF_INET, SOCK_DGRAM, 0);
char broadcast = 'a';
if(setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof(broadcast)) < 0)
{
perror("broadcast options");
closesocket(sock);
return 1;
}
struct sockaddr_in Recv_addr;
struct sockaddr_in Sender_addr;
int len = sizeof(struct sockaddr_in);
char recvBuff[50];
int recvBuffLen = 50;
//char sendMsg[] = "broadcast message from salam rofl";
Recv_addr.sin_family = AF_INET;
Recv_addr.sin_port = htons(PORT);
Recv_addr.sin_addr.s_addr = INADDR_ANY;
if(bind(sock, (sockaddr*)&Recv_addr, sizeof(Recv_addr)) < 0)
{
perror("bind");
_getch;
closesocket(sock);
return 1;
}
//recvfrom(sock, recvBuff, recvBuffLen, 0, (sockaddr *)&Sender_addr, &len);
//cout << "\nreceived message: " << recvBuff;
while(1)
{
Sleep(3000);
//_getch();
getTime();
if(sendto(sock, currentTime, strlen(currentTime)+1, 0, (sockaddr *)&Sender_addr, sizeof(Sender_addr)) < 0)
{
perror("borhot send: ");
_getch();
closesocket(sock);
return 0;
}
else cout << "message sent successfully";
}
_getch;
closesocket(sock);
WSACleanup();
return 0;
basically if I remove recvfrom, it will give me a send error ("No error") which simply puzzles me. also, if I send it something with a client, it will start broadcasting, but if I connect with another client, only the first client is receiving the broadcast.
thank you in advance.
I doubt it matters but I'm trying to broadcast the current time.
You are not initializing Sender_Addr so you are not telling sendto() where to actually broadcast to.
Try this instead:
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock == INVALID_SOCKET)
{
perror("socket creation");
_getch;
return 1;
}
BOOL enabled = TRUE;
if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char*)&enabled, sizeof(BOOL)) < 0)
{
perror("broadcast options");
_getch;
closesocket(sock);
return 1;
}
struct sockaddr_in Sender_addr;
Sender_addr.sin_family = AF_INET;
Sender_addr.sin_port = htons(BROADCAST_PORT);
Sender_addr.sin_addr.s_addr = inet_addr("Broadcast IP Here");
struct sockaddr_in Recv_addr;
Recv_addr.sin_family = AF_INET;
Recv_addr.sin_port = htons(PORT);
Recv_addr.sin_addr.s_addr = INADDR_ANY;
if (bind(sock, (sockaddr*)&Recv_addr, sizeof(Recv_addr)) < 0)
{
perror("bind");
_getch;
closesocket(sock);
return 1;
}
while(1)
{
Sleep(3000);
getTime();
if (sendto(sock, currentTime, strlen(currentTime)+1, 0, (sockaddr *)&Sender_addr, sizeof(Sender_addr)) < 0)
{
perror("borhot send: ");
_getch();
closesocket(sock);
return 0;
}
cout << "message sent successfully";
}
_getch;
closesocket(sock);
WSACleanup();
return 0;
Looks like your Sender_addr is never being initialized, thus when you remove the recvfrom you're getting an error, and when the recvfrom is in place it's getting populated with the address of the first client to connect (but never being updated).
If you don't know the addresses of the clients that you want to broadcast to, you'll need to setup some handshake where they send you a ping, you receive it with recvfrom, and you store their address in a list or something. Then, when you broadcast, you need to send your message to every client address in the list.