Bellow is peace of code that I use to connect to socket in Windows. Sometimes I don't have IP dress, but have DNS name in inetAddr. Probably I must resolve address from host name, or it is possible create socket with DNS name? What is the best way to create socket when have DNS name?
commStatus communicate( const char * tx, char * rx, const int bufSize , const char * inetAddr, const int port )
{
...
SOCKET s;
struct sockaddr_in server;
server.sin_addr.s_addr = inet_addr(inetAddr);
server.sin_family = AF_INET;
server.sin_port = htons( port );
if((s = socket(AF_INET , SOCK_STREAM , 0 )) == INVALID_SOCKET)
{
FILELOGL("Could not create socket : " << WSAGetLastError(),Level::Error );
} else
{
if (connect(s , (struct sockaddr *)&server , sizeof(server)) < 0)
{
FILELOGL("connect error", Level::Error);
r= commStatus::COMM_NO_TRANSMIT ;
} else
{
...
}
}
...
}
There is only one way: Resolve the host name.
Here's a part of my code that I've modified and haven't tested, but it should work:
WSADATA wsdata;
const char * inetAddr
addrinfo hints, *res;
WSAStartup (MAKEWORD (2, 2), &wsdata);
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
if (getaddrinfo("someaddress.com", NULL, &hints, &res) != 0)
return false;
inetAddr = inet_ntoa(((sockaddr_in *) res -> ai_addr) -> sin_addr);
You need to resolve the DNS name. Use getaddrinfo() for that. Just note that a DNS name can resolve to multiple IPs, both IPv4 and IPv6, so you have to call getaddrinfo() first in order to know how many IPs are reported, and what type of socket to create for each IP.
For example:
commStatus communicate( const char * tx, char * rx, const int bufSize , const char * inetAddr, const int port )
{
...
SOCKET s = INVALID_SOCKET;
struct addrinfo hint = {0};
hint.ai_flags = AI_NUMERICHOST;
hint.ai_family = AF_UNSPEC;
hint.ai_socktype = SOCK_STREAM;
hint.ai_protocol = IPPROTO_TCP;
struct addrinfo *addrs = NULL;
int ret = getaddrinfo(inetAddr, NULL, &hint, &addrs);
if (ret == EAI_NONAME)
{
hint.ai_flags = 0;
ret = getaddrinfo(inetAddr, NULL, &hint, &addrs);
}
if (ret != 0)
{
FILELOGL("Could not resolve inetAddr: " << ret, Level::Error);
}
else
{
for (struct addrinfo *addr = addrs; addr != NULL; addr = addr->ai_next)
{
s = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if (s == INVALID_SOCKET)
{
FILELOGL("Could not create socket : " << WSAGetLastError(), Level::Error);
break;
}
if (connect(s, addr->ai_addr, addr->ai_addrlen) == 0)
break;
closesocket(s);
s = INVALID_SOCKET;
if (addr->ai_next == NULL)
{
FILELOGL("connect error", Level::Error);
}
}
freeaddrinfo(addrs);
if (s != INVALID_SOCKET)
{
...
}
}
...
}
Related
For a class I have to implement an HTTP web server in C++. I currently have the server to the point that it can listen and accept connections, but I can't figure out how to accept data and parse it. When I send a request I get an error saying "Transport endpoint is nt connected." Here's what my code looks like so far:
int main(int argc, char *argv[]) {
// parse CLI args
if (argc != 3) { /** change to 3 once DOCROOT is added */
cout << "invalid arguments" << endl;
exit(1);
}
const char *DOCROOT = argv[1];
const char *PORT = argv[2];
// declare vars....from beej's so some not in use yet
int sockfd, new_fd;
struct addrinfo hints, *servinfo, *p;
struct sockaddr_storage their_addr;
socklen_t sin_size;
struct sigaction sa;
int yes = 1;
char s[INET6_ADDRSTRLEN];
int rv;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
// try getaddrinfo
if ((rv = getaddrinfo(nullptr, PORT, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// loop through getaddrinfo response and bind when possible
for (p=servinfo; p!=nullptr; p=p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
perror("server: socket");
continue;
}
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
perror("setsockopt");
exit(1);
}
if (::bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
perror("server: bind");
continue;
}
break;
}
freeaddrinfo(servinfo);
if (p == nullptr) {
fprintf(stderr, "server: failed to bind\n");
exit(1);
}
/** choose backlog number and declare constant */
if (listen(sockfd, 5) == -1) {
perror("listen");
exit(1);
}
sa.sa_handler = sigchld_handler; // reap all dead processes
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, nullptr) == -1) {
perror("sigaction");
exit(1);
}
printf("server: waiting for connections on port %s...\n", PORT);
while(true) { /** this will be the main accept loop */
sin_size = sizeof their_addr;
new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
if (new_fd == -1) {
perror("accept");
continue;
}
inet_ntop(their_addr.ss_family,
get_in_addr((struct sockaddr *)&their_addr),
s, sizeof s);
printf("server: got connection from %s\n", s);
// receive
char recvbuf[1024];
int recvbuflen = 1024;
int request = recv(sockfd, recvbuf, recvbuflen, 0);
if (request == -1) perror("error receiving data");
if(send(new_fd, "Data received", 15, 0) == -1)
perror("send");
close(new_fd);
exit(0);
}
return 0;
}
I am trying to set up a simple client-server system for message passing, using a local PC and a cloud virtual machine (Ubuntu 16) hosted by AWS. It's quite basic right now and will be extended using e.g. protobuf, but right now it's about creating a basic working example before I start integrating the communication scheme into my actual program.
The 'server' waits for a request from the 'client', and will then send the data to the client. The client is given the IP address of the server on startup.
If I run the server part on my AWS virtual machine and the client part on my local PC, everything works perfect, the data is transferred. However, it does not work the other way round, I cannot reach my local PC from the AWS virtual machine. I'm pretty sure AWS security groups are correct, since it works with the server running in the virtual machine. Also the IP of the local machine should be correct, since when running the server on the virtual machine, it output the IP of the client that requested the data.
I searched before in some forums, and I figured out it seems generally more complicated to reach the local PC from the AWS virtual machine. However, I didn't find a solution to my problem, and I assume it must be possible to send data from a cloud virtual machine to a local PC...
Thanks for any help!
***** Client side *****
-- call client with IP of server as arg --
#define PORT "3490" // the port client will be connecting to
int main(int argc, char *argv[])
{
int sockfd, numbytes;
unsigned char buf[MAXDATASIZE];
struct addrinfo hints, *servinfo, *p;
int rv;
char s[INET6_ADDRSTRLEN];
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if ((rv = getaddrinfo(argv[1], PORT, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// loop through all the results and connect to the first we can
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -1) {
continue;
}
if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
continue;
}
break;
}
inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr), s, sizeof s);
printf("client: connecting to %s\n", s);
freeaddrinfo(servinfo);
if ((numbytes = recv(sockfd, buf, MAXDATASIZE-1, 0)) == -1) {
exit(1);
}
//deserialize and output data -- omitted
return 0;
}
***** Server side *****
int main(void)
{
srand (time(NULL));
mydata md0; //data structure that will be sent
//write members of md0 -- omitted
unsigned char buf[MAXDATASIZE],*ptr;
ptr = Serialize_mydata(buf,&md0);
int sockfd, new_fd; // listen on sock_fd, new connection on new_fd
struct addrinfo hints, *servinfo, *p;
struct sockaddr_storage their_addr; // connector's address information
socklen_t sin_size;
struct sigaction sa;
int yes=1;
char s[INET6_ADDRSTRLEN];
int rv;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; // use my IP
if ((rv = getaddrinfo(NULL, PORT, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// loop through all the results and bind to the first we can
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype,p->ai_protocol)) == -1) {
perror("server: socket");
continue;
}
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
exit(1);
}
if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
continue;
}
break;
}
freeaddrinfo(servinfo); // all done with this structure
if (listen(sockfd, BACKLOG) == -1) {
exit(1);
}
sa.sa_handler = sigchld_handler; // reap all dead processes
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
exit(1);
}
printf("server: waiting for connections...\n");
while(1) { // main accept() loop
sin_size = sizeof their_addr;
new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
if (new_fd == -1) {
continue;
}
inet_ntop(their_addr.ss_family, get_in_addr((struct sockaddr *)&their_addr), s, sizeof s);
printf("server: got connection from %s\n", s);
if (!fork()) { // this is the child process
close(sockfd); // child doesn't need the listener
if (send(new_fd, buf, ptr - buf, 0) == -1)
close(new_fd);
exit(0);
}
close(new_fd); // parent doesn't need this
}
return 0;
}
I found the following code to open a connection in C:
int OpenConnection(const char *hostname, int port)
{
int sd;
struct hostent *host;
struct sockaddr_in addr = {0};
if ((host = gethostbyname(hostname)) == NULL)
{
perror(hostname);
abort();
}
sd = socket(PF_INET, SOCK_STREAM, 0);
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = *(long *)(host->h_addr_list[0]);
if (connect(sd, (struct sockaddr *)&addr, sizeof(addr)) != 0)
{
close(sd);
perror(hostname);
abort();
}
return sd;
}
As I was rewriting this code in C++, I found out that I should be using getaddrinfo instead of gethostbyname, according to a comment in this post: C - What does *(long *)(host->h_addr); do?.
I was looking at Beej's guide to socket programming, and found the following example:
int status;
struct addrinfo hints;
struct addrinfo *servinfo; // will point to the results
memset(&hints, 0, sizeof hints); // make sure the struct is empty
hints.ai_family = AF_UNSPEC; // don't care IPv4 or IPv6
hints.ai_socktype = SOCK_STREAM; // TCP stream sockets
hints.ai_flags = AI_PASSIVE; // fill in my IP for me
if ((status = getaddrinfo(NULL, "3490", &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(status));
exit(1);
}
// servinfo now points to a linked list of 1 or more struct addrinfos
// ... do everything until you don't need servinfo anymore ....
freeaddrinfo(servinfo); // free the linked-list
However, this example applies to creating a server, which makes me wonder
A] is getaddrinfosupposed to be used for clients
and
B] How would I modify the hints struct and the function parameters in order to make this code suitable for creating a client?
Currently, I have the following code but I
struct addrinfo hints = {0};
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
struct addrinfo *client_info;
const int status = getaddrinfo(hostname, port, &hints, &client_info); //don't know if its correct
Update:
For anyone who may find it useful, here is my final code after reading the accepted answer:
int OpenConnection(const char *hostname, const char *port)
{
struct hostent *host;
if ((host = gethostbyname(hostname)) == nullptr)
{
//More descriptive error message?
perror(hostname);
exit(EXIT_FAILURE);
}
struct addrinfo hints = {0}, *addrs;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
const int status = getaddrinfo(hostname, port, &hints, &addrs);
if (status != 0)
{
fprintf(stderr, "%s: %s\n", hostname, gai_strerror(status));
exit(EXIT_FAILURE);
}
int sfd, err;
for (struct addrinfo *addr = addrs; addr != nullptr; addr = addr->ai_next)
{
sfd = socket(addrs->ai_family, addrs->ai_socktype, addrs->ai_protocol);
if (sfd == ERROR_STATUS)
{
err = errno;
continue;
}
if (connect(sfd, addr->ai_addr, addr->ai_addrlen) == 0)
{
break;
}
err = errno;
sfd = ERROR_STATUS;
close(sfd);
}
freeaddrinfo(addrs);
if (sfd == ERROR_STATUS)
{
fprintf(stderr, "%s: %s\n", hostname, strerror(err));
exit(EXIT_FAILURE);
}
return sfd;
}
The gethostbyname() and gethostbyaddr() functions are deprecated on most platforms, and they don't implement support for IPv6. IPv4 has reached its limits, the world has been moving to IPv6 for awhile now. Use getaddrinfo() and getnameinfo() instead, respectively.
To answer your questions:
A. getaddrinfo() and getnameinfo() can be used for clients and servers alike, just as gethostbyname() and gethostbyaddr() can be. They are just host/address resolution functions, how the resolved values get used is up to the calling app to decide.
B. client code using getaddrinfo() would look something like this:
int OpenConnection(const char *hostname, int port)
{
int sd, err;
struct addrinfo hints = {}, *addrs;
char port_str[16] = {};
hints.ai_family = AF_INET; // Since your original code was using sockaddr_in and
// PF_INET, I'm using AF_INET here to match. Use
// AF_UNSPEC instead if you want to allow getaddrinfo()
// to find both IPv4 and IPv6 addresses for the hostname.
// Just make sure the rest of your code is equally family-
// agnostic when dealing with the IP addresses associated
// with this connection. For instance, make sure any uses
// of sockaddr_in are changed to sockaddr_storage,
// and pay attention to its ss_family field, etc...
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
sprintf(port_str, "%d", port);
err = getaddrinfo(hostname, port_str, &hints, &addrs);
if (err != 0)
{
fprintf(stderr, "%s: %s\n", hostname, gai_strerror(err));
abort();
}
for(struct addrinfo *addr = addrs; addr != NULL; addr = addr->ai_next)
{
sd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if (sd == -1)
{
err = errno;
break; // if using AF_UNSPEC above instead of AF_INET/6 specifically,
// replace this 'break' with 'continue' instead, as the 'ai_family'
// may be different on the next iteration...
}
if (connect(sd, addr->ai_addr, addr->ai_addrlen) == 0)
break;
err = errno;
close(sd);
sd = -1;
}
freeaddrinfo(addrs);
if (sd == -1)
{
fprintf(stderr, "%s: %s\n", hostname, strerror(err));
abort();
}
return sd;
}
I've always used gethostbyname() since "forever". It's always worked, it continues to work, and it's "simpler".
getaddrinfo() is the newer function:
http://man7.org/linux/man-pages/man3/getaddrinfo.3.html
The getaddrinfo() function combines the
functionality provided by the gethostbyname(3) and getservbyname(3)
functions into a single interface, but unlike the latter functions,
getaddrinfo() is reentrant and allows programs to eliminate
IPv4-versus-IPv6 dependencies.
I understand that getaddrinfo() ismore robust, more efficient, and more secure: You shouldn't be using gethostbyname() anyway
ADDENDUM:
In reply to your specific questions:
A] getaddrinfo() is preferred over gethostbyname() to lookup the IP address of a hostname; either "client" or "server".
B] Q: How would I modify the hints struct and the function parameters?
A: The "hints" look OK, but I would probably modify the port to NULL.
Here's a complete example:
https://www.kutukupret.com/2009/09/28/gethostbyname-vs-getaddrinfo/
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
struct addrinfo hints, *res, *p;
int status;
char ipstr[INET6_ADDRSTRLEN];
if (argc != 2) {
fprintf(stderr, "Usage: %s hostname\n", argv[0]);
return 1;
}
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version
hints.ai_socktype = SOCK_STREAM;
if ((status = getaddrinfo(argv[1], NULL, &hints, &res)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
return 2;
}
for(p = res;p != NULL; p = p->ai_next) {
void *addr;
if (p->ai_family == AF_INET) {
return 1;
} else {
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
addr = &(ipv6->sin6_addr);
/* convert the IP to a string and print it: */
inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);
printf("Hostname: %s\n", argv[1]);
printf("IP Address: %s\n", ipstr);
}
}
freeaddrinfo(res); // free the linked list
return 0;
}
I'm trying to connect to a UDP tracker server using the code below, but I'm not getting any responses from the tracker...
I gleaned what I could from this link:
http://xbtt.sourceforge.net/udp_tracker_protocol.html
and I thought I got it...but apparently not. The code executes fine and then hangs at the call to RecvFrom. So I'm guessing I'm either not sending the correct data, or I'm sending it to the wrong place.....
struct ConnectionIdRequest_t {
uint64_t connectionId;
uint32_t action;
int32_t transactionId;
} typedef ConnectionIdRequest;
const bool UdpTorrentTrackerComm::initiateConnection(const int amountUploaded,
const int amountDownloaded,
const int amountLeft) {
struct sockaddr_in serverAddress, clientAddress;
struct hostent * host;
struct in_addr address;
//Setup dummy client address
clientAddress.sin_family = AF_INET;
clientAddress.sin_addr.s_addr = htonl(INADDR_ANY);
clientAddress.sin_port = htons(0);
//Setup server address
serverAddress.sin_family = AF_INET;
serverAddress.sin_port = htons(portNumber);
//SETUP in_addr server address
//If we have an IP
if (trackerAddress) {
if (isIp4Address(*trackerAddress)) {
//retrieve hostname from ip address
if (inet_aton(trackerAddress->c_str(), &address)) {
host = gethostbyaddr((const char *) &address, sizeof(address), AF_INET);
trackerHostname = new std::string(host->h_name);
}
else {
return false;
}
}
else {
return false;
}
}
else {
//retrieve ip address from hostname
host = gethostbyname(trackerHostname->c_str());
address.s_addr = ((struct in_addr *) host->h_addr_list)->s_addr;
trackerAddress = new std::string(inet_ntoa(address));
}
std::cout << *trackerAddress << std::endl;
//Convert trackerAddress to network format
if(!inet_aton(trackerAddress->c_str(), &serverAddress.sin_addr)) {
return false;
}
int sockFd = -1;
//Add IPv6 in the future
if ((sockFd = Socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
return false;
}
//Bind my address to the socket
if (Bind(sockFd, (struct sockaddr *) &clientAddress, sizeof(clientAddress)) == - 1) {
return false;
}
std::cout << "SendTo\n";
ConnectionIdRequest * idRequest = createConnectionIdRequest();
if (SendTo(sockFd, idRequest, sizeof(*idRequest), 0,
(struct sockaddr *) &serverAddress, sizeof(serverAddress)) == -1) {
return false;
}
timeRequestSent = clock();
std::cout << "Sent: " << idRequest->connectionId << "|||" << idRequest->action << "|||" << idRequest->transactionId << std::endl;
std::cout << "RecvFrom\n";
char buffer[3000];
socklen_t serverAddressLength = sizeof(serverAddress);
while(true) {
if (RecvFrom(sockFd, buffer, 3000, 0,
(struct sockaddr *) &serverAddress, &serverAddressLength) == - 1) {
break;
std::cout << "breaking...\n";
}
}
std::cout << "The buffer is: " << buffer << std::endl;
Close(sockFd);
return true;
}
ConnectionIdRequest * UdpTorrentTrackerComm::createConnectionIdRequest() {
ConnectionIdRequest * idRequest = new ConnectionIdRequest;
generatePeerId();
idRequest->connectionId = htonll(0x41727101980);
idRequest->action = htonl(CONNECT);
idRequest->transactionId = htonl(*peerId);
return idRequest;
}
EDIT: Alright I made the one change that Arvid suggested, but that didn't help any. I'm going through and making sure I'm converting all the bytes being sent are in network byte order...Maybe I'm missing something......
it looks like you're conflating transaction ID and peer ID. They are different. The transaction ID is the cookie you send in order to match returning packets to the correct request.
It also looks like you're not initializing the connectionID. You have to set it to the magic 64 bit number in the initial connect message. 0x41727101980
You can find an alternative protocol description here.
I ended up getting it to work. The problem was mostly that I was not converting all of the values I needed to be converting to big endian (network ordering), along with using outdated functions, (gethostbyname etc).
If you'd like more details just comment.
This is the code that works for me to establish a link with the tracker server:
NOTE: serverAddress and clientAddress are class fields of type: struct sockaddr_in
const bool UdpTorrentTrackerComm::initiateConnection() {
//Setup dummy client address
clientAddress.sin_family = AF_INET;
clientAddress.sin_addr.s_addr = htonl(INADDR_ANY);
clientAddress.sin_port = htons(51413);
//Setup server address
serverAddress.sin_family = AF_INET;
serverAddress.sin_port = htons(portNumber);
//SETUP in_addr server address
//If we have an IP
if (trackerAddress) {
if (isIp4Address(*trackerAddress)) {
//Convert human readable trackerAddress to network byte order ip address and place in serverAddress.sin_addr
if (inet_pton(AF_INET, trackerAddress->c_str(), &(serverAddress.sin_addr))) {
//retrieve hostname and service type from ip address
char hostBuffer[100], serviceBuffer[100];
getnameinfo((struct sockaddr *) &serverAddress, sizeof(serverAddress),
hostBuffer, sizeof(hostBuffer),
serviceBuffer, sizeof(serviceBuffer),
NI_NAMEREQD | NI_DGRAM);
trackerHostname = new std::string(hostBuffer);
}
else {
return false;
}
}
else {
return false;
}
}
else {
//Setup structs to be used in getaddrinfo
struct addrinfo hints;
struct addrinfo * result, * resultPointer;
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = 0;
hints.ai_protocol = 0;
//Convert port number to string to pass to getaddrinfo
std::stringstream ss;
ss << portNumber;
std::string portNumberString = ss.str();
//retrieve ip address from hostname--------
if (GetAddrInfo(trackerHostname->c_str(), portNumberString.c_str(), &hints, &result) != 0) {
return false;
}
//Iterate over results for IP address V4 (ADD V6 later!)
char ipBuffer[INET_ADDRSTRLEN];
for (resultPointer = result; resultPointer != NULL; resultPointer = resultPointer->ai_next) {
//If we have an IPv4 address
if (resultPointer->ai_family == AF_INET) {
//convert to presentation format and store in ipBuffer
inet_ntop(AF_INET, &((struct sockaddr_in *) resultPointer->ai_addr)->sin_addr, ipBuffer, INET_ADDRSTRLEN);
}
}
//Free result
freeaddrinfo(result);
//Convert ipBuffer to std::string and store in trackerAddress field
trackerAddress = new std::string(ipBuffer);
//Convert trackerAddress to network format
if(!inet_pton(AF_INET, trackerAddress->c_str(), &serverAddress.sin_addr)) {
return false;
}
}
int sockFd = -1;
//Add IPv6 in the future
if ((sockFd = Socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
return false;
}
//Bind my address to the socket
if (Bind(sockFd, (struct sockaddr *) &clientAddress, sizeof(clientAddress)) == - 1) {
return false;
}
//Send a request to the tracker
ConnectionIdRequest * idRequest = createConnectionIdRequest();
if (SendTo(sockFd, idRequest, sizeof(*idRequest), 0,
(struct sockaddr *) &serverAddress, sizeof(serverAddress)) == -1) {
return false;
}
timeRequestSent = clock();
//Re-send until timeout.....
ConnectionIdResponse idResponse;
socklen_t serverAddressLength = sizeof(serverAddress);
while((timeRequestSent - clock()) / 1000 < SECONDS_UNTIL_TIMEOUT) {
//Response received!
if (RecvFrom(sockFd, &idResponse, sizeof(idResponse), 0,
(struct sockaddr *) &serverAddress, &serverAddressLength) > 0) {
break;
}
}
//Set class fields that will persist
activeSocket = sockFd;
connectionId = ntohll(idResponse.connectionId);
delete idRequest;
return true;
}
Where the two structs, ConnectionIdResponse and ConnectionIdRequest are defined as:
/* Struct used to send a request for a connectionId to the tracker server.*/
struct ConnectionIdRequest_t {
uint64_t connectionId;
uint32_t action;
uint32_t transactionId;
} typedef ConnectionIdRequest;
/* Struct used in receipt of a request for a connectionId from the tracker server. */
struct ConnectionIdResponse_t {
uint32_t action;
uint32_t transactionId;
uint64_t connectionId;
} typedef ConnectionIdResponse;
I am working on some networking code. I have 2 solutions running on my same machine. The first is my server and the other is my client. I am using C++ and winsock for the networking solution.
Here is my server code (setup).
addrinfo hints;
addrinfo* pAddrInfo;
char pIPAddress[INET6_ADDRSTRLEN];
char pPortFinal[128];
unsigned long tempUL;
int error;
memset(&hints, 0, sizeof hints); // make sure the struct is empty
hints.ai_family = AF_UNSPEC;//AF_INET; // don't care IPv4 or IPv6
hints.ai_socktype = type;
hints.ai_flags = AI_PASSIVE; // fill in my IP for me
error = getaddrinfo(NULL/*"localhost"*/, pPort, &hints, &pAddrInfo);
if(error != 0)
{
GetWinSockError(error);
return;
}
for(pAddrInfo = pAddrInfo; pAddrInfo != NULL; pAddrInfo = pAddrInfo->ai_next)
{
this->m_Socket = socket(pAddrInfo->ai_family, pAddrInfo->ai_socktype, pAddrInfo->ai_protocol);
if(bind(this->m_Socket, pAddrInfo->ai_addr, pAddrInfo->ai_addrlen) != 0)
continue;
break;
}
if(!pAddrInfo)
{
GetWinSockError(WSAGetLastError());
freeaddrinfo(pAddrInfo);
this->ShutDown();
return;
}
//7 = magic number recommended by 'beej's guide' online
if(listen(this->m_Socket, 7) != 0)
{
GetWinSockError(WSAGetLastError());
freeaddrinfo(pAddrInfo);
this->ShutDown();
return;
}
getnameinfo(pAddrInfo->ai_addr, pAddrInfo->ai_addrlen, pIPAddress, sizeof pIPAddress, pPortFinal, sizeof pPortFinal, NI_NUMERICHOST | NI_NUMERICSERV);
this->m_pIPAddress = Helpers::String::NewCopy(pIPAddress);
this->m_pPort = Helpers::String::NewCopy(pPortFinal);
cout << "IP:" << this->m_pIPAddress << " Port:" << this->m_pPort << "\n";
this->SetState(NET_COMM_STATE__READY);
freeaddrinfo(pAddrInfo);
Here is my server code (tick).
NetServerClient* pNetComm;
sockaddr* pSockAddr;
sockaddr_storage their_addr;
socklen_t addr_size;
void* pTempBuffer;
char pIPAddress[INET6_ADDRSTRLEN];
char pPort[256];
unsigned short port;
int newClientSocket;
addr_size = sizeof their_addr;
newClientSocket = accept(this->m_Socket, (struct sockaddr*)(&their_addr), &addr_size);
if(newClientSocket == -1)
{
if(WSAGetLastError() != WSAEWOULDBLOCK)
GetWinSockError(WSAGetLastError());
return;
}
pSockAddr = (struct sockaddr*)(&their_addr);
pNetComm = new NetServerClient(newClientSocket);
addr_size = sizeof their_addr;
if(bind(newClientSocket, pSockAddr, addr_size) != 0)
{
GetWinSockError(WSAGetLastError());
pNetComm->ShutDown();
return;
}
if (pSockAddr->sa_family == AF_INET)
{
pTempBuffer = &(((struct sockaddr_in*)pSockAddr)->sin_addr);
port = ((struct sockaddr_in*)pSockAddr)->sin_port;
}
else
{
pTempBuffer = &(((struct sockaddr_in6*)pSockAddr)->sin6_addr);
port = ((struct sockaddr_in6*)pSockAddr)->sin6_port;
}
inet_ntop(their_addr.ss_family, pTempBuffer, pIPAddress, sizeof pIPAddress);
_snprintf(pPort, 256, "%d", (unsigned int)port);
pNetComm->StartCommunication(pIPAddress, pPort);
this->m_vpClients.push_back(pNetComm);
And here is my client code:
addrinfo hints;
addrinfo* pAddrInfo;
unsigned long tempUL;
int error;
memset(&hints, 0, sizeof hints); // make sure the struct is empty
hints.ai_family = AF_UNSPEC; // don't care IPv4 or IPv6
hints.ai_socktype = type;
error = getaddrinfo(pIPAddress, pPort, &hints, &pAddrInfo);
if(error != 0)
{
GetWinSockError(error);
return;
}
for(pAddrInfo = pAddrInfo; pAddrInfo != NULL; pAddrInfo = pAddrInfo->ai_next)
{
this->m_Socket = socket(pAddrInfo->ai_family, pAddrInfo->ai_socktype, pAddrInfo->ai_protocol);
if(connect(this->m_Socket, pAddrInfo->ai_addr, pAddrInfo->ai_addrlen) != 0)
continue;
break;
}
if(!pAddrInfo)
{
GetWinSockError(WSAGetLastError());
freeaddrinfo(pAddrInfo);
this->ShutDown();
return;
}
this->SetState(NET_COMM_STATE__READY);
freeaddrinfo(pAddrInfo);
The problem is that connect on the client always fails. I get the error "WSAECONNREFUSED: Connection refused".
I am pretty certain the reason my connection fails is because my client is not passing the correct IP address to getaddrinfo. When I run my server, I output the IP and port of the server solution. Then I have my client solution call it's getaddrinfo with the same IP and port info. However, my server solution does not appear to be gathering the correct IP and port info. It always tell me it's IP is either 0.0.0.0 (IPv4) or :: (IPv6). I don't understand how I get the proper IP for my client to connect to my server.
I have read these posts Getting server ip 0.0.0.0:0 from getaddrinfo() and sin_port returned in getaddrinfo wrong already and tried their solutions, but they didn't work for me.
Any help would be much appreciated.