The way my code is currently written only allows a message from the server to be read directly after input is taken and a message is sent. However, this code is for a chat server and must allow a read to occur at any time a message is sent.
#include <iostream>
#include <string>
#include <cstring>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#define SERVER_ADDRESS "127.0.0.1"
constexpr int server_port = 15555;
#define SERVER_SUCCESS "1"
#define SERVER_FAILURE "-1"
constexpr int msg_buffer_size = 4096;
int main(int argc, char *argv[])
{
struct sockaddr_in serv_addr;
int sock;
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
std::cerr << "Socket creation failed!" << std::endl;
return 1;
}
memset(&serv_addr, '0', sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(server_port);
if (inet_pton(AF_INET, SERVER_ADDRESS, &serv_addr.sin_addr) <= 0)
{
std::cerr << "Invalid server address!" << std::endl;
return 1;
}
if (connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0)
{
std::cerr << "Failed to connect to chat server!" << std::endl;
return 1;
}
int valread;
while (true)
{
std::cout << ">> ";
char msg[msg_buffer_size];
char return_msg[msg_buffer_size];
std::string input;
std::getline(std::cin, input);
if (input == "quit")
return 0;
if (input.length() > 4000)
{
std::cout << "Input length must be less than 4000 characters." << std::endl;
continue;
}
strcpy(msg, input.c_str());
if (send(sock, msg, strlen(msg), 0) < 0)
{
std::cout << "Error sending data." << std::endl;
continue;
}
if (recv(sock, return_msg, msg_buffer_size, 0) < 0)
{
std::cout << "Error receiving data." << std::endl;
continue;
}
std::string code(strtok(return_msg, " "));
if (code == SERVER_FAILURE)
std::cout << "Failure: " << strtok(NULL, "") << std::endl;
else
std::cout << strtok(NULL, "") << std::endl;
memset(msg, 0, msg_buffer_size);
memset(return_msg, 0, msg_buffer_size);
}
std::cout << "Exiting." << std::endl;
close(sock);
return 0;
}
What would be a correct way to allow the client to receive a message as soon as one is sent from the server? I was thinking about making a thread, but it seemed kind of redundant since I would be receiving in two places.
Related
I'm using ubuntu 20.04 now. I have text file in client document. The server can listen the port and I can sen the file but when I open it, there is no text.
client code;
#include <iostream>
#include <string>
#include <cstring>
#include <fstream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
const int BUFSIZE = 4096;
int main(int argc, char *argv[]) {
int client_socket;
struct sockaddr_in server_address;
char buffer[BUFSIZE];
// Create a socket
client_socket = socket(AF_INET, SOCK_STREAM, 0);
if (client_socket < 0) {
std::cerr << "Failed to create socket" << std::endl;
return 1;
}
// Set up the server address
memset(&server_address, 0, sizeof(server_address));
server_address.sin_family = AF_INET;
server_address.sin_port = htons(12345);
if (inet_aton("127.0.0.1", &server_address.sin_addr) == 0) {
std::cerr << "Invalid address" << std::endl;
return 1;
}
// Connect to the server
if (connect(client_socket, (struct sockaddr *) &server_address, sizeof(server_address)) < 0) {
std::cerr << "Failed to connect to server" << std::endl;
return 1;
}
std::cout << "Connected to server at " << inet_ntoa(server_address.sin_addr) << ":" << ntohs(server_address.sin_port) << std::endl;
// Send the file
std::ifstream input_file("file_to_send.txt", std::ios::binary); //
int bytes_sent;
while (input_file.read(buffer, BUFSIZE)) {
bytes_sent = send(client_socket, buffer, input_file.gcount(), 0);
if (bytes_sent < 0) {
std::cerr << "Failed to send data" << std::endl;
break;
}
}
input_file.close();
close(client_socket);
std::cout << "File 'file_to_send.txt' sent" << std::endl;
return 0;
}
server code;
#include <iostream>
#include <string>
#include <cstring>
#include <fstream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
const int BUFSIZE = 4096;
int main(int argc, char *argv[]) {
int server_socket, client_socket;
struct sockaddr_in server_address, client_address;
socklen_t client_address_len;
char buffer[BUFSIZE];
// Create a socket
server_socket = socket(AF_INET, SOCK_STREAM, 0);
if (server_socket < 0) {
std::cerr << "Failed to create socket" << std::endl;
return 1;
}
// Set up the server address
memset(&server_address, 0, sizeof(server_address));
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = htonl(INADDR_ANY);
server_address.sin_port = htons(12345);
// Bind the socket to the server address
if (bind(server_socket, (struct sockaddr *) &server_address, sizeof(server_address)) < 0) {
std::cerr << "Failed to bind socket" << std::endl;
return 1;
}
// Listen for incoming connections
if (listen(server_socket, 5) < 0) {
std::cerr << "Failed to listen on socket" << std::endl;
return 1;
}
std::cout << "Listening for incoming connections on port 12345.." << std::endl;
// Accept an incoming connection
client_address_len = sizeof(client_address);
client_socket = accept(server_socket, (struct sockaddr *) &client_address, &client_address_len);
if (client_socket < 0) {
std::cerr << "Failed to accept connection" << std::endl;
return 1;
}
std::cout << "Accepted connection from " << inet_ntoa(client_address.sin_addr) << std::endl;
// Receive the file
std::ofstream output_file("received_file.txt", std::ios::binary);
int bytes_received;
std::cout << "Bytes received: " << bytes_received << std::endl;
while( (bytes_received = recv(client_socket, buffer, BUFSIZE,0)))>0{
std::cout << "Bytses recieved are not null." << std::endl;
output_file.write(buffer , bytes_received); }
output_file.close();
close(client_socket);
close(server_socket);
std::cout << "File received and saved as 'received_file.txt'" << std::endl;
return 0;
}
I added sys/socket.h library but I think there is a problem in line 65. I ran the server and client. On terminaL, I see "Connected to server at" and "File 'file_to_send.txt' sent". And for server, I see "Listening for incoming connections on port","Accepted connection from ", "Bytes received: 0" and "File received and saved as 'received_file.txt'". "Hello world" is written in the text file I'm new to this topic so I don't know what to do properly. How can I handle this? Thank you.
I'm supposed to use get_nprocs_conf() to get the number of execution contexts on my machine. I'm doing this because I am coding a server and client to interact with each other, and the server may only host get_nprocs_conf()-1 clients. Before I add code to my server to wait for an opening, I want to figure out this issue.
I'm running this code on a virtual machine because I'm using Linux and my desktop is Windows, and when I use said code above, my maximum number of clients is 0, meaning that get_nprocs_conf() only returns 1. Is this because I'm using a virtual machine and for some reason it only can use one execution context, or am I misunderstanding and my computer only has one execution context?
Provided below are my server and client programs.
Server Code:
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <iostream>
#include <fstream>
#include <sys/sysinfo.h>
#include <vector>
#include <cstring>
#include <arpa/inet.h>
//#define BUFFER_SIZE 32
//May need to replace -1 with EXIT_FAILURE in all the exits
int main(int argc, char *argv[]) {
if(argc != 2) {
std::cout << "Need domain socket file name" << std::endl;
exit(-1);
}
struct sockaddr_un server;
char buffer[32];
unlink(argv[1]);
int serverSock = socket(AF_UNIX, SOCK_STREAM, 0);
if(serverSock < 0) {
perror("socket");
exit(EXIT_FAILURE);
}
std::clog << "SERVER STARTED" << std::endl;
size_t maxClients = get_nprocs_conf()-1;
std::clog << "\t" << "MAX CLIENTS: " << maxClients << std::endl;
memset(&server, 0, sizeof(server));
server.sun_family = AF_UNIX;
strncpy(server.sun_path, argv[1], sizeof(server.sun_path)-1);
int success = bind(serverSock, (const struct sockaddr *) &server,
sizeof(struct sockaddr_un));
if(success < 0) {
perror("bind");
exit(EXIT_FAILURE);
}
success = listen(serverSock, maxClients);
if(success < 0) {
perror("listen");
exit(EXIT_FAILURE);
}
while(true) {
std::cout << "Waiting for clients" << std::endl;
int clientSock = accept(serverSock, nullptr, nullptr);
if(clientSock < 0) {
perror("accept");
exit(EXIT_FAILURE);
}
std::clog << "CLIENT CONNECTED" << std::endl;
std::string path="";
std::string searchStr="";
char fileBuff[32];
char searchBuff[32];
memset(fileBuff, 0, 32);
success = read(clientSock, fileBuff, 32);
if(success < 0) {
perror("read");
exit(EXIT_FAILURE);
}
path = fileBuff;
std::cout << path << std::endl;
if(path.empty()) {
std::cout << "No path to file given" << std::endl;
exit(1);
}
memset(searchBuff, 0, 32);
success = read(clientSock, searchBuff, 32);
if(success < 0) {
perror("read");
exit(EXIT_FAILURE);
}
searchStr = searchBuff;
std::cout << searchStr << std::endl;
if(searchStr.empty()) {
std::cout << "No search string given" << std::endl;
exit(1);
}
std::ifstream inFile;
inFile.open(path);
std::string line = "";
int bytesSent = 0;
std::vector<std::string> allLines;
if(inFile.is_open()) {
while(std::getline(inFile, line)) {
if(line.find(searchStr, 0)!=std::string::npos) {
allLines.push_back(line);
//std::cout << line << std::endl;
}
}
}
//Sending over entire length of vector containing all lines to be sent.
long length = htonl(allLines.size());
success = write(clientSock, &length, sizeof(length));
if(success < 0) {
perror("write");
exit(EXIT_FAILURE);
}
for(int b=0; b<allLines.size(); b++) {
length = htonl(allLines[b].length());
success = write(clientSock, &length, sizeof(length));
if(success < 0) {
perror("write");
exit(EXIT_FAILURE);
}
success = write(clientSock, allLines[b].data(), allLines[b].length());
if(success < 0) {
perror("write");
exit(EXIT_FAILURE);
}
bytesSent += allLines[b].length();
}
//char end[] = {'\n'};
//write(clientSock, end, sizeof(char));
std::cout << "BYTES SENT: " << bytesSent << std::endl;
inFile.close();
close(clientSock);
}
//return 0;
}
Client Code:
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <sys/socket.h>
#include <unistd.h>
#include <iostream>
#include <fstream> //Don't think I'll need this
#include <errno.h>
#include <sys/un.h>
#include <sstream>
#include <arpa/inet.h>
#include <vector>
int main(int argc, char *argv[]){
if(argc != 4) {
std::cout << "Need domain socket file name, file path and name, " <<
"and search string" << std::endl;
exit(-1);
}
struct sockaddr_un client;
char buffer[64]; //Prof had 64 for client may need to change to 32 to match
int clientSock = socket(AF_UNIX, SOCK_STREAM, 0);
if(clientSock < 0) {
perror("socket");
exit(EXIT_FAILURE);
}
std::cout << "socket connected" << std::endl;
memset(&client, 0, sizeof(struct sockaddr_un));
client.sun_family = AF_UNIX;
strncpy(client.sun_path, argv[1], sizeof(client.sun_path)-1);
int connectClient = connect(clientSock, (const struct sockaddr *)&client,
sizeof(struct sockaddr_un));
if(connectClient < 0) {
fprintf(stderr, "The server is not working.\n");
exit(EXIT_FAILURE);
}
std::cout << "client connected" << std::endl;
//char arg2[] = {*argv[2],'\n'};
std::string path = argv[2];
std::cout << "Path: " << path << std::endl;
connectClient = write(clientSock, argv[2], path.length());
if(connectClient < 0) {
perror("write");
exit(EXIT_FAILURE);
}
std::string search = argv[3];
std::cout << "Search String: " << search << std::endl;
connectClient = write(clientSock, argv[3], search.length());
if(connectClient < 0) {
perror("write");
exit(EXIT_FAILURE);
}
//int servRet;
int lineCount=0;
int bytes_received=0;
//std::string line = "";
char length[sizeof(int)];
//std::string leng = "";
int num=0;
std::stringstream ss;
std::vector<std::string> allLines;
long size = 0;
read(clientSock, &size, sizeof(size));
size = ntohl(size);
for(int a=0; a<size; ++a) {
long length = 0;
std::string line = "";
connectClient = read(clientSock, &length, sizeof(length));
length = ntohl(length);
while(0 < length) {
char buffer[1024];
connectClient = read(clientSock, buffer, std::min<unsigned long>(sizeof(buffer),length));
line.append(buffer, connectClient);
length-=connectClient;
}
allLines.push_back(line);
lineCount++;
std::cout << lineCount << "\t" << line << std::endl;
bytes_received += line.length();
}
std::cout << "BYTES RECEIVED: " << bytes_received << std::endl;
close(clientSock);
return 0;
}
Right now everything in the server and client work as they should. I'm just hesitating to add code that waits for an execution context to open for another client to be read because it seems like it would never accept any clients since the sole execution context is being used by the server. Any clarification on my issue or on if I'm just using get_nprocs_conf() incorrectly would be greatly appreciated.
I have a client and server set up to talk to each other. But every time I try to echo back to the client the socket seems to have disconnected. Much of the code is adapted from a sockets tutorial over at yolinux. Also, I'm running this remotely over ssh.
Client:
#include <cerrno>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
#include <netdb.h>
#include <sys/select.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <iostream>
#include <cstdlib>
#include <stdlib.h>
#include <strings.h>
#include <string.h>
#include <string>
#include <sstream>
using namespace std;
main(int argc, char *argv[])
{
if (argc != 3) {
cout << "exiting\n";
exit(EXIT_FAILURE);
}
struct sockaddr_in remoteSocketInfo;
struct hostent *hPtr;
int socketHandle;
char *remoteHost = argv[1];
int portNumber = atoi(argv[2]);
cout << "Welcome!\n";
// create socket
if ((socketHandle = socket(AF_INET, SOCK_STREAM, IPPROTO_IP)) < 0)
{
cout << "Socket creation failed.\n";
close(socketHandle);
exit(EXIT_FAILURE);
}
cout << "Socket created!\n";
bzero(&remoteSocketInfo, sizeof(sockaddr_in)); // Clear structure memory
if ((hPtr = gethostbyname(remoteHost)) == NULL)
{
cerr << "System DN name resolution not configured properly.\n";
cerr << "Error number: " << ECONNREFUSED << endl;
exit(EXIT_FAILURE);
}
// Load system information for remote socket server into socket data structures
memcpy((char*)&remoteSocketInfo.sin_addr, hPtr->h_addr, hPtr->h_length);
remoteSocketInfo.sin_family = AF_INET;
remoteSocketInfo.sin_port = htons((u_short)portNumber); // set port number
if (connect(socketHandle, (struct sockaddr *)&remoteSocketInfo, sizeof(sockaddr_in)) < 0) {
cout << "connection failed\n";
close(socketHandle);
exit(EXIT_FAILURE);
}
cout << "Connected!\n";
string input;
int message;
while (1) {
cout << "Please indicate rotation amount:";
cin >> input;
if (input == "exit") {
close(socketHandle);
break;
}
char buf[input.length()+1];
const char *conv_input = input.c_str();
strcpy(buf, conv_input);
int bytes_sent = 0;
if ( (bytes_sent = send(socketHandle, buf, strlen(buf)+1, 0)) < 0) {
char buffer[256];
char * errorMessage = strerror_r( errno, buffer, 256);
cout << errorMessage << endl;
close(socketHandle);
exit(EXIT_FAILURE);
}
cout << "bytes sent: " << bytes_sent << endl;
int rc;
char buf2[input.length()+1];
rc = recv(socketHandle, buf2, strlen(buf)+1, 0);
buf[rc] = (char)NULL; // Null terminate string
cout << "received: " << buf2 << endl;
cout << "bytes received: " << rc << endl;
}
close(socketHandle);
}
Server:
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdlib.h>
#include <strings.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <cstring>
#define MAXHOSTNAME 256
using namespace std;
main(int argc, char *argv[])
{
if (argc != 2) {
cout << "not enough arguments, ex: ./CaesarCipherServer 9876\n";
exit(EXIT_FAILURE);
}
struct sockaddr_in socketInfo;
char sysHost[MAXHOSTNAME+1]; // Hostname of this computer we're running on
struct hostent *hPtr;
int portNumber = atoi(argv[1]);
int sock;
bzero(&socketInfo, sizeof(sockaddr_in)); // Clear structure memory
// Get system information
gethostname(sysHost, MAXHOSTNAME); // Get this computer's hostname
if ((hPtr = gethostbyname(sysHost)) == NULL)
{
cerr << "System hostname misconfigured." << endl;
exit(EXIT_FAILURE);
}
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
close(sock);
exit(EXIT_FAILURE);
}
// Load system info into socket data structures
socketInfo.sin_family = AF_INET;
socketInfo.sin_addr.s_addr = htonl(INADDR_ANY); // Use any addr available
socketInfo.sin_port = htons(portNumber); // Set port number
// Bind the socket to a local socket address
if (bind(sock, (struct sockaddr *) &socketInfo, sizeof(socketInfo)) < 0)
{
close(sock);
perror("bind");
exit(EXIT_FAILURE);
}
cout << "listening for initial connection \n";
listen(sock, 1);
int sockConn;
if ((sockConn = accept(sock, NULL, NULL)) < 0)
{
exit(EXIT_FAILURE);
} else {
cout << "connection accepted!\n";
}
int rc = 0;
char buf[512];
cout << "about to receive message... \n";
// rc is number of chars returned
rc = recv(sockConn, buf, 512, 0);
buf[rc] = (char)NULL; // Null terminate string
cout << "received: " << buf << endl;
cout << "rc: " << rc << endl;
int bytes_sent;
if ((bytes_sent = send(sock, buf, rc, MSG_NOSIGNAL)) < 0) {
cout << "error sending\n";
close(sock);
exit(EXIT_FAILURE);
}
cout << "bytes sent: " << bytes_sent << endl;
close(sock);
}
Client Output:
./CaesarCipherClient cs-ssh 9876
Welcome!
Socket created!
socket handle : 3
Connected!
Please indicate rotation amount:5
bytes sent: 2
received:
bytes received: 0
Please indicate rotation amount:
Server Output:
./CaesarCipherServer 9876
listening for initial connection
connection accepted!
about to receive message...
received: 5
rc: 2
error sending
If the MSG_NOSIGNAL flag isn't specified, the server crashes at send(), which means the socket has disconnected at the other end. Why would the socket consistently disconnect after a send()/recv() pair?
I apologize for any poor readability/style/pure stupidity in my submission.
Thank you for your help!
In your server, you are using:
if ((bytes_sent = send(sock, buf, rc, MSG_NOSIGNAL)) < 0) {
cout << "error sending\n";
close(sock);
exit(EXIT_FAILURE);
}
Here, sock is the listening socket, not the accepted client socket. You need to replace sock with sockCon instead (which you are using in your recv() function call, and that is working).
// In server.cpp after connection has established
std::string input;
input.reserve(5);
std::cout << "Enter message to send: ";
std::cin.ignore(); // =====(1)=====
std::getline(std::cin, input);
std::cout << "Sending..." << std::endl;
auto len = input.length();
auto bytes_sent = send(newFD, input.data(), len, 0); // =====(2)=====
std::cout << "Input length : " << input.length() << std::endl
<< "Input bytes sent : " << bytes_sent << std::endl;
My aim is to use std::string instead of plain old char[fixed] in simple tcp client server program. So in server.cpp I have 2 doubts. So far my initial guesses are working as expected. I've marked them above in code.
cin.ignore() vs cin.clear() + cin.sync()
std::string.data() vs std::string.c_str()
Which should I use? I'm not even sure of the difference b/w any of these and I don't know if they're contributing to my problem.
// In client.cpp
std::string message;
message.reserve(5);
auto len = message.capacity();
auto bytes_recv = recv(sockFD, &message.front(), len - 1, 0); // =====(1)=====
message[len] = 0; // =====(2)=====
close(sockFD);
freeaddrinfo(res);
std::cout << "Bytes recieved :" << bytes_recv << std::endl;
std::cout << message.c_str() << std::endl; // =====(3)=====
And in client.cpp, everything goes wrong when I try to send bigger strings. But I probably know the cause, however solution is somewhat tricky to implement.
Am I doing right thing to pass &std::string.front() to write incoming data?
This is wrong, string class should manage this, right? However since I'm directly writing to &front(), I guess length won't get updated or I'm not sure what happens but data surely gets lost when outputting with std::cout << message;.
I'm doing this just because I'm directly writing to &front, still it produces garbage if somehow data returned is smaller than total length, probably because it cannot find terminating character at right place?
server.cpp
// compile as 'g++ server.cpp -o server.app -std=c++14'
// run as : './server.app 8080'
#include <iostream>
#include <string>
#include <cstring>
extern "C" {
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
}
int main(int argc, char *argv[])
{
if(argc != 2) {
std::cerr << "Run program as 'program port'" << std::endl;
return -1;
}
auto &portNum = argv[1];
const unsigned int backLog = 5;
struct addrinfo hints, *res, *p;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
int gAddRes = getaddrinfo(NULL, portNum, &hints, &res);
if(gAddRes != 0) {
std::cerr << gai_strerror(gAddRes) << std::endl;
return -2;
}
std::cout << "Detecting addresses" << std::endl;
unsigned int numOfAddr = 0;
char ipStr[INET6_ADDRSTRLEN];
for(p = res; p != NULL; p = p->ai_next) {
void *addr;
std::string ipVer = "IPv0";
if(p->ai_family == AF_INET) {
ipVer = "IPv4";
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
addr = &(ipv4->sin_addr);
++numOfAddr;
}
else {
ipVer = "IPv6";
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
addr = &(ipv6->sin6_addr);
++numOfAddr;
}
inet_ntop(p->ai_family, addr, ipStr, sizeof(ipStr));
std::cout << "(" << numOfAddr << ") " << ipVer << " : " << ipStr
<< std::endl;
}
if(!numOfAddr) {
std::cerr << "Found no host address to use" << std::endl;
return -3;
}
std::cout << "Enter the number of host address to bind with:" << std::endl;
unsigned int choice = 0;
bool madeChoice = false;
do {
std::cin >> choice;
if(choice > (numOfAddr + 1) || choice < 1) {
madeChoice = false;
std::cout << "Wrong choice, try again!" << std::endl;
}
else
madeChoice = true;
} while(!madeChoice);
p = res;
bool isIPv4 = true;
if(choice > 1) {
unsigned int temp = 1;
while(choice < temp) {
p = p->ai_next;
++temp;
}
if(p->ai_family == AF_INET) {
isIPv4 = true;
}
else
isIPv4 = false;
}
int sockFD = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if(sockFD == -1) {
std::cerr << "Error while creating socket" << std::endl;
freeaddrinfo(res);
return -4;
}
int bindR = bind(sockFD, p->ai_addr, p->ai_addrlen);
if(bindR == -1) {
std::cerr << "Error while binding socket" << std::endl;
close(sockFD);
freeaddrinfo(res);
return -5;
}
int listenR = listen(sockFD, backLog);
if(listenR == -1) {
std::cerr << "Error while Listening on socket" << std::endl;
close(sockFD);
freeaddrinfo(res);
return -6;
}
struct sockaddr_storage client_addr;
socklen_t client_addr_size = sizeof(client_addr);
int newFD =
accept(sockFD, (struct sockaddr *)&client_addr, &client_addr_size);
if(newFD == -1) {
std::cerr << "Error while Accepting on socket" << std::endl;
close(sockFD);
freeaddrinfo(res);
return -7;
}
std::string input;
input.reserve(5);
std::cout << "Enter message to send: ";
std::cin.ignore();
std::getline(std::cin, input);
std::cout << "Sending..." << std::endl;
auto len = input.length();
auto bytes_sent = send(newFD, input.data(), len, 0);
std::cout << "Input length : " << input.length() << std::endl
<< "Input bytes sent : " << bytes_sent << std::endl;
close(newFD);
close(sockFD);
freeaddrinfo(res);
return 0;
}
client.cpp
// compile as 'g++ client.cpp -o client.app -std=c++14'
// run as : './client.app 0 8080'
#include <iostream>
#include <cstring>
extern "C" {
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
}
int main(int argc, char *argv[])
{
if(argc != 3) {
std::cerr << "Run program as 'program ipaddress port'" << std::endl;
return -1;
}
auto &ipAddress = argv[1];
auto &portNum = argv[2];
struct addrinfo hints, *res, *p;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
int gAddRes = getaddrinfo(ipAddress, portNum, &hints, &res);
if(gAddRes != 0) {
std::cerr << gai_strerror(gAddRes) << std::endl;
return -2;
}
std::cout << "Detecting addresses" << std::endl;
unsigned int numOfAddr = 0;
char ipStr[INET6_ADDRSTRLEN];
for(p = res; p != NULL; p = p->ai_next) {
void *addr;
std::string ipVer = "IPv0";
if(p->ai_family == AF_INET) {
ipVer = "IPv4";
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
addr = &(ipv4->sin_addr);
++numOfAddr;
}
else {
ipVer = "IPv6";
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
addr = &(ipv6->sin6_addr);
++numOfAddr;
}
inet_ntop(p->ai_family, addr, ipStr, sizeof(ipStr));
std::cout << "(" << numOfAddr << ") " << ipVer << " : " << ipStr
<< std::endl;
}
if(!numOfAddr) {
std::cerr << "Found no host address to use" << std::endl;
return -3;
}
std::cout << "Enter the number of host address to bind with:" << std::endl;
unsigned int choice = 0;
bool madeChoice = false;
do {
std::cin >> choice;
if(choice > (numOfAddr + 1) || choice < 1) {
madeChoice = false;
std::cout << "Wrong choice, try again!" << std::endl;
}
else
madeChoice = true;
} while(!madeChoice);
p = res;
bool isIPv4 = true;
if(choice > 1) {
unsigned int temp = 1;
while(choice < temp) {
p = p->ai_next;
++temp;
}
if(p->ai_family == AF_INET) {
isIPv4 = true;
}
else
isIPv4 = false;
}
int sockFD = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if(sockFD == -1) {
std::cerr << "Error while creating socket" << std::endl;
return -4;
}
int connectR = connect(sockFD, p->ai_addr, p->ai_addrlen);
if(connectR == -1) {
close(sockFD);
std::cerr << "Error while connecting socket" << std::endl;
return -5;
}
std::string message;
message.reserve(5);
auto len = message.capacity();
auto bytes_recv = recv(sockFD, &message.front(), len - 1, 0);
message[len] = 0;
close(sockFD);
freeaddrinfo(res);
std::cout << "Bytes recieved :" << bytes_recv << std::endl;
std::cout << message.c_str() << std::endl;
return 0;
}
Your question on this is too vague to provide a helpful answer.
data() and c_str() are effectively the same thing since C++11. It doesn't matter which one you use. EDIT: In C++17, data() will have a non-const overload that returns a non-const char*, so you will not need to do &message.front() to access a modifiable form of the underlying buffer. c_str() will remain const.
&message.front() is right... and wrong. That is the way to get a non-const char* to the contents of your std::string. BUT message is uninitialized and has a size() of 0 at that point in the code, so I'm not even sure that line of code is well-defined behavior. Rather than doing a reserve(5) I would construct your string like this: auto message = std::string(5, ' '); Then when you pass it into recv there will actually be valid stuff there for it to overwrite and you'll be able to read it from message afterwards.
Yes, this is wrong. You should be setting your string up to be the actual size you need. I suspect you can just pass in len instead of len - 1 if you do this. On this topic, are you certain everything you'll ever receive is only 4 bytes long? Or are you intentionally only reading 4 bytes at a time?
a) you don't need to pass c_str() to std::cout. << is overloaded to accept std::string as well. b) recv returns the number of bytes that you received. If that value is less than the size you initialized your message to, then the remaining characters in your string will be garbage (or ' ' chars if you followed my advice re:#3). I would do message.resize(bytes_recv); after receiving the message.
Your questions have been addressed by caps,, but my 2 cents. What about having your own send/recv functions and hiding the complexity?
For example along the lines of:
ssize_t recv(int sockfd, std::string &buf, size_t len, int flags) {
buf.resize(len); // current status unknown -> make it fit
ssize_t n = ::recv(sockfd, (void *)buf.data(), len, flags);
buf.resize(n >= 0 ? n : 0); // take error into account
return n;
}
I'm building a ping function that, based on a destination and maximum number of hops, tries to reach the destination and report the stats of the response.
However I seem to have some difficulties decoding the received packet and extracting ttl, icmp-type and icmp-code and identification of the inner ip-packet. I'd also like to be able to set the identification of the transmitted packet, read the source port of the inner udp-packet or other means to identify packets for this function.
TL;DR
I'm struggeling and need help with:
Extracting ttl, icmp-type and icmp-code from received packet;
Extracting identification from received inner packet;
Setting identification of transmitted packet, read the source port of the inner udp-packet or other means to identify packets for this function.
Source:
#define SOCKET_ERROR -1
#include <cstdlib> //EXIT_SUCCES & EXIT_FAILURE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/ip.h> //iphdr
#include <netinet/ip_icmp.h> //icmphdr
#include <netinet/in.h> //sockaddr, sockaddr_in
#include <sys/socket.h>
#include <unistd.h>
//Additional network headers (taken from boost 1.53.0)
#include "../includes/icmp_header.hpp"
#include "../includes/ipv4_header.hpp"
int perform_ping(const char *destination, int max_hops) {
int msg_size = 32;
int transmission_socket;
if ((transmission_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == SOCKET_ERROR) {
printf("Failed to setup transmission socket.\n");
return EXIT_FAILURE;
}
int receiver_socket;
if ((receiver_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP)) == SOCKET_ERROR) { //Non-Priviledged
printf("Failed to setup receiver socket.\n");
return EXIT_FAILURE;
}
struct timeval timeout;
timeout.tv_sec = 3;
timeout.tv_usec = 0;
if (setsockopt(receiver_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)) == SOCKET_ERROR) {
printf("Failed to setup receiver socket with timeout: %ld.%06d\n", timeout.tv_sec, timeout.tv_usec);
return EXIT_FAILURE;
}
struct sockaddr_in destination_address;
destination_address.sin_addr.s_addr = inet_addr(destination);
destination_address.sin_family = AF_INET;
destination_address.sin_port = htons(33434);
if (setsockopt(transmission_socket, IPPROTO_IP, IP_TTL, (char *)&max_hops, sizeof(max_hops)) == SOCKET_ERROR) {
printf("Failed to setup transmission socket with max_hops: %d\n", max_hops);
return EXIT_FAILURE;
}
char *transmission_buffer = NULL;
transmission_buffer = new char [sizeof (icmp) + msg_size];
int bytes_send = sendto(transmission_socket, transmission_buffer, sizeof(icmp) + msg_size, 0, (struct sockaddr *)&destination_address, sizeof(struct sockaddr_in));
if (bytes_send == SOCKET_ERROR) {
printf("An error has occured while sending: %s.", strerror(errno));
delete transmission_buffer;
return EXIT_FAILURE;
}
char *response_buffer;
if ((response_buffer = (char *)malloc(sizeof(struct ip) + sizeof(struct icmp))) == NULL) {
fprintf(stderr, "Could not allocate memory for packet\n");
return EXIT_FAILURE;
}
struct sockaddr remoteAddr;
socklen_t remoteAddrLen = sizeof(remoteAddr);
int bytes_received = recvfrom(receiver_socket, response_buffer, sizeof(ip) + sizeof(icmp), 0, &remoteAddr, &remoteAddrLen);
if (bytes_received != SOCKET_ERROR) {
printf("%s (%d)\n", inet_ntoa(((struct sockaddr_in *)&remoteAddr)->sin_addr), max_hops);
struct ip *ipheader = (struct ip *)response_buffer;
struct icmp *icmpheader = (struct icmp *)(response_buffer + sizeof(struct ip));
std::cout << "ip_ttl : " << std::dec << ipheader->ip_ttl << std::endl;
std::cout << "ip_ttl(hex): " << std::hex << ipheader->ip_ttl << std::endl;
std::cout << "ip_id : " << std::dec << ntohs(ipheader->ip_id) << std::endl;
std::cout << "ip_id(hex): " << std::hex << ntohs(ipheader->ip_id) << std::endl;
std::cout << "icmp_id : " << std::dec << ntohs(icmpheader->icmp_hun.ih_idseq.icd_id) << std::endl;
std::cout << "icmp_id(hex): " << std::hex << icmpheader->icmp_hun.ih_idseq.icd_id << std::endl;
std::cout << "icmp_type : " << std::dec << icmpheader->icmp_type << std::endl;
std::cout << "icmp_type(hex): " << std::hex << icmpheader->icmp_type << std::endl;
std::cout << "icmp_code : " << std::dec << icmpheader->icmp_code << std::endl;
std::cout << "icmp_code(hex): " << std::hex << icmpheader->icmp_code << std::endl;
} else {
printf("Socket error\n");
free(response_buffer);
return EXIT_FAILURE;
}
delete transmission_buffer;
free(response_buffer);
return EXIT_SUCCESS;
}
Received packet
ip (
identification: 0xe0ab (57515),
time to live: 250
),
icmp (
type: 11
code: 0
ip:(
identification: 0x7ec7 (32455),
time to live: 1
),
udp:(
source port: 56086
)
)
Expected output
90.145.29.174 (5)
ip_ttl : 250
ip_id : 32455
ip_id(hex): 7ec7
icmp_type: 11
icmp_code: 0
Actual output
90.145.29.174 (5)
ip_ttl : ? (actual question mark)
ip_ttl(hex): ? (action question mark)
ip_id : 57515
ip_id(hex): e0ab
icmp_type :
(newline)
icmp_type(hex):
(newline)
icmp_code :
icmp_code(hex):
PS: This is my first post, please forgive any mistakes :)