I think I am missing something and doesn't make sense.
I am writing pretty simple TCP server, everything works pretty much as expected, but when a message saying "500 LOGIN FAILED" gets send over network, it get interpreted as "$500 LOGIN FAILED".
I am testing my server using telnet on localhost
here is simplified version of my code
recv(c_sockfd, buf, BUFFSIZE, 0))
inBuffer.push_back(buf);
auto messageToSend = checkResponse(parseBuffer(inBuffer.back()));
//get the second thing in the tuple
outBuffer.push_back(std::get<1>(messageToSend));
bzero(buf, sizeof(buf));
send(c_sockfd, &outBuffer.back(), sizeof( outBuffer.back() ), 0)
in the checkResponse func, I am implementing logic to decide what message to send, and somehow when I send ERROR message the extra character is added at the beginning of the message.
EXAMPLE 1:
Connected to localhost.
Escape character is '^]'.
200 LOGIN
Robot345\r\n
201 PASSWORD
674\r\n
202 OK
INFO iasdijasdjiajsdiajdijasidjiansdjsdvhdf dfvsdfsdf\r\n
&501 SYNTAX ERROR
Notice the "&" character
EXAMPLE 2:
Connected to localhost.
Escape character is '^]'.
200 LOGIN
Robot345\r\n
201 PASSWORD
456\r\n
$500 LOGIN FAILED
Notice the "$" character
Does anyone have any idea where the extra characters could be added to the string?
I didn't want to include full code, because the requirement was to have all in one file, which makes it difficult to read. Here it goes tho.
FULL CODE:
#include <iostream>
#include <regex>
#include <iterator>
#include <vector>
#include <sstream>
#include <string>
#include <stdio.h>
#include <string.h>
#include <string.h>
#include <netdb.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <tuple>
#define MIN_PORT 3000
#define MAX_PORT 3999
#define BUFFSIZE 1000
/**
0 - LOGIN SUCCESSFUL, USERNAME IS IN THE BUFFER
1 - PASSWORD CHECK
2 - PASSWORD OK, COMMUNICATING
*/
int state = 0;
std::string username, password;
/**
CHECKS ENTERED PASSWORD BASES ON THE SUM OF ASCII VALUES OF USERNAME
#return: true on success, false otherwise
*/
bool checkPassword(std::string password){
std::istringstream sst;
sst.str(username);
unsigned char byte = '\0';
int value = 0;
// std::cout << "byte poprve: " << byte << std::endl;
// std::cout << "byte poprve INT: " << (int) byte << std::endl;
while (sst >> byte) {
std::cout << "podruhe: " << byte << std::endl;
std::cout << "podruhe INT: " << (int) byte << std::endl;
std::cout << "Prubezna SUMA: " << (int) value << std::endl;
value += byte;
}
std::cout << "suma: " << value << std::endl;
// Check the entered password
if (password == std::to_string(value)) {
return true;
}
return false;
}
/**
CHECKS MESSAGE SYNTAX BASED ON THE STATE WE ARE IN
CHECKS PASSWORD
CHECKS CHECK SUM
#param response <string type (if available), string message to parse>
#return TRUE on success, FALSE otherwise
*/
bool checkMessage(std::tuple<std::string,std::string> response){
auto messageToParse = std::get<1>(response);
std::string delimeter = "\r\n";
std::string::size_type pos = messageToParse.find(delimeter);
//INITIAL CHECK
if (pos < 1){
return false;
}
//somehow you have to multiply the length by 2
auto parsedMessage = messageToParse.substr(0,pos - 2*delimeter.length());
std::cout << parsedMessage << " : THIS IS YOUR PARSED MESSAGE";
//USERNAME
if (state == 0) {
username = parsedMessage;
return true;
}
//PASSWORD CHECK
if (state == 1 && checkPassword(parsedMessage)) {
password = parsedMessage;
return true;
}
if (state == 2) {
std::string type = std::get<0>(response);
//INFO
if( type == "I" ){
return true;
}
//PHOTO
if ( type == "F") {
return true;
}
}
return false;
}
/**
THIS FUNC WILL CHECK RESPONSE FROM THE ROBOT, AND DECIDE WHAT TO DO BASED ON THE STATE
#return tuple<bool TRUE if everything is right,std::string MESSAGE to send to the robot>
*/
std::tuple<bool,std::string> checkResponse(std::tuple<std::string, std::string> response){
if (state == 0) {
if (checkMessage(response)) {
std::cout << state << " / / state" << std::endl;
return std::make_tuple(true, "201 PASSWORD\r\n");
}
}
if (state == 1) {
// TADY BUDE JESTE PODMINKA, ZE HESLO JE SPRAVNE
if(checkMessage(response)){
std::cout << state << " / / / state" << std::endl;
return std::make_tuple(true, "202 OK\r\n");
}else{
std::cout << state << " / / / / state" << std::endl;
return std::make_tuple(false, "500 LOGIN FAILED\r\n");
}
}
if (state == 2) {
if (checkMessage(response)) {
std::cout << state << " / / / / / state" << std::endl;
return std::make_tuple(true, "202 OK\r\n");
}else{
std::cout << state << " / / / / / / state" << std::endl;
return std::make_tuple(false, "501 SYNTAX ERROR \r\n");
}
}
std::cout << state << " / / / / / / / / state" << std::endl;
return std::make_tuple(false, "unexpected result");
}
/**
This func will parse the incoming buffer
#param buffer incoming buffer
#return tuple <String type of message (U,I,P,F), String actual message>
*/
std::tuple<std::string, std::string> parseBuffer(std::string buffer){
if (state == 0) {
return std::make_tuple("U", buffer);
}
if (state == 1) {
return std::make_tuple("P", buffer);
}else{
std::string delimeter = " ";
std::string::size_type pos = buffer.find(delimeter);
std::string type = buffer.substr(0, pos );
std::string message = buffer.erase(0, pos + delimeter.length());
return std::make_tuple(type, message);
}
}
int main(int argc, char *argv[])
{
char buf[BUFFSIZE];
std::vector<std::string> outBuffer;
std::vector<std::string> inBuffer;
int sockfd, c_sockfd;
sockaddr_in my_addr, rem_addr;
socklen_t rem_addr_length;
int mlen;
const int PORT_NUM = atoi(argv[1]);
if( (PORT_NUM > MAX_PORT) || (PORT_NUM < MIN_PORT)){
perror("Port number is not acceptable");
exit(-1);
}
if ((sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
{
perror("Socket nelze otevrit");
exit(-1);
}
bzero(&my_addr, sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(PORT_NUM);
std::cout << PORT_NUM << " PORT NUM" << std::endl;
if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(my_addr)) == -1)
{
perror("Chyba v bind");
close(sockfd); exit(1);
}
if (listen(sockfd, SOMAXCONN) == -1)
{
perror("Nelze provest listen");
close(sockfd); exit(1);
}
while (1)
{
rem_addr_length=sizeof(rem_addr);
c_sockfd = accept(sockfd, (struct sockaddr*) &rem_addr, &rem_addr_length);
if ( c_sockfd == -1)
{
perror("Nelze accept");
close(sockfd); exit(1);
}
///FIRST MESSAGE
std::string ok = "200 LOGIN\r\n";
send(c_sockfd, &ok, sizeof(std::string), 0);
if ((mlen = recv(c_sockfd, buf, BUFFSIZE, 0)) == -1)
perror("Chyba pri cteni");
else{
while (mlen)
{
///---------- MAIN PART--------------
//This is where comunication is happening
inBuffer.push_back(buf);
//Parse the buffer, check the message and
auto messageToSend = checkResponse(parseBuffer(inBuffer.back()));
//get the second thing in the tuple
outBuffer.push_back(std::get<1>(messageToSend));
bzero(buf, sizeof(buf));
///---------- MAIN PART--------------
state++;
std::cout << state << " state num" << std::endl;
if (send(c_sockfd, &outBuffer.back(), sizeof( outBuffer.back() ), 0) == -1)
{
perror("Chyba pri zapisu");
break;
}else{
}
std::cout << inBuffer.back() << std::endl;
if ((mlen = recv(c_sockfd, buf, BUFFSIZE, 0)) == -1)
{
perror("Chyba pri cteni");
break;
}
}
close(c_sockfd);
}
}
}
The problem is with this:
std::vector<std::string> outBuffer;
and this:
send(c_sockfd, &outBuffer.back(), sizeof( outBuffer.back() ), 0)
You can't send std::string objects over the network. You must send the string it contains. Those are two very different things.
For a simple fix, do e.g.
send(c_sockfd, outBuffer.back().c_str(), outBuffer.back().length(), 0)
If you want to send the terminating null then add one to the length to send.
For more details, while implementations of std::string is allowed to optimize small strings to be contained inside the actual object, otherwise a std::string object is really nothing more than a size and a pointer to the actual string (implementations might have other members as well).
A pointer is unique to the currently running process on the host system. You can't transfer a pointer over the network. You can't even save a pointer to a file and then load it again and have it working in a new process (even if it's a process from the same program).
By sending the std::string object, all you're really sending is this pointer. So on the receiving side it have no idea what you're really sending and how it should treat that.
Related
I need to close and then reuse the same socket in my app. The first time the socket connects it's able to connect properly, but a second time it's tried to be used, client gets a wsaerror 10054 (existing connection was forcibly closed by the remote host) from the server, and I see that server does not receive the "syn" data from the client. What seems to be wrong here? The client that has connected before is able to connect to a server again, but a server that has received a connection before is unable to accept a new connection as it somehow causes a 10054.
connectionmanager.hpp
#pragma once
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
#include <iostream>
#include <string>
#pragma comment (lib, "Ws2_32.lib")
#define DEFAULT_PORT 27015
#define DEFAULT_BUFFER_LENGTH 64
class ConnectionManager {
private:
fd_set fdset;
struct timeval client_wait_timeout;
struct timeval server_wait_timeout;
SOCKET sock = INVALID_SOCKET;
// This is where we'll be setting up connection parameters or where we'll be storing the parameters for a connection that's made.
SOCKADDR_IN connection_data;
int connection_data_len = sizeof(connection_data);
char receive_buffer[DEFAULT_BUFFER_LENGTH] = { 0 }; // The object where the recieved data will be placed on.
public:
std::wstring server_ipv4;
bool is_connected = false;
std::string type = "none";
ConnectionManager();
void init(std::string connection_type);
void reset();
bool establish_first_connection();
bool await_first_connection();
std::string receive_data();
std::string send_data(std::string data);
};
connectionmanager.cpp
#include "connection_manager.hpp"
ConnectionManager::ConnectionManager() {
WSADATA wsadata;
int result;
// Initialize Windows Sockets library, version 2.2.
result = WSAStartup(MAKEWORD(2, 2), &wsadata);
if (result != 0)
std::cerr << "WSAStartup failed, error: " << result << "\n";
connection_data.sin_family = AF_INET; // Using IPv4
connection_data.sin_port = htons(DEFAULT_PORT);
}
void ConnectionManager::init(std::string connection_type) {
int result = 0;
if (connection_type == "server") {
connection_data.sin_addr.s_addr = INADDR_ANY; // Bind the socket to all available interfaces - or in other words, accept connections from any IPv4 address. We'll change this after we establish our first connection with the client.
// Create a socket for the server to listen from client for data / send data to client.
sock = socket(connection_data.sin_family, SOCK_DGRAM, 0);
if (sock == INVALID_SOCKET) {
std::cerr << "Error occured while creating server socket: " << WSAGetLastError() << "\n";
WSACleanup();
}
// Bind the listening socket.
result = bind(sock, (SOCKADDR*)&connection_data, connection_data_len);
if (result == SOCKET_ERROR) {
std::cerr << "Listening socket bind failed with error: " << WSAGetLastError() << "\n";
closesocket(sock);
WSACleanup();
}
std::cout << "Awaiting connection..." << "\n";
if (!await_first_connection())
std::cerr << "Either no one connnected during the 60 second period, or there was a problem with the server. Last WSA error:" << WSAGetLastError() << "\n";
else {
std::cout << "Connected successfully!" << "\n";
is_connected = true;
}
}
else if (connection_type == "client") {
InetPton(connection_data.sin_family, (PCWSTR)(server_ipv4.c_str()), &connection_data.sin_addr.s_addr); // Set the IP address to connect to on the connection_data structure.
// Create a socket for sending data to server.
sock = socket(connection_data.sin_family, SOCK_DGRAM, IPPROTO_UDP);
if (sock == INVALID_SOCKET) {
std::cerr << "Error occured while creating client socket: " << WSAGetLastError() << "\n";
WSACleanup();
}
std::wcout << "Attempting to connect to " << server_ipv4 << "..." << "\n";
if (!establish_first_connection())
std::cerr << "There was a problem connecting the server. Last WSA error: " << WSAGetLastError() << "\n";
else {
std::wcout << "Successfully connected to " << server_ipv4 << "!" << "\n";
is_connected = true;
}
}
// Put the socket in non-blocking mode.
unsigned long mode = 1;
if (ioctlsocket(sock, FIONBIO, (unsigned long*)&mode) == SOCKET_ERROR) {
std::cerr << "Error while putting the socket into non-blocking mode: " << WSAGetLastError() << "\n";
}
}
void ConnectionManager::reset() {
is_connected = false;
closesocket(sock);
}
/*
Functions "establish_first_connection" and "await_first_connection" do something that's quite similar to the three-way handshake method of a TCP connection.
*/
bool ConnectionManager::establish_first_connection() { // This will be used by the client.
// Set up the file descriptor set.
FD_ZERO(&fdset);
FD_SET(sock, &fdset);
int send_result = INT32_MAX;
std::string syn_message = "SYN";
send_result = sendto(sock, syn_message.c_str(), syn_message.length(), 0, (SOCKADDR*)&connection_data, connection_data_len);
if (send_result == SOCKET_ERROR) {
std::cerr << "Error occured while attempting to send SYN to server: " << WSAGetLastError() << "\n";
}
else {
int result = 0;
int receive_result = 0;
// Set up the timeval struct for the timeout.
// We'll wait for 10 seconds for the server to respond, or else we'll call the connection off.
client_wait_timeout.tv_sec = 10; // seconds
client_wait_timeout.tv_usec = 0; // microseconds
// Wait until the timeout or until we receive data.
result = select(sock, &fdset, NULL, NULL, &client_wait_timeout);
if (result == 0)
std::cout << "Timeout." << "\n"; // todo
else if (result == -1)
std::cerr << "Error occured while awaiting first connection data from server. Last WSA error:" << WSAGetLastError() << "\n";
receive_result = recvfrom(sock, receive_buffer, DEFAULT_BUFFER_LENGTH, 0, (SOCKADDR*)&connection_data, &connection_data_len);
if (receive_result > 0) { // If we received any data before the timeout, return true.
std::string client_ack_message = "ACK";
std::cout << receive_buffer << "\n";
sendto(sock, client_ack_message.c_str(), client_ack_message.length(), 0, (SOCKADDR*)&connection_data, connection_data_len);
return true;
}
}
return false;
}
bool ConnectionManager::await_first_connection() { // This will be used by the server.
int result = 0;
int receive_result = 0;
int send_result = 0;
// Set up the file descriptor set.
FD_ZERO(&fdset);
FD_SET(sock, &fdset);
// Set up the timeval struct for the timeout.
// We'll wait for 60 seconds for someone to connect and if someone doesn't connect, we'll cancel the server.
server_wait_timeout.tv_sec = 60; // seconds
server_wait_timeout.tv_usec = 0; // microseconds
// Wait until the timeout or until we receive data.
result = select(sock, &fdset, NULL, NULL, &server_wait_timeout);
if (result == 0) {
std::cout << "Timeout." << "\n";
return false;
}
else if (result == -1)
std::cerr << "Error occured while awaiting first connection data from client. Last WSA error: " << WSAGetLastError() << "\n";
receive_result = recvfrom(sock, receive_buffer, DEFAULT_BUFFER_LENGTH, 0, (SOCKADDR*)&connection_data, &connection_data_len); // We set the first connected client as the only suitable connector from now on here.
if (receive_result > 0) { // If we received any data before the timeout, let the client know that we acknowledge their request and return true.
std::string ack_message = "ACK";
send_result = sendto(sock, ack_message.c_str(), ack_message.length(), 0, (SOCKADDR*)&connection_data, connection_data_len); // Let the client know that we received their message.
if (send_result != SOCKET_ERROR)
return true;
}
return false;
}
std::string ConnectionManager::receive_data() {
ZeroMemory(receive_buffer, DEFAULT_BUFFER_LENGTH); // Clean the receive buffer of any possibly remaining data.
int receive_result = 42;
u_long ioctl_result = 123;
while (true) { // When ioctl with FIONREAD results 0, that means there's no datagram pending in the receive queue. We'll use this to grab only the last received package.
receive_result = recvfrom(sock, receive_buffer, DEFAULT_BUFFER_LENGTH, 0, (SOCKADDR*)&connection_data, &connection_data_len);
ioctlsocket(sock, FIONREAD, &ioctl_result);
if (ioctl_result == 0)
break;
}
// Handle errors.
if (receive_result > 0) {
return std::string(receive_buffer, receive_result); // Using the built-in method of casting char to std::string.
}
else if (receive_result == 0)
return "RECEIVEDNOTHING";
else if (receive_result == SOCKET_ERROR)
switch (WSAGetLastError()) {
case WSAEWOULDBLOCK:
return "WOULDBLOCK";
break;
case WSAECONNRESET:
return "CONNRESET";
break;
case NTE_OP_OK:
break;
default:
std::cerr << "Unhandled error while receiving data: " << WSAGetLastError() << "\n";
}
return "NONE";
}
std::string ConnectionManager::send_data(std::string data) {
int send_result = 666;
send_result = sendto(sock, data.c_str(), data.length(), 0, (SOCKADDR*)&connection_data, connection_data_len);
// Handle errors.
if (send_result == SOCKET_ERROR) {
std::cerr << "Error while sending data: " << WSAGetLastError() << "\n";
return std::string("FAIL");
}
else
return std::string("OK");
}
main.cpp
#include <iostream>
#include <string>
#include "connectionmanager.hpp"
int main() {
ConnectionManager connection_manager;
std::string connection_type;
std::cout << "server or client?" << "\n";
std::cin >> connection_type;
if (connection_type == "client") {
std::wstring ipv4_addr;
std::cout << "ip address?" << "\n";
std::wcin >> ipv4_addr;
connection_manager.server_ipv4 = ipv4_addr;
}
connection_manager.type = connection_type;
connection_manager.init(); // this works fine
connection_manager.reset();
connection_manager.init(); // client returns wsaerror 10054, server receives no data
}
I was able to solve this issue by moving the sin_family and sin_port initialization to ConnectionManager::init() from the constructor and by editing the ConnectionManager::reset() to look like this:
void ConnectionManager::reset() {
puts("reset!");
is_connected = false;
closesocket(sock);
sock = INVALID_SOCKET;
memset(&connection_data, 0, sizeof(connection_data)); // Get rid of the data from the previous connection.
memset(&receive_buffer, 0, sizeof(receive_buffer));
}
I need to send a string back to client that includes the cost of vehicle and the vehicle with modifier(carStyling). I want to return a string containing the sline and the cost to the client. Something like;
Your Sedan Offroad will cost $150000.
Paragraph below contains the code necessary.
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <unistd.h>
#include <iostream>
#include <cstring>
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <sstream>
#define MAX_MSG 100
#define LINE_ARRAY_SIZE (MAX_MSG+1)
using namespace std;
int main()
{
int listenSocket, connectSocket, i;
unsigned short int listenPort;
socklen_t clientAddressLength
;
struct sockaddr_in clientAddress, serverAddress;
char line[LINE_ARRAY_SIZE];
cout << "Enter port number to listen on (between 1500 and 65000): ";
cin >> listenPort;
// Create socket for listening for client connection
// requests.
listenSocket = socket(AF_INET, SOCK_STREAM, 0);
if (listenSocket < 0) {
cerr << "cannot create listen socket";
exit(1);
}
// Bind listen socket to listen port. First set various
// fields in the serverAddress structure, then call
// bind().
// htonl() and htons() convert long integers and short
// integers (respectively) from host byte order (on x86
// this is Least Significant Byte first) to network byte
// order (Most Significant Byte first).
serverAddress.sin_family = AF_INET;
serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
serverAddress.sin_port = htons(listenPort);
if (bind(listenSocket,
(struct sockaddr *) &serverAddress,
sizeof(serverAddress)) < 0) {
cerr << "cannot bind socket";
exit(1);
}
// Wait for connections from clients. This is a
// non-blocking call; i.e., it registers this program with
// the system as expecting connections on this socket, and
// then this thread of execution continues on.
listen(listenSocket, 5);
while (1) {
cout << "Waiting for TCP connection on port " << listenPort << " ...\n";
// Accept a connection with a client that is requesting
// one. The accept() call is a blocking call; i.e., this
// thread of execution stops until a connection comes
// in. connectSocket is a new socket that the system
// provides, separate from listenSocket. We *could*
// accept more connections on listenSocket, before
// connectSocket is closed, but this program doesn't do
// that.
clientAddressLength = sizeof(clientAddress);
connectSocket = accept(listenSocket,
(struct sockaddr *) &clientAddress,
&clientAddressLength);
if (connectSocket < 0) {
cerr << "cannot accept connection ";
exit(1);
}
// Show the IP address of the client.
// inet_ntoa() converts an IP address from binary form to the
// standard "numbers and dots" notation.
cout << " connected to " << inet_ntoa(clientAddress.sin_addr);
// Show the client's port number.
// ntohs() converts a short int from network byte order (which is
// Most Significant Byte first) to host byte order (which on x86,
// for example, is Least Significant Byte first).
cout << ":" << ntohs(clientAddress.sin_port) << "\n";
// Read lines from socket, using recv(), storing them in the line
// array. If no messages are currently available, recv() blocks
// until one arrives.
// First set line to all zeroes, so we'll know where the end of
// the string is.
memset(line, 0x0, LINE_ARRAY_SIZE);
while (recv(connectSocket, line, MAX_MSG, 0) > 0) {
cout << " -- " << line << "\n";
// Convert line to upper case.
for (i = 0; line[i] != '\0'; i++)
line[i] = toupper(line[i]);
// creating an object to direct line to a string array
std::string delimiter[2];
int i = 0;
double cost = 0;
std::string carType;
std::string carStyling;
std::string sline;
sline = line;
stringstream ssin(sline);
while (ssin.good() && i < 2){
ssin >> delimiter[i];
++i;
}
for(i = 0; i < 2; i++){
cout << delimiter[i] << endl;
}
if(i==0) {
carType = delimiter[0];
if(carType.compare("SEDAN")==0){
sline = "Your Sedan";
cost = 100000;
std::copy(sline.begin(), sline.end(), line);
line[sline.size()] = '\0';
}
else if(carType.compare("MPV")==0){
sline = "MPV";
cost = 120000;
std::copy(sline.begin(), sline.end(), line);
line[sline.size()] = '\0';
}
else if(carType.compare("SUV")==0){
sline = "SUV";
cost = 140000;
std::copy(sline.begin(), sline.end(), line);
line[sline.size()] = '\0';
}
else if(carType.compare("LUXURY")==0){
sline = "LUXURY";
cost = 180000;
std::copy(sline.begin(), sline.end(), line);
line[sline.size()] = '\0';
}
if(i==2) {
carStyling = delimiter[1];
if(carStyling.compare("SPORTY")==0){
sline += "Sporty";
cost = cost * 1.5;
}
else if(carStyling.compare("OFFROAD")==0){
sline += "Offroad";
cost = cost * 1.3;
}
}
}
// Send converted line back to client.
if (send(connectSocket, line, strlen(line) + 1, 0) < 0)
cerr << "Error: cannot send modified data";
memset(line, 0x0, LINE_ARRAY_SIZE); // set line to all zeroes
}
}
}
The other one here is the code for the client.cc
#include <netdb.h>
#include <netinet/in.h>
#include <unistd.h>
#include <iostream>
#include <cstring>
#include <stdlib.h>
#define MAX_LINE 100
#define LINE_ARRAY_SIZE (MAX_LINE+1)
using namespace std;
int main()
{
int socketDescriptor;
unsigned short int serverPort;
struct sockaddr_in serverAddress;
struct hostent *hostInfo;
char buf[LINE_ARRAY_SIZE], c;
cout << "Enter server host name or IP address: ";
cin.get(buf, MAX_LINE, '\n');
// gethostbyname() takes a host name or ip address in "numbers and
// dots" notation, and returns a pointer to a hostent structure,
// which we'll need later. It's not important for us what this
// structure is actually composed of.
hostInfo = gethostbyname(buf);
if (hostInfo == NULL) {
cout << "problem interpreting host: " << buf << "\n";
exit(1);
}
cout << "Enter server port number: ";
cin >> serverPort;
cin.get(c); // dispose of the newline
// Create a socket. "AF_INET" means it will use the IPv4 protocol.
// "SOCK_STREAM" means it will be a reliable connection (i.e., TCP;
// for UDP use SOCK_DGRAM), and I'm not sure what the 0 for the last
// parameter means, but it seems to work.
socketDescriptor = socket(AF_INET, SOCK_STREAM, 0);
if (socketDescriptor < 0) {
cerr << "cannot create socket\n";
exit(1);
}
// Connect to server. First we have to set some fields in the
// serverAddress structure. The system will assign me an arbitrary
// local port that is not in use.
serverAddress.sin_family = hostInfo->h_addrtype;
memcpy((char *) &serverAddress.sin_addr.s_addr,
hostInfo->h_addr_list[0], hostInfo->h_length);
serverAddress.sin_port = htons(serverPort);
if (connect(socketDescriptor,
(struct sockaddr *) &serverAddress,
sizeof(serverAddress)) < 0) {
cerr << "cannot connect\n";
exit(1);
}
cout << "\nWelcome to Car Customization Server. What are your orders?\n";
cout << ">> Type of vehicle: Sedan, MPV, SUV, Luxury\n";
cout << ">> Type of Styling: Sporty, Offroad\n";
cout << ">> Eg. To order type: MPV Sporty\n";
// Prompt the user for input, then read in the input, up to MAX_LINE
// charactars, and then dispose of the rest of the line, including
// the newline character.
cout << "Enter Order: ";
cin.get(buf, MAX_LINE, '\n');
while (cin.get(c) && c != '\n')
; //Loop does nothing except consume the spare bytes
// Stop when the user inputs a line with just a dot.
while (strcmp(buf, ".")) { //strcmp returns 0 when the two strings
//are the same, so this continues when
//they are different
// Send the line to the server.
if (send(socketDescriptor, buf, strlen(buf) + 1, 0) < 0) {
cerr << "cannot send data ";
close(socketDescriptor); //Note this is just like using files...
exit(1);
}
// Zero out the buffer.
memset(buf, 0x0, LINE_ARRAY_SIZE);
// Read the modified line back from the server.
if (recv(socketDescriptor, buf, MAX_LINE, 0) < 0) {
cerr << "didn't get response from server?";
close(socketDescriptor);
exit(1);
}
cout << "results: " << buf << "\n";
// Prompt the user for input, then read in the input, up to MAX_LINE
// charactars, and then dispose of the rest of the line, including
// the newline character. As above.
cout << "Enter Order: ";
cin.get(buf, MAX_LINE, '\n');
while (cin.get(c) && c != '\n')
; //again, consuming spare bytes
}
close(socketDescriptor);
return 0;
}
So there, if anyone knows how to send back both the string and the cost. Please reply. Thank you.
You can pack your std::string sline into sending buffer by copying bytes:
memcpy(line, sline.c_str(), strlen(sline.c_str()))
send it, and then on the client side unpack it the same way.
edit:
Try code below for you server, is this what you wanted?
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <unistd.h>
#include <iostream>
#include <cstring>
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <sstream>
#define MAX_MSG 100
#define LINE_ARRAY_SIZE (MAX_MSG+1)
using namespace std;
int main()
{
int listenSocket, connectSocket, i;
unsigned short int listenPort;
socklen_t clientAddressLength;
struct sockaddr_in clientAddress, serverAddress;
char line[LINE_ARRAY_SIZE];
cout << "Enter port number to listen on (between 1500 and 65000): ";
cin >> listenPort;
listenSocket = socket(AF_INET, SOCK_STREAM, 0);
if (listenSocket < 0) {
cerr << "cannot create listen socket";
exit(1);
}
serverAddress.sin_family = AF_INET;
serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
serverAddress.sin_port = htons(listenPort);
if (bind(listenSocket, (struct sockaddr *) &serverAddress,
sizeof(serverAddress)) < 0) {
cerr << "cannot bind socket";
exit(1);
}
listen(listenSocket, 5);
while (1) {
cout << "Waiting for TCP connection on port " << listenPort << " ...\n";
clientAddressLength = sizeof(clientAddress);
connectSocket = accept(listenSocket, (struct sockaddr *) &clientAddress,
&clientAddressLength);
if (connectSocket < 0) {
cerr << "cannot accept connection ";
exit(1);
}
cout << " connected to " << inet_ntoa(clientAddress.sin_addr);
cout << ":" << ntohs(clientAddress.sin_port) << "\n";
memset(line, 0x0, LINE_ARRAY_SIZE);
while (recv(connectSocket,line, MAX_MSG, 0) > 0) {
cout << " -- " << line << "\n";
std::string delimiter[2];
int i = 0;
double cost = 0;
std::string carType;
std::string carStyling;
std::string sline;
sline = line;
stringstream ssin(sline);
while (ssin.good() && i < 2){
ssin >> delimiter[i];
++i;
}
sline = "";
for(i = 0; i < 2; i++){
cout << delimiter[i] << endl;
}
sline += "Your ";
carType = delimiter[0];
if(carType.compare("Sedan")==0){
sline += "Sedan";
cost = 100000;
}
else if(carType.compare("MPV")==0){
sline += "MPV";
cost = 120000;
}
else if(carType.compare("SUV")==0){
sline += "SUV";
cost = 140000;
}
else if(carType.compare("Luxury")==0){
sline += "Luxury";
cost = 180000;
}
carStyling = delimiter[1];
if(carStyling.compare("Sporty")==0){
sline += " Sporty ";
cost = cost * 1.5;
}
else if(carStyling.compare("Offroad")==0){
sline += " Offroad ";
cost = cost * 1.3;
}
sline += "will cost ";
std::ostringstream ss;
ss << cost;
sline += ss.str();
sline.copy(line, sline.length());
if (send(connectSocket, line, strlen(line) + 1, 0) < 0)
cerr << "Error: cannot send modified data";
memset(line, 0x0, LINE_ARRAY_SIZE); // set line to all zeroes
}
}
}
I have no idea why send data is 48 bytes 010,0,0..., someone can explain? the problem is buffer for data received, I don't know how big he should be, and even if I receive data, how to make normal time from it?
Here's the code:
#include <iostream>
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define WIN32_MEAN_AND_LEAN
#include <winsock2.h>
#include <windows.h>
#include <time.h>
using namespace std;
class HRException
{
public:
HRException() :
m_pMessage("") {}
virtual ~HRException() {}
HRException(const char *pMessage) :
m_pMessage(pMessage) {}
const char * what() { return m_pMessage; }
private:
const char *m_pMessage;
};
const int REQ_WINSOCK_VER = 2; // Minimum winsock version required
const char DEF_SERVER_NAME[] = "0.pl.pool.ntp.org";
const int SERVER_PORT = 123;
const int TEMP_BUFFER_SIZE = 128;
const char msg[48] = { 010,0,0,0,0,0,0,0,0 };
// IP number typedef for IPv4
typedef unsigned long IPNumber;
IPNumber FindHostIP(const char *pServerName)
{
HOSTENT *pHostent;
// Get hostent structure for hostname:
if (!(pHostent = gethostbyname(pServerName)))
throw HRException("could not resolve hostname.");
// Extract primary IP address from hostent structure:
if (pHostent->h_addr_list && pHostent->h_addr_list[0])
return *reinterpret_cast<IPNumber*>(pHostent->h_addr_list[0]);
return 0;
}
void FillSockAddr(sockaddr_in *pSockAddr, const char *pServerName, int portNumber)
{
// Set family, port and find IP
pSockAddr->sin_family = AF_INET;
pSockAddr->sin_port = htons(portNumber);
pSockAddr->sin_addr.S_un.S_addr = FindHostIP(pServerName);
}
bool RequestHeaders(const char *pServername)
{
SOCKET hSocket = INVALID_SOCKET;
char tempBuffer[TEMP_BUFFER_SIZE];
sockaddr_in sockAddr = { 0 };
bool bSuccess = true;
try
{
// Lookup hostname and fill sockaddr_in structure:
cout << "Looking up hostname " << pServername << "... ";
FillSockAddr(&sockAddr, pServername, SERVER_PORT);
cout << "found.\n";
// Create socket
cout << "Creating socket... ";
if ((hSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET)
throw HRException("could not create socket.");
cout << "created.\n";
// Connect to server
cout << "Attempting to connect to " << inet_ntoa(sockAddr.sin_addr)
<< ":" << SERVER_PORT << "... ";
if (connect(hSocket, reinterpret_cast<sockaddr*>(&sockAddr), sizeof(sockAddr)) != 0)
throw HRException("could not connect.");
cout << "connected.\n";
cout << "Sending request... ";
// send request part 1
if (send(hSocket, msg, sizeof(msg) , 0) == SOCKET_ERROR)
throw HRException("failed to send data.");
cout << "request sent.\n";
cout << "Dumping received data...\n\n";
// Loop to print all data
recv(hSocket, tempBuffer, sizeof(tempBuffer), 0); // <-- the problem
///
//part where we take time out of tempBuffer
///
}
catch (HRException e)
{
cerr << "\nError: " << e.what() << endl;
bSuccess = false;
}
if (hSocket != INVALID_SOCKET)
{
closesocket(hSocket);
}
return bSuccess;
}
int main(int argc, char* argv[])
{
int iRet = 1;
WSADATA wsaData;
cout << "Initializing winsock... ";
if (WSAStartup(MAKEWORD(REQ_WINSOCK_VER, 0), &wsaData) == 0)
{
// Check if major version is at least REQ_WINSOCK_VER
if (LOBYTE(wsaData.wVersion) >= REQ_WINSOCK_VER)
{
cout << "initialized.\n";
// Set default hostname:
const char *pHostname = DEF_SERVER_NAME;
// Set custom hostname if given on the commandline:
if (argc > 1)
pHostname = argv[1];
iRet = !RequestHeaders(pHostname);
}
else
{
cerr << "required version not supported!";
}
cout << "Cleaning up winsock... ";
// Cleanup winsock
if (WSACleanup() != 0)
{
cerr << "cleanup failed!\n";
iRet = 1;
}
cout << "done.\n";
}
else
{
cerr << "startup failed!\n";
}
int x;
cin >> x;
return iRet;
}
Most part of code is from madwizard.org
Ok it works, main part of code:
const char msg[48] = { 010,0,0,0,0,0,0,0,0 };
if (send(hSocket, msg, sizeof(msg) , 0) == SOCKET_ERROR)
throw HRException("failed to send data.");
cout << "request sent.\n";
cout << "Dumping received data...\n\n";
char tempBuffer[1024];
int bytes = recv(hSocket, tempBuffer, sizeof(tempBuffer), 0);
cout << "bytes received: " << bytes << endl;
time_t tmit;
tmit = ntohl(((time_t*)tempBuffer)[4]);
tmit -= 2208988800U;
cout << ctime(&tmit);
No idea why data that we send is
msg[48] = { 010,0,0,0,0,0,0,0,0 };
and why received data contains many numbers? for example if change code to
tmit = ntohl(((time_t*)tempBuffer)[6]);
I will get date 2008y, why?
Guys why so many minuses?, still waiting for an explanation :D
Here's whole code http://pastebin.com/Sv3ERGfV , dont forget to link ws2_32.lib
Similar to my issue when trying to query the time from a self-hostet Windows-NTP-Server with the C++ library NTPClient which uses boost for the network tasks, msg[48] = { 010,0,0,0,0,0,0,0,0 }; configures the ntp.flags.mode. After comparing the network traffic of w32tm /stripchart /computer:10.159.96.65 using Wireshark, flag 27 or 11 seem to be the choices for my usecase:
Comparison of NTP network packages
tmit = ntohl(((time_t*)tempBuffer)[6]); extracts the data from the received package. It looks like
4 yields the reference time (last sync with timeserver I assume),
8 the time when server received request and
10 the transmit time (which should be almost equal).
I am completely new to Winsock and have been trying to write a small HTTP server listening on localhost for educational purposes mainly. Currently the server simply returns a web page to whoever connects to it, without parsing any request.
Logically, I must always be listening for new connections on the listening port (I chose 81 here) after I finish with a client and close the connection, so I've googled quite a bit and found that I should probably be using SO_REUSEADDR for this purpose, but maybe I got it wrong. I am using Firefox as the client.
The first connection always goes without a hitch. However, the second time a client attempts to connect, the accept function doesn't seem to accept the connection. On the other hand, I can see that a connection IS established at that time using a utility that watches local ports (CurrPorts). I've looked for hours for a solution and have tried to make the socket non-blocking, but no luck. What did I do wrong?
#pragma comment(lib,"Ws2_32.lib")
#include <WinSock2.h>
#include <iostream>
#include <thread>
#include <string>
#include <array>
#include <ctime>
#include <winerror.h>
inline std::string getAddress(sockaddr_in* sin)
{
std::string res = std::to_string(sin->sin_addr.S_un.S_un_b.s_b1) + '.' + std::to_string(sin->sin_addr.S_un.S_un_b.s_b2) + '.' + std::to_string(sin->sin_addr.S_un.S_un_b.s_b3) + '.' + std::to_string(sin->sin_addr.S_un.S_un_b.s_b4);
return res;
}
void acceptTCP(SOCKET& origSock)
{
SOCKET tempSock = SOCKET_ERROR;
struct sockaddr* sa = new sockaddr();
int size = sizeof(*sa);
while (tempSock == SOCKET_ERROR)
{
tempSock = accept(origSock, sa, &size);
int err = WSAGetLastError();
if (err != 0 && err != WSAEWOULDBLOCK) std::cout << "\r\n" << err;
}
struct sockaddr_in* sin = (struct sockaddr_in*)sa;
std::cout << "\r\nConnected to " << getAddress(sin) << ":" << htons(sin->sin_port);
origSock = tempSock;
}
int closeSocket(SOCKET socket)
{
shutdown(socket, 2); //I've tried using 0
std::clock_t start = std::clock();
char buf[1];
while ((std::clock() - start) / (double)CLOCKS_PER_SEC < 5)
{
int res = recv(socket, buf, strlen(buf), IPPROTO_TCP);
//std::cout << "\r\n" << res;
bool br = false;
switch (res)
{
case 0: br = true; break; //client closed connection
case -1:
{
int err = WSAGetLastError();
if (err != WSAEWOULDBLOCK && err != WSAEINTR) //client closed connection
{
br = true;
break;
}
else std::cout << "\r\nError on close socket: " << err;
}
default: exit(1); //data is being sent after shutdown request
};
if (br) break;
//if (res == -1) std::cout << ": " << WSAGetLastError();
//else std::cout << ": " << buf;
//Sleep(1000);
}
return closesocket(socket);
}
int main()
{
WSADATA WsaDat;
if (WSAStartup(MAKEWORD(1, 1), &WsaDat) != 0) std::cout << "???";
while (true)
{
SOCKET socket0 = socket(AF_INET, SOCK_STREAM, 0);
if (socket0 == INVALID_SOCKET) std::cout << "Invalid socket!";
struct sockaddr_in saServer;
saServer.sin_family = AF_INET;
saServer.sin_port = htons(81);
saServer.sin_addr.S_un.S_un_b.s_b1 = 127;
saServer.sin_addr.S_un.S_un_b.s_b2 = 0;
saServer.sin_addr.S_un.S_un_b.s_b3 = 0;
saServer.sin_addr.S_un.S_un_b.s_b4 = 1;
int enable = 1;
if (setsockopt(socket0, SOL_SOCKET, SO_REUSEADDR, (const char*)&enable, sizeof(int)) < 0)
std::cout << "setsockopt(SO_REUSEADDR) failed";
u_long iMode = 1;
ioctlsocket(socket0, FIONBIO, &iMode);
if (bind(socket0, (SOCKADDR*)&saServer, sizeof(saServer)) == SOCKET_ERROR) std::cout << "\r\nSocket Error " << WSAGetLastError();
else std::cout << "Socket bound!";
listen(socket0, 1);
std::thread threadConnection(&acceptTCP, std::ref(socket0)); //I use a thread in case I will want to handle more than one connection at a time in the future, but it serves no purpose here
threadConnection.join();
std::string content = "<!DOCTYPE html><html><head><title>test</title></head><body><p>test</p></body></html>";
std::string response = "HTTP/1.1 200 OK\r\nServer: myServer\r\nContent-Type: text/html\r\nConnection: close\r\nContent-Length: " + std::to_string(content.length()) + "\r\n\r\n" + content;
std::cout << "\r\n" << send(socket0, response.c_str(), strlen(response.c_str())*sizeof(char), 0);
Sleep(1000);
std::cout << "\r\n" << closeSocket(socket0);
}
WSACleanup();
}
Here's how your code should work:
Main function:
Open listening socket.
Bind it.
Call listen.
Call accept.
Dispatch a thread to handle the socket we just accepted.
Go to step 4.
Note that the thread never touches the listening socket.
hopefully this will be my last C++ question related to sockets.
I have a .SWF file and it sends a policy file request.
I check if my incoming data char 0 is <, so like this:
if (raw[0] == '<')
Then I send my policy shit:
send(this->s, Env::Policy().c_str(), sizeof(Env::Policy()), 0);
std::cout << "Sent " << Env::Policy().c_str() << std::endl;
running = false;
closesocket(this->s);
break;
break; will stop the while (this->running) loop.
My policy string:
std::string Env::Policy()
{
char c = 0;
return "<?xml version=\"1.0\"?>\r\n<!DOCTYPE cross-domain-policy SYSTEM \"/xml/dtds/cross-domain-policy.dtd\">\r\n<cross-domain-policy>\r\n<allow-access-from domain=\"*\" to-ports=\"1-31111\" />\r\n</cross-domain-policy>" + c;
}
But every time I send it, nothing happens. The socket won't receive a new connection (like in C# socket server). But when I reconnect on the .swf, it will accept a new connection.
What's going on?
My full while(this->running) loop:
while (running)
{
char c[256];
int bits = recv(s, c, sizeof(c), 0);
if (bits > 0)
{
std::string data = c;
std::string raw = data.substr(0, bits);
std::cout << "First char: " << raw[0] << std::endl;
if (raw[0] == '<')
{
send(this->s, Env::Policy().c_str(), sizeof(Env::Policy()), 0);
std::cout << "Sent " << Env::Policy().c_str() << std::endl;
running = false;
closesocket(this->s);
break;
}
int header = Env::B64Decode(raw.substr(3, 2));
switch (header)
{
case 202:
this->msg = new ServerMessage("DA");
this->msg->AddInt32(6);
this->msg->AddInt32(0);
this->msg->AddInt32(1);
this->msg->AddInt32(1);
this->msg->AddInt32(1);
this->msg->AddInt32(3);
this->msg->AddInt32(0);
this->msg->AddInt32(2);
this->msg->AddInt32(1);
this->msg->AddInt32(4);
this->msg->AddInt32(1);
this->msg->AddString("dd-MM-yyyy");
this->msg->AddChar(2);
this->sendData(this->msg->toString());
this->msg = new ServerMessage("#H");
this->msg->AddString("[100,105,110,115,120,125,130,135,140,145,150,155,160,165,170,175,176,177,178,180,185,190,195,200,205,206,207,210,215,220,225,230,235,240,245,250,255,260,265,266,267,270,275,280,281,285,290,295,300,305,500,505,510,515,520,525,530,535,540,545,550,555,565,570,575,580,585,590,595,596,600,605,610,615,620,625,626,627,630,635,640,645,650,655,660,665,667,669,670,675,680,685,690,695,696,700,705,710,715,720,725,730,735,740]");
this->msg->AddChar(2);
this->sendData(this->msg->toString());
break;
default:
std::cout << "Unregistered header " << header << std::endl;
break;
}
}
else
{
break;
}
}
std::string data = c;
is only good if the string is surely 0-terminated
std::string raw = data.substr(0, bits);
you could do that simpler
const std::string raw(c, c+bits);
in your policy function there's a char c for no reason, but if it had value >0, would likely cause problems.
And most importantly, sending sizeof(Env::Policy()) bytes makes no sense at all, you shall send the whole string!
const auto& policy = Env::Policy();
send(this->s, policy, policy.size() + 1, 0);
maybe without +1, depending if you want the 0.