I am trying to send one value to multiple TcpSockets but every client gets an own value.
I have a variable called clientCounter, the clientCounter has the number of clients which are connected. The server sends to all clients the number of clients which are connected. When one is connected 1 is sent to all clients.
Now the results: For example when 4 clients are connected, Client1 receives the number 1, Client2 the number 2, etc...
But the results should be: Every client should get the number 4 when for example four clients are connected.
Main thread:
int main(){
Server server;
while (true) {
server.Send(server.GetPlayerCount());
}
return 0;
}
The number of clients are stored in there:
std::atomic<unsigned int> clientCounter;
int Server::GetPlayerCount()
{
return clientCounter;
}
This shows the function which is sending the data to all clients:
void Server::Send(int m)
{
for (int i = 0; i < clientCounter; i++) {
sf::Packet packet;
packet << m;
if (clients[i].send(packet) != sf::Socket::Status::Done) {
std::cout << "Cant send Packet" << std::endl;
}
packet.clear();
}
}
This is what the client is doing:
sf::Packet packet;
int count = 0;
while (true) {
socket.receive(packet);
packet >> count;
std::cout << count << std::endl;
}
When client is connected, clientCounter add one:
if (listener.accept(clients[clientCounter]) != sf::Socket::Status::Done) {
std::cout << "Error cant get Socket..." << std::endl;
}
else {
std::cout << "New client connected: " << clients[clientCounter].getRemoteAddress() << std::endl;
clientCounter++;
}
And I want that all clients get all the same number.
Related
I have to try to implement the following details, can you please let me know what is wrong with the implementation and how the right one implemented.
Client and Server should be able exchange 4-byte messages via TCP protocol.Client generate random unsigned number in range [10^6, 10^8) and sends it to Server.
2)Server has a SUM resource which represents a calculation result of processed messages received from Clients.
On receiving random unsigned number in range [10^6, 10^8) from client, Server adds this number to SUM if it is an even number and subtracts otherwise.
3)Server continuously listens for new connections and is capable of handling multiple clients at the same time.
4)If an absolute value of SUM in the console did not exceed MAX UINT 32 value after the operation,
The server prints current all connections and sends an acknowledgment message with MAX UINT 32 value.
otherwise, SUM exceeds MAX UINT 32 value, closes all connections, and exits.
5)If the Client receives the acknowledgment, it starts a 50ms timer and repeats the generation/sending sequence, until Server closes the connection.
=======================client.cpp===================================
#include <iostream>
#include <boost/asio.hpp>
#include<boost/lexical_cast.hpp>
using namespace boost::asio;
using ip::tcp;
using std::string;
using std::cout;
using std::endl;
int getRandomNumber(int min, int max)
{ // static used for efficiency, so we only calculate this value once
static constexpr uint_32 fraction { 1.0 / (RAND_MAX + 1.0) };
// evenly distribute the random number across our range
return min + static_cast<int>((max - min + 1) * (std::rand() * fraction));
}
int main() {
boost::asio::io_service io_service;
//socket creation
tcp::socket socket(io_service);
//connection
socket.connect( tcp::endpoint( boost::asio::ip::address::from_string("127.0.0.9"), 4321 ));
// request/message from client
const string msg = "Hello from Client!\n";
unsigned long int number;
number = getRandomNumber(10^6, 10^8);
boost::system::error_code error;
boost::asio::write( socket, boost::asio::buffer(number), error );
if( !error ) {
cout << "Client sent hello message!" << endl;
}
else {
cout << "send failed: " << error.message() << endl;
}
// getting response from server
boost::asio::streambuf receive_buffer;
boost::asio::read(socket, receive_buffer, boost::asio::transfer_all(), error);
if( error && error != boost::asio::error::eof ) {
cout << "receive failed: " << error.message() << endl;
sleep_for_a_while(50);
}
else {
const char* data = boost::asio::buffer_cast<const char*>(receive_buffer.data());
cout << data << endl;
}
return 0;
}
=============================server.cpp===========================
unsigned long int& read_(tcp::socket & socket, unsigned long int& sum) {
boost::asio::streambuf buf;
boost::asio::read_until( socket, buf, "\n" );
unsigned long int data = boost::asio::buffer_cast<const char*>(buf.data());
if(data % 2 == 1) {
sum = sum - data;
} else {
sum = sum + data;
}
return data;
}
//send msg to client
void send_(tcp::socket & socket, const string& message) {
const string msg = message + "\n";
boost::asio::write( socket, boost::asio::buffer(message) );
}
//main to init server
int main() {
unsigned long int sum = 0;
//io_service object is needed whenever a program is using asio
boost::asio::io_service io_service;
//listen for new connection requested by the client
tcp::acceptor acceptor_(io_service, tcp::endpoint(tcp::v4(), 4321 ));// IP-ipv4 and port-1234
//socket creation
tcp::socket socket_(io_service);
//waiting for connection
acceptor_.accept(socket_);
//read operation //write operation
unsigned long int SUM = read_(socket_, &sum);
if(abs(SUM) <= 4294967295) { //2^32
cout << "SUM = " << SUM<< endl;
send_(socket_, "Acknowledgment from Server with MAX UINT 32 value-4294967295");
} else {
socket_.close();
}
return 0;
}
I have tried following the tutorial from boost, however the API is not identical so I have had to guess some parts.
My attempt so far is shown bellow:
#include <iostream>
#include <experimental/internet>
#include <experimental/socket>
#include <thread>
#include <chrono>
using namespace std::experimental;
int main(int argc, char* argv[])
{
std::thread server = std::thread([]()
{
std::cout << "Starting server" << std::endl;
net::io_context context;
net::ip::tcp::endpoint endpoint{net::ip::tcp::v4(), 1234};
net::ip::tcp::acceptor acceptor{context, endpoint};
acceptor.non_blocking(true);
std::cout << "opened server on " << endpoint << std::endl;
std::error_code error;
net::ip::tcp::socket socket(context);
while (true)
{
socket = acceptor.accept(error); //accept connections
if (!error) //if connected with a client
{
std::cout << "Connected to client!" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(2));
std::string data = "Hello World!";
net::const_buffer buf(&data, sizeof(data));
socket.send(buf);
std::cout << "Sent data!" << std::endl;
while(true) {}
}
}
});
std::thread client = std::thread([]()
{
net::io_context context;
net::ip::tcp::socket socket(context);
net::ip::tcp::endpoint server{net::ip::tcp::v4(), 1234};
std::error_code error;
while(true)
{
socket.connect(server, error); //attempt to connect
if (!error) //if connected
{
std::cout << "Connected to server!" << std::endl;
net::mutable_buffer buf;
while(buf.size() == 0)
{
socket.receive(buf);
}
std::cout << "Received data!" << std::endl;
std::cout << buf.data() << std::endl;
while(true) {}
}
}
});
server.join();
return 0;
}
The sever and client connect, but the message is not received by the client. The output from the program above is:
Starting server
opened server on 0.0.0.0:1234
Connected to server!
Connected to client!
Sent data!
And then it waits forever.
How do I get the socket to correctly receive the data?
This
std::string data = "Hello World!";
net::const_buffer buf(&data, sizeof(data));
is wrong. You want to send content of data string, not its internal bytes. &data gives you a pointer to underlying data of string instance, not its content. If you want to create buffer which represents content of data you can do:
const std::string data = "Hello World!";
net::const_buffer buf = net::buffer(data);
This
net::mutable_buffer buf;
while(buf.size() == 0)
{
socket.receive(buf);
}
gives you infinite loop because initial size of buf is 0, so receive reads 0 bytes and returns. Then while condition is checked, buf's size is still 0, and the loop goes on.
Before calling receive you need to specify the size of buffer - it indicates how many bytes must be read. You are sending Hello World! so
std::string msg;
msg.resize(12); // prepare space for incoming data
net::mutable_buffer buf = net::buffer(msg);
socket.receive(buf);
std::cout << "I got: " << msg << std::endl;
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
I send a packet as client to server and I want to the server sends that packet forward to all client, here is the code:
#include <iostream>
#include <SFML/Network.hpp>
using namespace std;
int main()
{
int fromID; // receive data from 'fromID'
int Message; // fromID's message
sf::SocketTCP Listener;
if (!Listener.Listen(4567))
return 1;
// Create a selector for handling several sockets (the listener + the socket associated to each client)
sf::SelectorTCP Selector;
Selector.Add(Listener);
while (true)
{
unsigned int NbSockets = Selector.Wait();
for (unsigned int i = 0; i < NbSockets; ++i)
{
// Get the current socket
sf::SocketTCP Socket = Selector.GetSocketReady(i);
if (Socket == Listener)
{
// If the listening socket is ready, it means that we can accept a new connection
sf::IPAddress Address;
sf::SocketTCP Client;
Listener.Accept(Client, &Address);
cout << "Client connected ! (" << Address << ")" << endl;
// Add it to the selector
Selector.Add(Client);
}
else
{
// Else, it is a client socket so we can read the data he sent
sf::Packet Packet;
if (Socket.Receive(Packet) == sf::Socket::Done)
{
// Extract the message and display it
Packet >> Message;
Packet >> fromID;
cout << Message << " From: " << fromID << endl;
//send the message to all clients
for(unsigned int j = 0; j < NbSockets; ++j)
{
sf::SocketTCP Socket2 = Selector.GetSocketReady(j);
sf::Packet SendPacket;
SendPacket << Message;
if(Socket2.Send(SendPacket) != sf::Socket::Done)
cout << "Error sending message to all clients" << endl;
}
}
else
{
// Error : we'd better remove the socket from the selector
Selector.Remove(Socket);
}
}
}
}
return 0;
}
Client code:
in Player class I have this function :
void Player::ReceiveData()
{
int mess;
sf::Packet Packet;
if(Client.Receive(Packet) == sf::Socket::Done)
{
Client.Receive(Packet);
Packet >> mess;
cout << mess << endl;
}
}
main.cpp:
Player player;
player.Initialize();
player.LoadContent();
player.Connect();
..
..
//GAME LOOP
while(running==true)
{
sf::Event Event;
while(..) // EVENT LOOP
{
...
}
player.Update(Window);
player.ReceiveData();
player.Draw(Window);
}
When I run this client code, the program not responding, freezes.
The problem is with that ReceiveDate() function.
All sockets, even the one created by SFML, are by default blocking. This means that when you try to receive when there is nothing to receive, the call will block, making your application seem "freezed".
You can toggle the blocking status of a SFML socket with the sf::SocketTCP::SetBlocking function.
The problem with sending to all clients failing is because you use GetSocketReady to get the clients to send to. That function only returns a socket for clients that are ready (i.e. the previous call to Wait marked the socket as having input).
You need to refactor the server to keep track of the connected clients in another way. The common way is to reset and recreate the selector every time in the outer loop, and have a separate collection of the connected clients (e.g. a std::vector).
Note: I am mixing some C & C++ but it shouldn't be too confusing.
Connected clients are added to a multimap (might aswel have been a map, I know ^^).
The multimap
typedef std::multimap<std::string, SOCKET> clientMap;
typedef std::pair<clientMap::iterator,
clientMap::iterator>
clientIters;
clientMap clientmap;
clientIters clientByID(std::string clientID)
{
return clientmap.equal_range(clientID);
}
Adding a client (each client runs in it's own thread)
//Add clientID to map of clients
if(clientAdded == false)
{
std::stringstream ss;
ss << lpParam; //Use socket# as the unique ID
clientID = ss.str();
clientmap.insert(std::pair<std::string,SOCKET>(clientID,sock));
clientAdded = true;
}
//Client wasn't added correctly!
if(clientID == "" || clientAdded == false)
{
std::cout << "Problem adding client" << std::endl;
}
Server waits for client messages with a blocking recv() call
ret = recv(sock, szBuff, DEFAULT_BUFFER, 0);
if (ret == 0) // Graceful close
break;
else if (ret == SOCKET_ERROR)
{
printf("recv() failed: %d\n", WSAGetLastError());
removeUser(clientID);
break;
}
removeUser() function
void removeUser(std::string clientID)
{
std::cout << "Going to try and remove client: " << clientID << std::endl;
//Remove client
clientIters iters = clientByID(clientID);
clientMap::iterator it = iters.first;
while(it != iters.second)
{
clientmap.erase(it++);
std::cout << "removed client: " << it->first << std::endl;
}
//Show remaining clients
std::cout << "clients left:" << std::endl;
for (clientMap::iterator it = clientmap.begin(); it != clientmap.end(); ++it)
{
std::cout << it->first << std::endl;
}
}
All of this is working fine if I close a client that was connected to either my local IP or external IP (directly).
recv() failed: 10054
Going to try and remove client 0000008C
removed client 0000008C
clients left:
00000084
00000088
But if I put a proxy inbetween (using proxifier), the server thinks the connection is still active or something because the server doesn't do anything when I close the client. It just sits there.. waiting for nothing.
I'm using this socket class as a base but it's pretty heavily modified by now.
How can I make sure a closed client is definitely removed from the multimap?
Could you store some state for each connection? like the last time something was received?
You could then periodically loop through your active connections and prune any that haven't sent anything for a long time, as the connection is more then likely stale.