it may be a strange request, but, actually, I would like make my call to recv() blocking.
It appears that it should be blocking by default, but for me, it is not, and I can't find any way to make it.
I have a very simple server application, "open socket - bind - wait for connection - receive" on one hand,
and, on the other hand, a simple "open socket - connect - send" client application.
They communicate on LocalHost.
Server :
int clTcpServer::openSocket(void)
{
this->__iLocalSocketId = socket(AF_INET, //stand for IPV4
SOCK_STREAM, //using TCP protocol
0); //no specified / ip protocol
return 0;
}
int clTcpServer::openServer(uint16_t u16Port)
{
this->__strServerInfo.sin_family = AF_INET; // stand for IPV4
this->__strServerInfo.sin_addr.s_addr = INADDR_ANY; // Listen to any address (no filter)
this->__strServerInfo.sin_port = htons(u16Port);
if(bind(this->__iLocalSocketId, (sockaddr *)&this->__strServerInfo, sizeof(this->__strServerInfo)) < 0)
{
std::cout << "TCP server - ERROR - Can't bind socket to port " << std::dec << (int)u16Port << std::endl;
return -1;
}
std::cout << "TCP server - Server opened" << std::endl;
listen(this->__iLocalSocketId, 3);
return 0;
}
int clTcpServer::waitForConnection(void)
{
int iClientIpInfo;
char* pcClientIp;
std::cout << "Waiting for connection ..." << std::endl;
iClientIpInfo = sizeof(struct sockaddr_in);
this->__iDistantSocketId = accept(this->__iLocalSocketId,
(sockaddr *)&this->__strClientInfo,
(socklen_t*)&iClientIpInfo);
if(this->__iDistantSocketId < 0)
{
std::cout << "TCP server - ERROR - Connection refused" << std::endl;
return -1;
}
pcClientIp = inet_ntoa(this->__strClientInfo.sin_addr);
std::cout << "Connection accepted from " << pcClientIp << std::endl;
return 0;
}
int clTcpServer::receiveDataBuffer(void * pvidBuffer, size_t sizeMaxBufferSize)
{
int iRecvSize;
iRecvSize = recv(this->__iDistantSocketId, pvidBuffer, sizeMaxBufferSize, (int)0);
if (iRecvSize < (int)0 )
{
std::cout << "TCP server - ERROR - Recv failed" << std::endl;
return -1;
}
return iSentSize;
}
Client :
int clTcpClient::openSocket(void)
{
/* Create a new socket for client comm. */
this->__iSocketId = socket( AF_INET, //stand for IPV4
SOCK_STREAM, //using TCP protocol
0); //no specified / ip protocol
if (this->__iSocketId == (int)-1)
{
std::cout << "TCP client - ERROR - Can't open Socket" << std::endl;
return -1;
}
return 0;
}
int clTcpClient::connectToServer(std::string stgIpAddress, uint16_t u16Port)
{
/* configure server info according to parameters */
this->__strServerInfo.sin_family = AF_INET; //stand for IPV4
this->__strServerInfo.sin_port = htons(u16Port);
/* Retrieve IP addr*/
this->__strServerInfo.sin_addr.s_addr = inet_addr(stgIpAddress.c_str());
/* Try to connect to the server */
if (connect(this->__iSocketId,
(sockaddr*)&this->__strServerInfo,
sizeof(this->__strServerInfo)) < 0)
{
std::cout << "TCP client - ERROR - Can't connect to " << stgIpAddress << ":" << std::dec << (int)u16Port << std::endl;
return -1;
}
std::cout << "TCP client - Connected" << std::endl;
return 0;
}
int clTcpClient::sendDataBuffer(const void *kpvidData, size_t sizeDataLength)
{
ssize_t sizeSentSize;
/* Send the buffer */
sizeSentSize = send(this->__iSocketId, kpvidData, sizeDataLength, (int)0);
if (sizeSentSize < (ssize_t)0 )
{
std::cout << "TCP client - ERROR - Send failed" << std::endl;
return -1;
}
std::cout << "TCP client - " << (int)sizeSentSize << " byte(s) sent." << std::endl;
return 0;
}
This is working fine, but recv() here is not blocking. If I call it (after having initialized all the stuff, of course), if there is nothing to receive, the function returns 0, as "0 byte received", and that's all.
It means If I want to put this recv function in a loop, CPU is working 100% to receive ... nothing.
What's wrong here ?How can I make it blocking ?
Thank you
Related
I am developing a Bluetooth application to connect to a mobile phone. I'm using Rfcomm Bluetooth sockets from the old C standard library. I have this code to create a Bluetooth object and prepare the connection:
class BtDevice final {
...
static constexpr bdaddr_t bdaddrAny {0, 0, 0, 0, 0, 0};
static constexpr uint8_t rfcommChannel = 1;
const sockaddr_rc ourAddress { .rc_family = AF_BLUETOOTH, .rc_bdaddr = bdaddrAny, .rc_channel = rfcommChannel };
int ourSocket = 0;
...
}
BtDevice::BtDevice() {
ourSocket = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
if (ourSocket == -1)
throw std::runtime_error("Bluetooth: Socket creation failed - " + std::string(strerror(errno)) + ".");
static constexpr auto flag = 1;
if(setsockopt(ourSocket, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(int)) != 0)
std::cerr << "Failed to translate socket into the reusable mode - " << strerror(errno) << "." << std::endl;
}
void BtDevice::prepare() {
if (bind(ourSocket, (sockaddr *) &ourAddress, sizeof(ourAddress)) < 0)
throw std::runtime_error("Bluetooth: Socket binding failed - " + std::string(strerror(errno)) + ".");
if (listen(ourSocket, 1) == -1)
throw std::runtime_error("Bluetooth Socket listening failed - " + std::string(strerror(errno)) + ".");
}
If for some reason the phone connection drops, I want my socket to be destroyed and reopened. I have this code for destroying an object:
BtDevice::~BtDevice() {
if(close(ourSocket) != 0)
std::cerr << "Bluetooth: Socket closure failed - " << strerror(errno) << "." << std::endl;
else
std::cout << "Bluetooth socket closed and object is destructed." << std::endl;
}
And I have my program running in a loop in case of errors:
while(true) {
try {
BtDevice btDevice;
btDevice.prepare();
...
/* Main functionality. */
...
break;
} catch (const std::exception& e) {
std::cerr << "Exception during the process: " << e.what() << "." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(5));
}
}
However, if the connection fails, I see in my output that the bind for the new socket failed with the message "Address already in use" (error 98):
Exception during the initialization process: ... (Connection with phone interrupted).
Bluetooth socket closed and object is destructed.
Exception during the initialization process: Bluetooth: Socket binding failed - Address already in use..
Sleeping for 5 seconds...
I've seen questions with similar problems (this question and links from it) and as you can see I added setting the SO_REUSEADDR option right after the socket creation in the constructor.
I see from the documentation that the SO_REUSEADDR option should work for AF_INET sockets:
For AF_INET sockets this means that a socket may bind, except when there is an active listening socket bound to the address.
Should it work the same for bluetooth sockets? And if not, is there a suitable replacement for this?
Premise:
I'm building on newly-learned networking fundamentals learned from these two questions: one, two.
I'll call the the code at the bottom of this post my "http responder," and not a "http server," since I recently got an educational/appreciated slap on the wrist for calling it the latter.
The program functions as follows:
it listens at INADDR_ANY port 9018 (a naively/randomly-chosen number)
it dumps (to stdout) the content received at the accepted socket until there's no more content to read
it sends a minimal HTTP response with status OK.
(in case #Remy Lebeau visits this question, item 2, specifically, is why this program is not a http server: it does not parse the incoming HTTP request, it just dumbly dumps it and responds -- even in the case of a closed TCP connection -- but I believe this is not relevant to the question asked here).
From my second link, above, I learned about why a web server would want to listen to a specific port on all interfaces.
My understanding is that the way this is done in C-family languages is by binding to INADDR_ANY (as opposed to a specific IP address, like "127.0.0.13").
Question:
When I run this program, I observe the expected result if I try to connect from a web browser that is running on the same PC as where the executable is run: my browser shows a minimal webpage with content "I'm the content" if I connect to 127.0.0.1:9018, 127.0.0.2:9018, 127.0.0.13.9018, 127.0.0.97:9018, etc.
Most relevant to this question, I also get the same minimal webpage by pointing my browser to 10.0.0.17:9018, which is the IP address assigned to my "wlpls0" interface:
$ ifconfig
...
wlp1s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 10.0.0.17 netmask 255.255.255.0 broadcast 10.0.0.255
inet6 fe80::5f8c:c301:a6a3:6e35 prefixlen 64 scopeid 0x20<link>
ether f8:59:71:01:89:cf txqueuelen 1000 (Ethernet)
RX packets 1272659 bytes 1760801882 (1.7 GB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 543118 bytes 74285210 (74.2 MB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
However, I only observe this desired webpage if the browser that I point to 10.0.0.7:9018 is running on the same PC as where the a.out is running.
From another PC on the same network, if I point its browser to 10.0.0.17:9018, the browser spins without connecting, and eventually says "Hmm...can't reach this page" and "10.0.0.17 took too long to respond".
So my question is: what are reasons why only a browser running on the same PC as the running a.out can connect to the "http responder"? Why do browsers on a different PC in the same network seem unable to connect?
What I have tried:
On the other PC, I am able to ping 10.0.0.17 -- and that just about exhausts my knowledge of how to debug networking issues.
I considered whether the issue at root is more likely to be "networking stuff", which might make this question better asked at Super User, but then I thought to start my inquiry with Stack Overflow, in case the issues is in the C++ code.
The code:
// main.cpp
#include <arpa/inet.h>
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <fcntl.h>
#include <iostream>
#include <netinet/in.h>
#include <pthread.h>
#include <semaphore.h>
#include <stdexcept>
#include <sstream>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#define IP "0.0.0.0"
#define PORT (9018)
/**
* A primitive, POC-level HTTP server that accepts its first incoming connection
* and sends back a minimal HTTP OK response.
*/
class Server {
private:
static const std::string ip_;
static const std::uint16_t port_{PORT};
int listen_sock_;
pthread_t tid_;
public:
Server() { ///< create + bind listen_sock_; start thread for startRoutine().
using namespace std;
int result;
if (! createSocket()) { throw runtime_error("failed creating socket"); }
if (! bindSocket()) { throw runtime_error("failed binding socket"); }
if ((result = pthread_create(&tid_, NULL, startRoutine, this))) {
std::stringstream ss;
ss << "pthread_create() error " << errno << "(" << result << ")";
std::cerr << ss.str() << std::endl;
throw runtime_error("failed spawning Server thread");
}
}
~Server() { ///< wait for the spawned thread and destroy listen_sock_.
pthread_join( tid_, NULL );
destroySocket();
}
private:
bool createSocket() { ///< Creates listen_sock_ as a stream socket.
listen_sock_ = socket(PF_INET, SOCK_STREAM, 0);
if (listen_sock_ < 0) {
std::stringstream ss;
ss << "socket() error " << errno << "(" << strerror(errno) << ")";
std::cerr << ss.str() << std::endl;
}
return (listen_sock_ >= 0);
}
void destroySocket() { ///< shut down and closes listen_sock_.
if (listen_sock_ >= 0) {
shutdown(listen_sock_, SHUT_RDWR);
close(listen_sock_);
}
}
bool bindSocket() { ///< binds listen_sock_ to ip_ and port_.
int ret;
sockaddr_in me;
me.sin_family = PF_INET;
me.sin_port = htons(port_);
me.sin_addr.s_addr = INADDR_ANY;
int optval = 1;
setsockopt(listen_sock_, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval);
if ((ret = bind(listen_sock_, (sockaddr*)&me, sizeof me))) {
std::stringstream ss;
ss << "bind() error " << errno << "(" << strerror(errno) << ")";
std::cerr << ss.str() << std::endl;
}
return (! ret);
}
/**
* Accept a connection from listen_sock_.
* Caller guarantees listen_sock_ has been listen()ed to already.
* #param tv [in, out] How long to wait to accept a connection.
* #return accepted socket; -1 on any error.
*/
int acceptConnection(timeval& tv) {
int sock = -1;
int ret;
fd_set readfds;
sockaddr_in peer;
socklen_t addrlen = sizeof peer;
FD_ZERO(&readfds);
FD_SET(listen_sock_, &readfds);
ret = select(listen_sock_ + 1, &readfds, NULL, NULL, &tv);
if (ret < 0) {
std::stringstream ss;
ss << "select() error " << errno << "(" << strerror(errno) << ")";
std::cerr << ss.str() << std::endl;
return sock;
}
else if (! ret) {
std::cout << "no connections within " << tv.tv_sec << " seconds"
<< std::endl;
return sock;
}
if ((sock = accept(listen_sock_, (sockaddr*)&peer, &addrlen)) < 0) {
std::stringstream ss;
ss << "accept() error " << errno << "(" << strerror(errno) << ")";
std::cerr << ss.str() << std::endl;
}
else {
std::stringstream ss;
ss << "socket " << sock << " accepted connection from "
<< inet_ntoa( peer.sin_addr ) << ":" << ntohs(peer.sin_port);
std::cout << ss.str() << std::endl;
}
return sock;
}
static void dumpReceivedContent(const int& sock) { ///< read & dump from sock.
fd_set readfds;
struct timeval tv = {30, 0};
int ret;
FD_ZERO(&readfds);
FD_SET(sock, &readfds);
ret = select(sock + 1, &readfds, NULL, NULL, &tv);
if (ret < 0) {
std::stringstream ss;
ss << "select() error " << errno << "(" << strerror(errno) << ")";
std::cerr << ss.str() << std::endl;
return;
}
else if (! ret) {
std::cout << "no content received within " << tv.tv_sec << "seconds"
<< std::endl;
return;
}
if (FD_ISSET(sock, &readfds)) {
ssize_t bytes_read;
char buf[80] = {0};
fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK);
std::cout << "received content:" << std::endl;
std::cout << "----" << std::endl;
while ((bytes_read = read(sock, buf, (sizeof buf) - 1)) >= 0) {
buf[bytes_read] = '\0';
std::cout << buf;
}
std::cout << std::endl << "----" << std::endl;
}
}
static void sendMinHttpResponse(const int& sock) { ///< min HTTP OK + content.
static const std::string html =
"<!doctype html>"
"<html lang=en>"
"<head>"
"<meta charset=utf-8>"
"<title>blah</title>"
"</head>"
"<body>"
"<p>I'm the content</p>"
"</body>"
"</html>";
std::stringstream resp;
resp << "HTTP/1.1 200 OK\r\n"
<< "Content-Length: " << html.length() << "\r\n"
<< "Content-Type: text/html\r\n\r\n"
<< html;
write(sock, resp.str().c_str(), resp.str().length());
}
/**
* Thread start routine: listen for, then accept connections; dump received
* content; send a minimal response.
*/
static void* startRoutine(void* arg) {
Server* s;
if (! (s = (Server*)arg)) {
std::cout << "Bad arg" << std::endl;
return NULL;
}
if (listen(s->listen_sock_, 3)) {
std::stringstream ss;
ss << "listen() error " << errno << "(" << strerror(errno) << ")";
std::cerr << ss.str() << std::endl;
return NULL;
}
std::cout << "Server accepting connections at "
<< s->ip_ << ":" << s->port_ << std::endl;
{
timeval tv = { 30, 0 };
int sock = s->acceptConnection(tv);
if (sock < 0) {
std::cout << "no connections accepted" << std::endl;
return NULL;
}
dumpReceivedContent(sock);
sendMinHttpResponse(sock);
shutdown(sock, SHUT_RDWR);
close(sock);
}
return NULL;
}
};
const std::string Server::ip_{IP};
int main( int argc, char* argv[] ) {
Server s;
return 0;
}
Compilation/execution:
This is a "working" case when the http responder receives a connection from a web browser on the same PC connecting to 10.0.0.17:9018:
$ g++ -g ./main.cpp -lpthread && ./a.out
Server accepting connections at 0.0.0.0:9018
socket 4 accepted connection from 10.0.0.17:56000
received content:
----
GET / HTTP/1.1
Host: 10.0.0.17:9018
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
----
This is the problem/question case when the http responder receives nothing from a web browser on a different PC in the same network connecting to 10.0.0.17:9018:
$ ./a.out
Server accepting connections at 0.0.0.0:9018
no connections within 0 seconds
no connections accepted
** The "no connections within 0 seconds" message is because select() updated the struct timeval.tv_sec field -- the program has actually waited 30 seconds.
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));
}
This question already has answers here:
What are the use cases of SO_REUSEADDR?
(2 answers)
Closed 7 years ago.
discovering TCP socket, I made a very simple test based on my understanding of the subject and some tuto found on the net
Server :
void Server(void)
{
int localSocket;
int distantSocket;
sockaddr_in serverInfo;
sockaddr_in clientInfo;
int sizeOfSocketInfo = sizeof(struct sockaddr_in);
/* Open Socket */
std::cout << "open socket" << std::endl;
localSocket = socket(AF_INET, SOCK_STREAM, 0);
if (localSocket == -1)
{
std::cout << "open failed, error - " << (int)errno << std::endl;
exit(errno);
}
/* Configure server */
serverInfo.sin_addr.s_addr = INADDR_ANY;
serverInfo.sin_family = AF_INET;
serverInfo.sin_port = htons(61001);
/* Bind Socket */
std::cout << "bind socket" << std::endl;
if (bind (localSocket, (sockaddr *) &serverInfo, sizeof(serverInfo)) == -1)
{
std::cout << "bind failed, error - " << (int)errno << std::endl;
exit(errno);
}
/* Wait for client */
std::cout << "Wait for client ..." << std::endl;
listen(localSocket, 1);
distantSocket = accept(localSocket, (sockaddr *)&clientInfo, (socklen_t*)&sizeOfSocketInfo);
std::cout << "client connected - " << inet_ntoa(clientInfo.sin_addr) << std::endl;
/* Close Socket */
close (localSocket);
close (distantSocket);
std::cout << "socket closed" << std::endl;
}
and client :
void Client(void)
{
int localSocket;
sockaddr_in clientInfo;
/* Open Socket */
std::cout << "open socket" << std::endl;
localSocket = socket(AF_INET, SOCK_STREAM, 0);
if (localSocket == -1)
{
std::cout << "open failed, error - " << (int)errno << std::endl;
exit(errno);
}
clientInfo.sin_family = AF_INET;
clientInfo.sin_port = htons(61001);
clientInfo.sin_addr.s_addr = inet_addr("127.0.0.1");
/* Open Socket */
std::cout << "connect to server" << std::endl;
if (connect(localSocket, (sockaddr*)&clientInfo, sizeof(clientInfo)) < (int)0)
{
std::cout << "connect failed, error - " << (int)errno << std::endl;
exit(errno);
}
std::cout << "connected !" << std::endl;
close(localSocket);
}
When I launch the server in one terminal and the client in another one, it seems to be ok :
server side :
> ./tcpTest -s
open socket
bind socket
Wait for client ...
client connected - 127.0.0.1
socket closed
>
and client side :
> ./tcpTest -c
open socket
connect to server
connected !
>
But, if, just after this first try, I launch the server again ...
> ./tcpTest -s
open socket
bind socket
bind failed, error - 98
>
And I have to wait a "certain time", I don't know exactly how long, one minute maybe, to have the server working again.
I can't figure out what's happening, looking to open socket with sockstat does not show anything strange (I only see mozilla socket).
I found this guy having the same problem but in Ruby
basic Ruby TCP server demo fails on startup: `bind': Address already in use, Errno::EADDRINUSE
If this is really the same problem, how can I apply the same solution in C++ ? Or do you have any idea ?
Thank you
You may need to use both SO_REUSEADDR and SO_REUSEPORT. You can further go through the documentation of Socket: Socket Docs
const int trueFlag = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &trueFlag, sizeof(int)) < 0)
error("Failure");
You can use reuse Port in similar way. Hope this helps
Try setsockopt(SO_REUSEADDR), this should help.
I'm trying to create a web crawler, and I want it to be able to connect to web sites through a local proxy.
So, let's say that we want to send a GET message to google and retrieve it's HTML code, all this through a local proxy (i'm working at my university and there is a proxy to connect to external sites like google).
This is my code:
#include <iostream>
#include <cstring> // Needed for memset
#include <sys/socket.h> // Needed for the socket functions
#include <netdb.h> // Needed for the socket functions
#include <cstdlib>
#include <string>
using namespace std;
int main(int argc, char* argv[])
{
addrinfo host_info; // The struct that getaddrinfo() fills up with data.
addrinfo *host_info_list;
int socketfd;
char* msg = NULL;
char* msg2 = NULL;
int status;
int len;
memset(&host_info, 0, sizeof host_info);
host_info.ai_family = AF_INET;//AF_UNSPEC;
host_info.ai_socktype = SOCK_STREAM;
//PROXY IP = proxy.feng.edu.uy ; PORT = 3318; //HTTP1.0 proxy
status = getaddrinfo("proxy.feng.edu.uy", "3318", &host_info, &host_info_list);
socketfd = socket(host_info_list->ai_family, host_info_list->ai_socktype,
host_info_list->ai_protocol);
if (socketfd == -1) std::cout << "ERROR: socket error " << std::endl ;
std::cout << "Connect()ing..." << std::endl;
status = connect(socketfd, host_info_list->ai_addr, host_info_list->ai_addrlen);
if (status == -1) std::cout << "ERROR: connect error" << std::endl ;
msg = new char[200];
strcpy(msg,"CONNECT www.google.com HTTP/1.0\r\n");
strcat(msg,"\r\n");
ssize_t bytes_sent;
len = strlen(msg);
bytes_sent = send(socketfd, msg, len, 0);
ssize_t bytes_recieved=0;
std::cout << "Waiting to recieve data..." << std::endl;
char* incoming_data_buffer = new char[200];
bytes_recieved = recv(socketfd, incoming_data_buffer,200, 0);
if (bytes_recieved == 0) std::cout << "host shut down." << std::endl ;
if (bytes_recieved == -1)std::cout << "ERROR: receive error!" << std::endl ;
std::cout << bytes_recieved << " bytes recieved" << std::endl ;
std::cout << incoming_data_buffer << std::endl;
msg2 = new char[300];
strcpy(msg2,"GET http://www.google.com/ HTTP/1.0\r\n\r\n");
std::cout << "Message sent to google: " << msg2 << std::endl;
len = strlen(msg2);
bytes_sent = send(socketfd, msg2, len, 0);
cout << "bytes_sent: " << bytes_sent << endl;
bytes_recieved=0;
std::cout << "Waiting to recieve data ..." << std::endl;
char* incoming_data_buffer2 = new char[1000];
bytes_recieved = recv(socketfd, incoming_data_buffer2,1000, 0);
if (bytes_recieved == 0) std::cout << "host shut down." << std::endl ;
if (bytes_recieved == -1)std::cout << "ERROR: recieve error!" << std::endl ;
std::cout << bytes_recieved << " bytes recieved" << std::endl ;
std::cout << incoming_data_buffer2 << std::endl;
return 0;
}
The problem I'm having is as follows..
First, incoming_data_buffer (which is the buffer from the "CONNECT") returns: "HTTP1.0 200 connection established", which is good, no problems until now.
Next I send the "GET" message to the proxy so that it forwards the message to google as expected(now that the connection is established), and it stays idle in the "recv()" for 1 minute or so, then it returns 0 (which means that the connection is closed i guess) and the buffer is empty...
My problem is that i have no clue why recv() returns 0... Any ideas?? It supposedly means the connection is closed, but why then? what else do i have to do in order for the proxy to maintain the connection? (supposing that a "connection closed" is the problem).
Thanks in advance!
The CONNECT method is an HTTP tunneling feature. A proxy that supports it may limit its use to connecting to HTTPS web sites (source: Wikipedia -- HTTP tunnel). You are trying to establish a connection with a standard HTTP server through CONNECT, which the proxy may be blocking.
After you establish a connection with your proxy, instead of establishing a tunnel, just send your request. This will work since you are using an absoluteURI to specify your GET target.