How to write to a socket file descriptor without using ``connect()``? - c++

I'm trying to write through a socket using its descriptor. I have used bind() to bind the socket to the address I want. Whenever I'm using sendto() everything is OK. But whenever I'm using write() (or send()) I'll face Destination address required(errno 89).
What I'm trying to do is to write through the socket using iovec. In this case we don't have any alternative for writev() to get destination address. As I don't want to connect every single time, I was wondering whether I can do it without having to use connect() or accept() syscalls.
Here is the code I wrote:
int sock = 0;
struct sockaddr_in serv_addr;
char *hello = "chetori to";
if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
printf("\n Socket creation error \n");
return -1;
}
int opt = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)))
{
perror("setsockopt");
exit(EXIT_FAILURE);
}
string interface_name = "lo";
ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", interface_name.c_str());
if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, reinterpret_cast<void*>(&ifr), sizeof(ifr)) < 0)
throw exception(); // Imaginary exception
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
string interface_address = "127.0.0.1";
if (inet_aton(interface_address.c_str(), &serv_addr.sin_addr) == 0)
throw exception(); // Imaginary exception
serv_addr.sin_port = htons(PORT);
if (bind(sock, reinterpret_cast<sockaddr*>(&serv_addr), sizeof(serv_addr)) < 0)
throw exception(); // Imaginary exception
char f[1024] = "Hello there";
char sec[1024] = "How are you";
char th[1024] = "I'm fine";
char fh[1024] = "Bye bye";
struct iovec bufs[] = {
{ .iov_base = f, .iov_len = sizeof(f) },
{ .iov_base = sec, .iov_len = sizeof(sec) },
{ .iov_base = th, .iov_len = sizeof(th) },
{ .iov_base = fh, .iov_len = sizeof(fh) },
};
int iovcnt = sizeof(bufs) / sizeof(struct iovec);
writev(sock, bufs, iovcnt);
Note that Interface name and IP address have no problem and are correct.

Related

How to send buffer data from client to server 4 characters at a time? (C++)

I am trying to build a UDP file transfer system with C++ that will allow the client to send four characters of a text file to the server at a time. The server will then take the four characters and add them to a new file on the server. I am confused on how to actually implement this. I know that a loop of some kind will have to be created on the client and server to continuously send data from the client and receive data on the server I just don't know how to implement it. I will post the code that I have so far for the client and server below. Any help is appreciated!
Client Code
// *** 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
socklen_t slen = sizeof(server);
memset(payload, 0, sizeof(payload));
strcpy(payload, dataBuffer.c_str());
sendRes = sendto(udpsocket, payload, 512, 0, (struct sockaddr *) &server, slen);
if (sendRes == -1) {
cerr << "Could not send to server";
return 1;
}
close(udpsocket);
return 0;
Server Code
// *** 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, 512, 0, (sockaddr*)&client, &clen);
cout << "Payload: " << payload << "\n";
close(udpsocket);
return 0;
}

Sending UDP package to Lifx and recvfrom

I am trying to send a broadcast UDP package to all my Lifx devices (wifi lamps). The devices should answer with another package to the same port I sent the first package from.
I am so far, that I see my sent broadcast package in wireshark. I also see the reply from the Lifx lamp.
But my program is stuck on recvfrom.
int sockfd;
struct sockaddr_in saddr_send, saddr_recv;
if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0 ) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
int one = 1;
if ((setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (void*) &one, sizeof(one))) < 0 ) {
perror("set socket option failed");
exit(EXIT_FAILURE);
}
if ((setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one))) < 0 ) {
perror("set socket option failed");
exit(EXIT_FAILURE);
}
memset(&saddr_send, 0, sizeof(saddr_send));
memset(&saddr_recv, 0, sizeof(saddr_recv));
// Filling dest information
saddr_send.sin_family = AF_INET; // IPv4
saddr_send.sin_addr.s_addr = INADDR_BROADCAST;
saddr_send.sin_port = htons(PORT);
// Filling source information
saddr_recv.sin_family = AF_INET; // IPv4
saddr_recv.sin_addr.s_addr = INADDR_ANY;
saddr_recv.sin_port = htons(PORT);
// Bind the socket with the server address
if (bind(sockfd, (const struct sockaddr *)&saddr_recv, sizeof(saddr_recv)) < 0 ) {
perror("bind failed");
exit(EXIT_FAILURE);
}
char buffer[BUFF_SIZE];
size_t length;
unsigned size = sizeof(saddr_recv);
char recvbuffer[BUFF_SIZE];
length = sendMessage->getEncodedHeader(buffer, BUFF_SIZE);
int e = sendto(sockfd, buffer, length, 0, (struct sockaddr*) &saddr_send, sizeof(saddr_send));
if (e < 0) {
printf("%i\n", errno);
perror("Send error: ");
}
unsigned int r = recvfrom(sockfd, (void*)&recvbuffer, sizeof(recvbuffer), 0, (struct sockaddr*)&saddr_recv, &size);
if (r < sizeof(lifx_protocol_header)) {
printf("package too short");
}
close(sockfd);
I am not sure if I made a conceptional mistake or if I am to slow with calling recvfrom.
Any idea on additional points I could try to solve my problem?

FIXED: C++ server/client program: "Connection refused"

I'm writing a simple program to transfer files from server to client (both on the same computer for now). Using "telnet 127.0.0.1 [port]", I can succesfully get the file from the server, but when I run the client, the server refuses connection. I suspect that the client is trying to connect to the wrong address, but I'm not sure. I also added some GDB test output if it helps.
Server: "./server 0 4100 bigfile 100 0.01"
int main(int argc, char* argv[])
{
int sockfd;
if(!(sockfd = socket(AF_INET, SOCK_STREAM, 0))) {
error("Failed to create socket");
}
struct sockaddr_in serv_addr, cl_addr;
bzero((void*) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(atoi(argv[2]));
if(bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
error("Failed to bind");
}
listen(sockfd, 10);
int fd = open(argv[3], O_RDONLY);
int bufsize = atoi(argv[4]);
int packet_period = atoi(argv[5]);
size_t cl_addr_len = sizeof(cl_addr);
char *buf = new char[bufsize];
while(true) {
int sd;
cout << "waiting for client..." << endl;
if(!(sd = accept(sockfd, (struct sockaddr *) &cl_addr, (socklen_t*) &cl_addr_len))) {
error("Failed to acccept");
}
cout << "Accepted client connection" << endl;
lseek(fd, 0, SEEK_SET);
while(int n = read(fd, buf, bufsize)) {
cout << "Transferring " << buf << endl;
usleep(100000);
write(sd, buf, n);
}
}
}
Client: "./client 0 127.0.0.1 4100 bigfile stats"
int main(int argc, char *argv[])
{
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
error("Failed to open socket");
}
struct sockaddr_in serv_addr;
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr(argv[2]);
serv_addr.sin_port = htons(atoi(argv[3])); // I tried both htons and htonl
if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0) {
error("Failed to connect");
}
int n;
char buf[256];
int fd = open(argv[4], O_RDONLY);
while((n = read(sockfd, buf, 256)) > 0) {
printf("Received: %s\n", buf);
write(fd, buf, n);
}
close(fd);
close(sockfd);
return 0;
}
GDB Output:
(gdb) r
Starting program: [...]/client 0 127.0.0.1 4100 bigfile stats
Breakpoint 1, main (argc=6, argv=0x7fffffffde68) at client.cpp:31
31 if (connect(sockfd,(struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
(gdb) p serv_addr
$1 = {sin_family = 2, sin_port = 0, sin_addr = {s_addr = 16777343},
sin_zero = "\000\000\000\000\000\000\000"}
{sin_family = 2, sin_port = 0, sin_addr = {s_addr = 16777343}, sin_zero = "\000\000\000\000\000\000\000"}
So sin_port is wrong: it shouldn't be zero. The code that sets it is:
serv_addr.sin_port = htonl(atoi(argv[3]));
The problem is here. It should be
serv_addr.sin_port = htons(atoi(argv[3]));
Not today's problem but
printf("Received: %s\n", buf);
should be
printf("Received: %.*s\n", n, buf);

Connecting to a UDP Tracker in C++

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;

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