How do I make two socket multicast transmitters talk to each other? - c++

I am trying to establish two sockets that are both able to multicast transmit. And the should both be members of each others multicast group.
I have two problems.
I have tried to disable the loopback, so that a transmitter does not receive its own messages. This does not seem to work.
The two transmitters should be the same, but only one of them receives the message sent on their multicast group.
The code looks like this:
#define ERROR_CODE -1
int init(const char *rx, const char *tx, uint16_t port, struct sockaddr_in& txaddress)
{
int status = 0;
int enable = 1;
int disable = 0;
int mainSocket = ::socket(PF_INET, SOCK_DGRAM|SOCK_NONBLOCK, 0);
if (mainSocket == ERROR_CODE)
std::cout << "Error creating socket." << std::endl;
if (::setsockopt(mainSocket, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) == ERROR_CODE)
std::cout << "System failed to set reuse address." << std::endl;
if (::setsockopt(mainSocket, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(enable)) == ERROR_CODE)
std::cout << "System failed to set reuse port." << std::endl;
status = setsockopt(mainSocket, IPPROTO_IP, IP_MULTICAST_LOOP, &disable, sizeof(disable));
if (status == ERROR_CODE)
std::cout << "Unable to disable multicast loopback." << std::endl;
struct sockaddr_in rxaddress = address(INADDR_ANY, port);
txaddress = address(inet_addr(tx), port);
status = ::bind(mainSocket, reinterpret_cast<sockaddr*>(&rxaddress), sizeof(sockaddr_in));
if (status == ERROR_CODE)
std::cout << "Error binding socket." << std::endl;
struct in_addr localInterface;
memset(&localInterface, 0, sizeof(localInterface));
localInterface.s_addr = inet_addr("127.0.0.1");
status = setsockopt(mainSocket, IPPROTO_IP, IP_MULTICAST_IF, (char*) &localInterface, sizeof(localInterface));
if (status == ERROR_CODE)
std::cout << "Unable to set interface to multicast." << std::endl;
struct ip_mreq group;
memset(&group, 0, sizeof(group));
group.imr_multiaddr.s_addr = inet_addr(rx);
group.imr_interface.s_addr = INADDR_ANY;
status = setsockopt(mainSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*) &group, sizeof(group));
if (status == ERROR_CODE)
std::cout << "Unable to add to multicast group." << std::endl;
return mainSocket;
}
Where
struct sockaddr_in address(uint32_t ip, uint16_t port)
{
struct sockaddr_in address;
memset(&address, 0, sizeof(sockaddr_in));
address.sin_family = AF_INET;
if (ip == INADDR_BROADCAST)
address.sin_addr.s_addr = htonl(INADDR_BROADCAST);
else
if (ip == INADDR_ANY)
address.sin_addr.s_addr = htonl(INADDR_ANY);
else
address.sin_addr.s_addr = ip;
address.sin_port = htons(port);
return address;
}
And the testcode is
#include <iostream>
#include <arpa/inet.h>
#include <cstring>
#include <thread>
#define MULTICAST_SERVER_ADDRESS "224.0.0.1"
#define MULTICAST_CLIENT_ADDRESS "224.0.0.0"
int main (int argc, char** argv)
{
char message1[16] = "server says hi.";
char message2[16] = "client says hi.";
char buffer1[100] = {0};
char buffer2[100] = {0};
socklen_t length = sizeof(sockaddr_in);
struct sockaddr_in servertxaddress;
struct sockaddr_in clienttxaddress;
memset(&servertxaddress, 0, length);
memset(&clienttxaddress, 0, length);
int serversocket = init(MULTICAST_CLIENT_ADDRESS, MULTICAST_SERVER_ADDRESS, 2000, servertxaddress);
int clientsocket = init(MULTICAST_SERVER_ADDRESS, MULTICAST_CLIENT_ADDRESS, 2000, clienttxaddress);
sendto(serversocket, message1, 16, 0, (const sockaddr*) &servertxaddress, length);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
recvfrom(clientsocket, buffer1, 16, 0, nullptr, nullptr);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
std::cout << buffer1 << std::endl;
sendto(clientsocket, message2, 16, 0, (const sockaddr*) &clienttxaddress, length);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
recvfrom(serversocket, buffer2, 160, 0, nullptr, nullptr);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
std::cout << buffer2 << std::endl;
return 0;
}
When I run the program, both sockets receive the multicast message (message1), so the IP_MULTICAST_LOOP I tried does not work, as I would expect it to. And the last transmit seems to get transmitted, but it does not arrive in the other end.
So the output is:
server says hi.
server says hi.
And I would like it to be:
server says hi.
client says hi.
Please advise.

Related

Why socket fails in bind after having a listenfd with same port?

Simple code first:
#include "NetFunc.h"
#include <iostream>
#include <string>
#include <thread>
#include <chrono>
using namespace std;
void test()
{
// SOCKET listenfd = OpenListenFD("127.0.0.1", "19287");
SOCKET connfd = OpenClientFD("127.0.0.1", "12389", "127.0.0.1", "19287");
send(connfd, "test", 5, 0);
close(connfd);
using namespace std::chrono_literals;
this_thread::sleep_for(100ms);
connfd = OpenClientFD("127.0.0.1", "12389", "127.0.0.1", "19287");
send(connfd, "test2", 6, 0);
close(connfd);
// close(listenfd);
cout << "over" << endl;
return;
}
int main()
{
SOCKET listenfd = OpenListenFD("127.0.0.1", "12389");
sockaddr_storage clientaddr;
socklen_t clientlen = sizeof clientaddr;
// SOCKET connfd = 0;
const int maxNameLen = 0x800;
char* buffer = new char[0x800 + 1];
char* hostname = new char[maxNameLen + 1], * port = new char[maxNameLen + 1];
std::thread newThread{&test};
newThread.detach();
SOCKET connfd;
while (true)
{
if ((connfd = accept(listenfd, (sockaddr*)&clientaddr, &clientlen)) < 0)
{
std::cerr << "Not accept correctly at InteractWithClients" << std::endl;
}
else{
getnameinfo((sockaddr*)&clientaddr, clientlen, hostname, maxNameLen, port, maxNameLen, NI_NUMERICHOST);
std::string completeAddr = string(hostname) + ":" + string(port);
std::cout << "Addr : " << completeAddr << std::endl;
recv(connfd, buffer, 0x800, 0);
std::cout << buffer << std::endl;
}
close(connfd);
}
delete[] buffer; delete[] hostname; delete[]port;
return 0;
}
Here, the NetFunc.h has necessary headers and #define SOCKET int. It corresponds with NetFunc.cpp, which has two functions for server&client as below:
(Note that I'm trying to generate a client with a specific port, for I just need two address-port to connect rather than to distinguish a server from a client. Besides, I already use SO_REUSEADDR.)
#include "NetFunc.h"
#include <iostream>
#include <string.h>
SOCKET OpenListenFD(const char* addr, const char* port)
{
struct addrinfo hints, * listp, * p;
SOCKET listenfd;
int optval = 1;
/* Get a list of potential server addresses */
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_socktype = SOCK_STREAM; /* Accept connections */
hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; /* ... on any IP address */
hints.ai_flags |= AI_NUMERICSERV; /* ... using port number */
hints.ai_family = AF_INET;
getaddrinfo(addr, port, &hints, &listp);
/* Walk the list for one that we can bind to */
for (p = listp; p; p = p->ai_next) {
/* Create a socket descriptor */
if ((listenfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0)
continue; /* Socket failed, try the next */
/* Eliminates "Address already in use" error from bind */
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, //line:netp:csapp:setsockopt
(setsockopt_ptr)&optval, sizeof(int));
/* Bind the descriptor to the address */
if (bind(listenfd, p->ai_addr, p->ai_addrlen) == 0)
{
break; /* Success */
}
closesocket(listenfd); /* Bind failed, try the next */
}
/* Clean up */
freeaddrinfo(listp);
if (!p) /* No address worked */
return -1;
/* Make it a listening socket ready to accept connection requests */
if (listen(listenfd, LISTENQ) < 0) {
closesocket(listenfd);
return -1;
}
return listenfd;
}
SOCKET OpenClientFD(const char* serverAddr, const char* port, const char* localAddr, const char* localPort)
{
struct sockaddr_in my_addr, my_addr1;
SOCKET client = socket(AF_INET, SOCK_STREAM, 0);
if (client < 0)
std::cerr<<"Error in client creating\n";
else
std::cout << "Client Created\n";
int optval = 1;
setsockopt(client, SOL_SOCKET, SO_REUSEADDR, //line:netp:csapp:setsockopt
(setsockopt_ptr)&optval, sizeof(int));
my_addr.sin_family = AF_INET;
my_addr.sin_addr.s_addr = INADDR_ANY;
my_addr.sin_port = htons(atoi(port));
// This ip address will change according to the machine
inet_pton(AF_INET, serverAddr, &my_addr.sin_addr.s_addr);
// Explicitly assigning port number 12010 by
// binding client with that port
my_addr1.sin_family = AF_INET;
my_addr1.sin_addr.s_addr = INADDR_ANY;
my_addr1.sin_port = htons(atoi(localPort));
// This ip address will change according to the machine
inet_pton(AF_INET, localAddr, &my_addr1.sin_addr.s_addr);
if (bind(client, (struct sockaddr*)&my_addr1, sizeof(struct sockaddr_in)) == 0)
std::cout << "Binded Correctly\n";
else
{
std::cerr << "Unable to bind\n";
return -1;
}
socklen_t addr_size = sizeof my_addr;
int con = 0;
do { con = connect(client, (struct sockaddr*)&my_addr, sizeof my_addr); } while (con != 0);
if (con == 0)
std::cout << "Client Connected\n";
else
std::cerr << "Error in Connection\n";
return client;
}
My OS is Ubuntu20.04 and such code will work with normal output. However, when I remove comments in test() for listenfd, it will output Unable to bind infinitely.
I'm really a newbie in socket and network. I have no idea why that will happen. Also, is there a way for an address-port to be both server and client at the same time? (Maybe to be exact, can an address-port both connect actively & accept others' connection passively?) Thank you!
my_addr1.sin_family = AF_INET;
my_addr1.sin_addr.s_addr = INADDR_ANY;
my_addr1.sin_port = htons(atoi(localPort));
// This ip address will change according to the machine
inet_pton(AF_INET, localAddr, &my_addr1.sin_addr.s_addr);
if (bind(client, (struct sockaddr*)&my_addr1, sizeof(struct sockaddr_in)) == 0)
std::cout << "Binded Correctly\n";
This code binds a listening socket to port 19287, localPort is 19287.
// SOCKET listenfd = OpenListenFD("127.0.0.1", "19287");
And so does the commented-out code. Un-commenting it out results in two sockets attempting to listen on the same port. Surprise, surprise: this doesn't work.
The fact that the first socket that binds this port uses SO_REUSEADDR is irrelevant, and makes no difference. With or without SO_REUSEADDR, no two sockets can listen on the same port.
SO_REUSEADDR does not allow you to have multiple sockets listening on the same port, at the same time. All that SO_REUSEADDR does is allow a listening socket to be bound on a port that's temporarily "take out of commission", in specific edge cases, until a prescribed timeout expires. It allows a port to be bound as long as nothing else is listening on it, even if it's sitting in a "timeout room".
But it still won't allow you to have two different sockets listening on the same port, at the same time.

Issue with C++ UDP Client Hanging Up

The project I am working on uses TCP and UDP to create a file transport protocol. The TCP connection generates a random port in which it returns to the client and the client then connects to the server on that port number using UDP. UDP is then used to transfer a text file four characters a time to the server and the server will send back the characters capitalized in which will then be displayed on the client. The issue is that the client is hanging up when waiting for the server to send back the capitalized version of the characters. I will leave the code below. The part of the code that is not working is commented out towards the end of the client and server files. Any help is appreciated!
Client Code
// Libraries
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <unistd.h>
using namespace std;
int main(int argc, char* argv[]) {
// Variables
int port = strtol(argv[2], NULL, 10);
string file = argv[3];
int r_port;
FILE* fp;
string fileString;
string dataBuffer;
int charCounter = 0;
char c;
// *** Declare TCP socket ***
int tcpsocket = 0;
tcpsocket = socket(AF_INET, SOCK_STREAM, 0);
if (tcpsocket == -1) {
cerr << "Can't create a socket";
return 1;
}
// Get host IP address
struct hostent *s;
s = gethostbyname(argv[1]);
// Setting destination info
struct sockaddr_in server;
memset((char *) &server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(port);
bcopy((char *) s->h_addr, (char *) &server.sin_addr.s_addr, s->h_length);
// Connect to server
int connectRes = connect(tcpsocket, (sockaddr*)&server, sizeof(server));
if (connectRes == -1) {
cerr << "Can't connect to socket";
return 1;
}
// Sending data to server
char payload[512] = "117";
int sendRes = send(tcpsocket, payload, 512, 0);
if (sendRes == -1) {
cerr << "Could not send to server";
return 1;
}
// Receive r_port from server
memset(payload, 0, sizeof(payload));
recv(tcpsocket, payload, 512, 0);
r_port = strtol(payload, NULL, 10);
close(tcpsocket);
// *** Declare UDP socket ***
int udpsocket = 0;
udpsocket = socket(AF_INET, SOCK_DGRAM, 0);
if (udpsocket == -1) {
cerr << "Can't create a socket";
return 1;
}
// Get host IP address
s = gethostbyname(argv[1]);
// Setting destination info
memset((char *) &server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(r_port);
bcopy((char *) s->h_addr, (char *) &server.sin_addr.s_addr, s->h_length);
// File manipulation
fp = fopen(file.c_str(), "r");
while (c != EOF) {
c = fgetc(fp);
fileString += c;
charCounter++;
}
fclose(fp);
// UDP file transfer
for (int i = 4; i < 8; i++) {
dataBuffer += fileString[i];
}
socklen_t slen = sizeof(server);
memset(payload, 0, sizeof(payload));
strcpy(payload, dataBuffer.c_str());
sendRes = sendto(udpsocket, payload, 32, 0, (struct sockaddr *) &server, slen);
if (sendRes == -1) {
cerr << "Could not send to server";
return 1;
}
// Receive ack
// slen = sizeof(server);
// memset(payload, 0, sizeof(payload));
// recvfrom(udpsocket, payload, 32, 0, (sockaddr*)&server, &slen);
// cout << "Capitalized data: " << payload;
close(udpsocket);
return 0;
}
Server Code
// Libraries
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <stdlib.h>
#include <unistd.h>
#include <cstdlib>
#include <time.h>
#include <ctype.h>
using namespace std;
// Variables
int r_port;
string fileData;
// Generate Random Port
int randomPort() {
srand(time(NULL));
return rand() % ((65535 - 1024) + 1) + 1024;
}
// Capitalization Function
string capitalize(char* payload) {
int i = 0;
char c;
string charArr;
while(payload[i]) {
c = payload[i];
charArr += toupper(c);
i++;
}
return charArr;
}
int main(int argc, char* argv[]) {
// Variables
int port = strtol(argv[1], NULL, 10);
// *** Declare TCP socket ***
int tcpsocket = 0;
tcpsocket = socket(AF_INET, SOCK_STREAM, 0);
if (tcpsocket == -1) {
cerr << "Can't create a socket";
return -1;
}
// Receive data
struct sockaddr_in server;
memset((char *) &server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(tcpsocket, (struct sockaddr *) &server, sizeof(server)) == -1) {
cerr << "Can't bind to IP/Port";
return -2;
}
if (listen(tcpsocket, SOMAXCONN) == -1) {
cerr << "Can't listen";
return -3;
}
struct sockaddr_in client;
char payload[512];
socklen_t clen = sizeof(client);
int clientSocket = accept(tcpsocket, (sockaddr*)&client, &clen);
if (clientSocket == -1) {
cerr << "Problem with client connecting";
return -4;
}
recv(clientSocket, payload, 512, 0);
// Check client data
if (strtol(payload,NULL,10) == 117) {
r_port = randomPort();
cout << "Handshake detected. Selected the random port " << r_port << "\n";
}
else {
cout << "Error occurred\n";
}
// Return random port
memset(payload, 0, sizeof(payload));
sprintf(payload,"%ld",r_port);
int sendRes = send(clientSocket, payload, 512, 0);
if (sendRes == -1) {
cerr << "Could not send to server\n";
return 1;
}
close(clientSocket);
close(tcpsocket);
// *** Declare UDP socket ***
int udpsocket = 0;
udpsocket = socket(AF_INET, SOCK_DGRAM, 0);
if (udpsocket == -1) {
cerr << "Can't create a socket";
return -1;
}
// Receive data
memset((char *) &server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(r_port);
server.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(udpsocket, (struct sockaddr *) &server, sizeof(server)) == -1) {
cerr << "Can't bind to IP/Port";
return -2;
}
clen = sizeof(client);
memset(payload, 0, sizeof(payload));
recvfrom(udpsocket, payload, 32, 0, (sockaddr*)&client, &clen);
fileData = capitalize(payload);
cout << "Payload: " << fileData << "\n";
// Send ack
// socklen_t slen = sizeof(server);
// memset(payload, 0, sizeof(payload));
// strcpy(payload, fileData.c_str());
// sendRes = sendto(udpsocket, payload, 32, 0, (struct sockaddr *) &server, slen);
// if (sendRes == -1) {
// cerr << "Could not send to server";
// return 1;
// }
close(udpsocket);
return 0;
}
ssize_t sendto(**int sockfd**, const void *buf, size_t len, int flags,
const struct **sockaddr *dest_addr**, socklen_t addrlen);
Reference : https://linux.die.net/man/2/sendto
Your destination address should be : (sockaddr *) &client

udp poll sockets and multicast in C++

1) I am trying to make a simple game server using UDP. Would my code be the correct way to check if there is any reads from a single socket?
2) I want to recieve data from one user on a request ( he wants to move left), then update where the server thinks he or she is located, then broadcast the x , y coordinates. How would I implement a multicast reply with a different socket?
void run()
{
//logging file
ofstream log;
log.open("server_log.txt", ios::out | ios::app);
struct sockaddr_in myaddr; // our address
struct sockaddr_in remaddr; // remote address
socklen_t addrlen = sizeof(remaddr);
int recvlen;
int fd; // server socket that listens
int fd_reply; // this will be used to reply to all users
char buf[BUFSIZE]; // receive buffer
memset((char *)&myaddr, 0, sizeof(myaddr));
myaddr.sin_family = AF_INET;
myaddr.sin_addr.s_addr = htonl(INADDR_ANY);
myaddr.sin_port = htons(PORT);
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
std::time_t result = std::time(nullptr);
log << "Error: cannot create socket! " << "TIMESTAMP: " << std::asctime(std::localtime(&result)) << endl;
log.close();
return 0;
}
if (bind(fd, (struct sockaddr *)&myaddr, sizeof(myaddr)) < 0) {
std::time_t result = std::time(nullptr);
log << "Error: bind failed " << "TIMESTAMP: " << std::asctime(std::localtime(&result)) << endl;
log.close();
return 0;
}
pollfd fds;
memset(fds, 0, sizeof(fds));
fds[0].fd = fd;
fds[0].events = POLLIN;
while (1)
{
int rv = poll(ufds, 1, 3500);
if (rv == -1)
{
// error occured
}
else if (rv == 0)
{
//time out
}
else
{
//check for events on fd
if (fds.revents & POLLIN)
{
recvlen = recvfrom(fd, buf, BUFSIZE, 0, (struct sockaddr *)&remaddr, &addrlen);
}
}
}
}
Yes it looks okay.
Keep a list of all clients, and then in a loop send to all of them. To populate this list, all clients need to contact the server the first thing they do.

socket connection recvfrom no response C++

I am trying to get data from UDP broadcast, but no response from recvfrom function. Connection, binding, everything looks fine.
Can there be a problem with the broadcast?
Is there anyway I can check the exact error?
#include <iostream>
using namespace std;
//#include <ServerSocket.h>
#include <cstring>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
enter code here
int status;
int socketfd;
int enableMulticast = 1; // Argument for set socket
struct addrinfo host_info; // The struct that getaddrinfo() fills up with data.
struct addrinfo *host_info_list; // Pointer to the to the linked list of host_info's.
struct sockaddr_in socketAddr;
unsigned char incomming_data_buffer[IN_LEN];
socklen_t socklen;
#ifndef IN_LEN
#define IN_LEN 4096
#endif
memset(&host_info, 0, sizeof host_info);
memset(&socketAddr, 0, sizeof(struct sockaddr_in));
cout << "Setting up the structs..." << endl;
host_info.ai_family = AF_UNSPEC; // IP version not specified. Can be both.
host_info.ai_socktype = SOCK_DGRAM; // Use SOCK_STREAM for TCP or SOCK_DGRAM for UDP.
host_info.ai_protocol = IPPROTO_UDP; // The protocol type for UDP. If left as zero, it will return all types
status = getaddrinfo("UDP IP Address","Port", &host_info, &host_info_list);
socketfd = socket(host_info_list->ai_family, host_info_list->ai_socktype,
host_info_list->ai_protocol);
if (socketfd == -1) cout << "socket error " ;
else cout << "socket created successfully " ;
status = setsockopt(socketfd, SOL_SOCKET, SO_REUSEADDR, &enableMulticast, sizeof(enableMulticast));
socketAddr.sin_family = AF_INET;
socketAddr.sin_port = htons(INT_PORT);
socketAddr.sin_addr.s_addr = htonl(INADDR_ANY);
status = bind(socketfd, (struct sockaddr *)&socketAddr, sizeof(struct sockaddr_in));
status = connect(socketfd, host_info_list->ai_addr, host_info_list->ai_addrlen);
if (status < 0) cout << "connect error" <<endl ;
cout << "Waiting to receive data..." << endl;
socklen = sizeof(struct socketAddr);
while(1){
status = recvfrom(socketfd, incomming_data_buffer, IN_LEN, 0, (struct sockaddr *)&socketAddr, &socklen);
if(status >= 0) {
cout << "data received" ;
freeaddrinfo(host_info_list);
close(socketfd);
exit(0);
}
}
}
if(status = 0)
This can never be true. (1) it's a typo for status == 0; (2) it should be status >= 0, i.e.status is the length of the datagram received.

UDP respond on connected socket

So I've been programming with TCP for quite a while, and decided to pick up UDP. I'm not quite sure what needs to be done in order for me to have communication both ways across the WAN(or lan for that matter, easier on lan because I could just open two ports) With UDP once I send information from client to server how can I respond on that socket. Is there a way to connect directly?
(Current quick functions)
int udpsock(int port, const char* addr){
int handle = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
if (handle < 1)
return -1;
sockaddr_in address;
address.sin_family = AF_INET;
if (addr == INADDR_ANY)
address.sin_addr.s_addr = INADDR_ANY;
else
address.sin_addr.s_addr = inet_addr(addr);
address.sin_port = htons( (unsigned short) port );
if ( bind( handle, (const sockaddr*) &address, sizeof(sockaddr_in) ) < 0 )
return -1;
return handle;
}
string recvudp(int sock,const int size){
sockaddr_in SenderAddr;
int SenderAddrSize = sizeof (SenderAddr);
char buf[size];
int retsize = recvfrom(sock, buf, sizeof(buf), 0, (SOCKADDR *) & SenderAddr, &Sen derAddrSize);
if (retsize == -1){
cout << "\nRecv Error : " << WSAGetLastError();
if (WSAGetLastError() == WSAEWOULDBLOCK || WSAGetLastError() == 0){
return "";
}
return "\0";
}
else if (retsize < size){
buf[retsize] = NULL;
}
return buf;
}
int sendudp(string str, string ip, unsigned short port, int sock){
sockaddr_in dest;
dest.sin_family = AF_INET;
dest.sin_addr.s_addr = inet_addr( ip.c_str() );
dest.sin_port = htons( port );
int ret = sendto(sock,str.c_str(),str.size(),0, (sockaddr*)&dest,sizeof(dest));
if (ret == -1){
cout << "\nSend Error Code : " << WSAGetLastError();
}
return ret;
}
With this it's pretty easy to make a socket with port xxxx and have the partner send on that port to get data to the client, the forth part is where I'm having some trouble =]
Make your sendudp function take a sockaddr_in. You get one back from recvfrom and can pass it to sendto. Alternatively, pass the received sockaddr_in to connect and use send from then on.
I assume that functions you posted should be shared between client and server. They need to be slightly modified in order to achieve that. E.g. on the server side, recvudp should return client address (possibly as an out parameter) as it is needed later for sending message back to it. Furthermore, as client address structure is already filled (in recvudp on the server side or manually on the client side) we can just pass it to sendudp as its argument.
I've played with this a bit and created two simple projects in Visual Studio 2010: UDP Server and client. They both use shared functions mentioned above. This code is far from perfect and is aimed only to show basic UDP socket communication.
Shared.h:
#ifndef SHARED_H
#define SHARED_H
#include <winsock2.h>
#include <string>
int udpsock(int port, const char* addr);
std::string recvudp(int sock, const int size, sockaddr_in& SenderAddr, int& SenderAddrSize);
int sendudp(std::string str, sockaddr_in dest, int sock);
#endif
Shared.cpp:
#include "Include\shared.h" // path to header - you might use different one
#include <iostream>
using namespace std;
int udpsock(int port, const char* addr)
{
int handle = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
if (handle < 1)
return -1;
sockaddr_in address;
address.sin_family = AF_INET;
if (addr == INADDR_ANY)
address.sin_addr.s_addr = INADDR_ANY;
else
address.sin_addr.s_addr = inet_addr(addr);
address.sin_port = htons( (unsigned short) port );
if ( bind( handle, (const sockaddr*) &address, sizeof(sockaddr_in) ) < 0 )
return -1;
return handle;
}
// function should return sender address info (for the code the server)
string recvudp(int sock, const int size, sockaddr_in& SenderAddr, int& SenderAddrSize)
{
// TODO: use std::vector<char> here instead of char array
char* buf = 0;
buf = new char[size];
int retsize = recvfrom(sock, buf, size, 0, (sockaddr*) &SenderAddr, &SenderAddrSize);
if(retsize == -1)
{
cout << "\nRecv Error : " << WSAGetLastError();
if (WSAGetLastError() == WSAEWOULDBLOCK || WSAGetLastError() == 0)
{
return "";
}
return "\0";
}
else if (retsize < size)
{
buf[retsize] = NULL;
}
string str(buf);
delete[] buf;
return str;
}
// On the client side, prepare dest like this:
// sockaddr_in dest;
// dest.sin_family = AF_INET;
// dest.sin_addr.s_addr = inet_addr(ip.c_str());
// dest.sin_port = htons(port);
int sendudp(string str, sockaddr_in dest, int sock)
{
int ret = sendto(sock,str.c_str(),str.size(),0, (sockaddr*)&dest,sizeof(dest));
if (ret == -1)
{
cout << "\nSend Error Code : " << WSAGetLastError();
}
return ret;
}
Server: main.cpp:
#include <winsock2.h>
#include <string.h>
#include <iostream>
#include "..\Shared\Include\shared.h"
// Link with ws2_32.lib
#pragma comment(lib, "Ws2_32.lib")
#define SERVER_PORT 27015
#define MAX_MSG 1024
using namespace std;
int main(int argc, char *argv[])
{
WSADATA wsaData;
int nResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if(nResult != NO_ERROR)
{
cout << "WSAStartup failed with error: " << nResult << endl;
return 1;
}
sock = udpsock(SERVER_PORT, "127.0.0.1");
cout << "Waiting for datagram on port: " << SERVER_PORT << endl;
while(1)
{
sockaddr_in clientAddr;
// receive message
int clientAddrLen = sizeof(clientAddr);
cout << "Received message from the client: " << recvudp(sock, MAX_MSG, clientAddr, clientAddrLen) << endl;
sendudp("Hello from server!", clientAddr, sock);
}
WSACleanup();
return 0;
}
Client: main.cpp:
#include <winsock2.h>
#include <iostream>
#include "..\Shared\Include\shared.h"
using namespace std;
#define MAX_MSG 1024
// Link with ws2_32.lib
#pragma comment(lib, "Ws2_32.lib")
int main(int argc, char* argv[])
{
WSADATA wsaData;
int nResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (nResult != NO_ERROR)
{
cout << "WSAStartup failed with error: " << nResult << endl;
return 1;
}
SOCKET sock = INVALID_SOCKET;
// Create a socket for sending data - it does not need to be binded like listening socket!
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(sock == INVALID_SOCKET)
{
cout << socket failed with error: " << WSAGetLastError() << endl;
WSACleanup();
return 1;
}
unsigned short Port = 27015;
sockaddr_in dest;
dest.sin_family = AF_INET;
dest.sin_addr.s_addr = inet_addr("127.0.0.1");
dest.sin_port = htons(Port);
sendudp("Hello from client!", dest, sock);
sockaddr_in RecvAddr;
int recvaddrlen = sizeof(RecvAddr);
cout << "Received message from the server: " << recvudp(sock, MAX_MSG, RecvAddr, recvaddrlen) << endl;
cout << "Closing socket..." << endl;
nResult = closesocket(sock);
if(nResult == SOCKET_ERROR)
{
cout << "closesocket failed with error: " << WSAGetLastError() << endl;
WSACleanup();
return 1;
}
WSACleanup();
return 0;
}
If you run client twice output is:
Server:
Waiting for datagram on port: 27015
Received message from the client: Hello from client!
Received message from the client: Hello from client!
Client:
Received message from the server: Hello from server!
Closing socket...
UDP is connectionless protocol, server just needs to start listening on UDP port and client can send data (datagram) immediately, there is no need for connection establishment (e.g. with connect()/accept(), like in TCP).