I am currently using SFML to create a game which will allow a client to connect and watch the player gameplay. This is currently being done on localhost, 127.0.0.1 on same port since its UDP.
I am launching two executables - one will be client and the other will be the main player[udpserver].
I am noticing that on the client, there is jittering when spaceship moves, and also bullet is drawn two or three times in instantiating position before going upward. (when bullet spawns, it is rendered to go upward, code executes for both client and server). This is what I have -
void createUdpServer(unsigned short port) { // send data to client
// Create a socket to receive a message from anyone
sf::UdpSocket socket;
sf::IpAddress local = sf::IpAddress::getLocalAddress(); // Currently 127.0.0.1 localhost
sf::IpAddress receiver = sf::IpAddress::getLocalAddress();
socket.setBlocking(false);
// I was figuring out and arranging according to example.. set socket non blocking
// Listen to messages on the specified port
if (socket.bind(port) != sf::Socket::Done && !portBound) // You bind once
return; // error
else {
portBound = true;
}
std::cout << "Server is listening to port " << port << ", waiting for a connection... " << std::endl;
// Wait for a message
char in[128];
std::size_t received;
sf::IpAddress sender;
//unsigned short senderPort; we will be receiving and sending data on sa,e port
sf::SocketSelector selector;
selector.add(socket);
if (selector.wait(sf::milliseconds(10.f))) { // times out after 10ms
if (selector.isReady(socket)) {
if (socket.receive(in, sizeof(in), received, sender, port) != sf::Socket::Done) // blocking
return;
std::cout << "Message received from client " << sender << ": \"" << in << "\"" << std::endl;
}
}
// Sends connection established to client
/*
const char out[] = "Connection with server established!";
if (socket.send(out, sizeof(out), sender, port) != sf::Socket::Done)
return;
*/
// acknowledgement of packets
float playerXPosition = player->getPosition().x;
float playerYPosition = player->getPosition().y;
sf::Packet playerData;
sf::Packet bulletData;
playerData << playerXPosition << playerYPosition << bulletPtr->isAlive() << bulletPtr->getLocation().x << bulletPtr->getLocation().y;
//bulletData << bulletPtr->isAlive() << bulletPtr->getLocation().x << bulletPtr->getLocation().y;
socket.send(playerData, sender, port);
//socket.send(bulletData, sender, port);
//const char out[] = "";
//if (socket.send(out, sizeof(out), sender, port) != sf::Socket::Done)
// return;
}
void runUdpClient(unsigned short port) { // receive data from server
// Ask for the server address
server = "127.0.0.1";
/*
if (server == sf::IpAddress::None)
do
{
std::cout << "Type the address or name of the server to connect to: ";
std::cin >> server;
} while (server != sf::IpAddress::None);
*/
// Create a socket for communicating with the server
sf::UdpSocket socket;
sf::IpAddress recipient = sf::IpAddress::getLocalAddress();
char data[100] = "Connection with client established!";
if (socket.send(data, 100, server, port) != sf::Socket::Done && !sendInitialToServer) // if send once dont't send again
{
// added up top send this only once
// error...
}
else{
sendInitialToServer = true;
}
// Think about putting this globally
sf::SocketSelector selector;
selector.add(socket);
if (selector.wait(sf::milliseconds(10.f))) { // not enough time for server to be created with 0.1f
// received something
if (selector.isReady(socket)) {
// Wait for a message
char in[128];
std::size_t received; // am I using this?
sf::IpAddress sender;
sf::Packet playerData;
sf::Packet bulletData;
float playerXPosition;
float playerYPosition;
float clientXPosition;
float clientYPosition;
float clientBulletX;
float clientBulletY;
bool bulletShot;
socket.receive(playerData, sender, port);
//socket.receive(bulletData, sender, port);
if (playerData >> playerXPosition >> playerYPosition >> bulletShot >> clientBulletX >> clientBulletY) { // if you are able to read
clientXPosition = playerXPosition;
clientYPosition = playerYPosition;
player->setPosition(clientXPosition, clientYPosition);
if (bulletShot) {
// - might not need this
bulletPtr->spawn(true); // once bullet spawned, should be taken care of both sides
if (bulletPtr->isAlive())
bulletPtr->setLocation(clientXPosition - 13.f, clientYPosition - 24.f);
//else
// clientbulletx and clientbullety
//bulletPtr->draw(window); // client window handled from both client and server what to do if bullet is alive
}
}
/*
if (bulletData >> bulletShot >> clientBulletX >> clientBulletY) {
if (bulletShot) {
bulletPtr->spawn(true); // once bullet spawned, should be taken care of both sides
bulletPtr->setLocation(player->getPosition().x - 13.f, player->getPosition().y - 24.f);
//bulletPtr->draw(window); // client window handled from both client and server what to do if bullet is alive
}
}*/
else {
// Handle error / packet loss // else simulate?
std::cout << "Error - failed to read from player data packet!" << std::endl; // this is normal when packet is lost
// I think I am getting several packet drops as I am receiving and sending from the same PC same port, localhost
}
//unsigned short senderPort;
//if (socket.receive(in, sizeof(in), received, sender, port) != sf::Socket::Done)
// return;
//std::cout << "Message received from server " << sender << ": \"" << in << "\"" << std::endl;
}
// this shouldn't be here, receive stuff should go on client, server just sends
}
else {
// timeout reached, nothing was received
}
// prediction
// bullet
}
Let me know if I can provide additional information. This is the order of execution from main -
while (window.isOpen())
{
if (userChoice == NULL){
do {
std::cout << "User Choice - " << userChoice << std::endl;
std::cout << "Server or client? (s/c)" << std::endl;
std::cin >> userChoice;
} while (userChoice != 's' && userChoice != 'c');
// The game clock will keep on restarting until the user makes a decision
clock.restart(); // This solved issue to start movements and updates at same time once user chooses an option
}
if (userChoice == 's' || userChoice == 'c') { // start of main if // else start the whole program
// game time over here once game starts // frame independent
// Binding of ports for both client or server
/*
// bind the socket to a port
if (socket.bind(54000) != sf::Socket::Done)
{
// Using UDP starts listening on this port
// whatever happens, bind for both -> will work with two executables running on same network with two different local IPs
}
*/
// Puts the time counter back to zero and also returns the time elapsed since the clock was started
sf::Time deltaTime = clock.restart();
timeSinceLastUpdate += deltaTime;
while (timeSinceLastUpdate > TimePerFrame) // fixed time steps // userchoice != NULL otherwise it will start immeadiately
{
timeSinceLastUpdate -= TimePerFrame; // solves problem with variable delta time, each frame is unique
processEvents(window);
//update(TimePerFrame);
// Start of if server
if (userChoice == 's'){
//if(!serverCreated){ // if problems of continous connection, this needs to place outside of bind
// create connection
createUdpServer(SERVER_PORT); // over here its happening every frame
//serverCreated = true; // is server actually created though?
//}
// Player 1 movement
sf::Vector2f movement(0.f, 0.f);
if (mIsMovingUp)
movement.y -= playerSpeed;
if (mIsMovingDown)
movement.y += playerSpeed;
if (mIsMovingLeft)
movement.x -= playerSpeed;
if (mIsMovingRight)
movement.x += playerSpeed;
// shooting logic
if (mIsShooting) {
if (!bullet.isAlive()) // && !gameOver
{
bullet.spawn(true);
bullet.setLocation(mPlayer.getPosition().x - 13.f, mPlayer.getPosition().y - 24.f); // mPlayer.getPosition().x + 24 (offset) if origin is not set to center
}
}
// ** These two parts need to be put in an if statement, if server, direct movement, if client, move needs to happen with data received
// from server (Server - main player streamer; Client - viewer)
// Possibly this will be the data that needs to be sent to server
mPlayer.move(movement * TimePerFrame.asSeconds()); // Time.deltaTime frame independent movement
// for each player eventually
screenBound(mPlayer);
// screenbound enemies or else check that they do not go off bounds and change direction
// **
} // end of if server
else {
// Handle control through incoming network data
// vector x = datacoming in
// Hypothetically mPlayer.move(data coming in);
// bullet spawn
// setlocation from data packet
runUdpClient(SERVER_PORT);
}
// Enemy Behaviour // Ideally move to separate method
sf::Vector2f enemyMovement(1.f, 0.f);
float yOffset = 30.f;
float xOffset = 60.f;
// Be careful with iterators as when deleting it will automatically jump to the next item in vector
for (std::vector<Enemy>::iterator i = enemyList->begin(); i != enemyList->end(); ++i) // CAUTION & solved everything again - lesson in C++, if we don't get address, we always create a copy and modify it, which is not what we want
{
//std::cout << "Direction before working move " << direction << std::endl;
i->getSprite().move(enemyMovement * direction); // Issue detected direction only changing sign if greater or less than screen bounds once
if (i->getSprite().getPosition().x + i->getSprite().getLocalBounds().width / 2 > game->getWindowWidth() ||
i->getSprite().getPosition().x - i->getSprite().getLocalBounds().width / 2 < game->getWindowWidth() - game->getWindowWidth()) { // this will be 0
direction = -(direction);
// Time stamp over here for network calculations
//std::cout << "Direction inside if statement " << direction << std::endl;
// another for loop move all down by yOffset
for (std::vector<Enemy>::iterator i = enemyList->begin(); i != enemyList->end(); ++i) {
i->getSprite().setPosition(i->getSprite().getPosition().x, i->getSprite().getPosition().y + yOffset);
//enemy.move(0.f, enemy.getPosition().y + yOffset);
}
//enemy.setPosition(enemy.getPosition().x, enemy.getPosition().y + yOffset); // y axis is inverted in SFML
//return 0; // move to separate method
}
// This takes care of everything on both sides
if ((i->getSprite().getPosition().y > game->getWindowHeight() - mPlayer.getLocalBounds().height)) {
//removeEnemy(enemy);
// ALIVE is false, then draw based on whether alien is alive or not
std::cout << "Everyone should be dead game over!" << std::endl;
return 0; // change state to paused
}
}
}
updateStatistics(deltaTime, mStatisticsText);
// ** Start of if server
if (userChoice == 's'){
for (std::vector<Enemy>::iterator i = enemyList->begin(); i != enemyList->end(); ++i) {
// Test player and alien collision
if (CollisionManager::collidesWith(mPlayer, *i) && i->isAlive()) { // *i it points to the enemy, and will give me an enemy
std::cout << "You're dead!" << std::endl;
mPlayerIsAlive = false;
return 0; //-> I'm dead gameOver = true;
}
// Test collision between bullets and aliens
if (CollisionManager::collidesWith(bullet, *i) && i->isAlive())
{
i->kill();
//enemyList->erase(i); // issue here, we are trying to do this in order to get rid of rendering problems .. shit just move it out of way
//i->setLocation(700.f, 10.f);
bullet.kill();
bullet.setLocation(700.f, 0.f); // temporary solution for dealing with not actually killing but stop drawing bullet
// without above line, the bullet will not be drawn but still exist on screen
}
/*
v.erase(std::remove_if(v.begin(), v.end(), //erase returns it, so it = erase
[](int i) { return i < 10; }), v.end());
*/
}
}
// ** end of if server
int deadEnemies = 0;
for (std::vector<Enemy>::iterator i = enemyList->begin(); i != enemyList->end(); ++i) {
if (!(i->isAlive())) {
deadEnemies++;
}
if (deadEnemies >= NUMBER_OF_ALIENS) {
std::cout << "You win!" << std::endl;
return 0; // Set state win
}
}
//std::cout << "Total Dead Enemies - " << deadEnemies << std::endl;
//for (Enemy& enemy : enemyList) {
// if (enemy.isAlive()) {
// enemy.draw(window);
// }
//}
//enemy.render(alienArray, mWindow);
/*
for (int i = 0; i < mEnemies.size(); i++)
{
sf::Sprite temp = mEnemies.at(i);
mWindow.draw(temp);
}
*/
// Render All
// Remember draw order is important
window.clear();
window.draw(mBackground);
window.draw(mPlayer);
window.draw(mStatisticsText);
for (std::vector<Enemy>::iterator i = enemyList->begin(); i != enemyList->end(); ++i) {
if (i->isAlive()) {
i->draw(window);
//std::cout << "Enemy " << i->getID() << " position - " << i->getLocation().x << " " << i->getLocation().y << std::endl;
}
}
if (bullet.isAlive()) // && !gameOver // for game state and win / lose screen representation
{
//draw bullet
bullet.draw(window);
//move bullet
bullet.getSprite().move(0.f, -20);
}
//test collision with bullet and boundary
if (bullet.getSprite().getPosition().y < 0)
bullet.kill();
window.display();
} // end of main second if
}
return 0;
}
Related
I am currently working on a project where I need to read the difference between the minimum
and maximum markers of a Rhode and Schwarz FSC3 device. I am using NIVisa to connect to the
device over an ethernet connection and pass SCPI commands to it.
The operating system I am using is Ubuntu 20.04.
The code I am using is as shown below -
// Gloabl Visa ResouceManager reference
VisaResourceManager rm;
/**
* Custom type that defines a relation between the deviceIP string
* and a session_t within the Resource Manager
**/
typedef std::map<std::string, VisaResourceManager::session_t> deviceIpSessionMap_t;
deviceIpSessionMap_t devices;
bool device_exists(const std::string deviceIP)
{
deviceIpSessionMap_t::iterator iter = devices.find(deviceIP);
if (iter != devices.end())
{
return true;
}
else
{
return false;
}
}
// --------------------------------------------------------
// Struct defining the ReadConfiguration which is required
// to get readings from the device
// --------------------------------------------------------
struct ReadConfig
{
// The center frequency for the reading
std::string center;
// The span for the reading
std::string span;
// rbw value for reading
std::string rbw;
// vbw value for reading
std::string vbw;
// IP address of the X-pole device
std::string ip;
};
/**
* This function retrieves values from the
* spectrum analyser
*/
int getValueFromDevice(ReadConfig &conf)
{
try
{
//-----------------------------------------------------------
// Set SCPI commands strings
//-----------------------------------------------------------
std::string DeviceIP = "TCPIP::" + conf.ip;
std::string CenterFreq = "FREQ:CENT " + conf.center + "KHz";
std::string Span = "FREQ:SPAN " + conf.span + "KHz";
std::string Rbw = "BAND " + conf.rbw + "KHz";
std::string Vbw = "BAND:VID " + conf.vbw + "Hz";
// logConf(DeviceIP, CenterFreq, Span, Rbw, Vbw);
// Check if the session already exists and reuse it
// if available. If not create a new session and add it
// to the deviceIP to session map
VisaResourceManager::session_t session;
if (device_exists(conf.ip))
{
session = devices[conf.ip];
}
else
{
session = rm.connect(DeviceIP);
devices[conf.ip] = session;
}
session->write("*RST;*CLS\n"); // Reset and clear the device
//-----------------------------------------------------------
// ID the device:
//-----------------------------------------------------------
// session->write("*IDN?");
// std::cout << "status byte = " << session->readSTB() << std::endl;
// std::cout << "*IDN? = " << session->read() << std::endl;
// std::cout << "status byte = " << session->readSTB() << std::endl;
//-----------------------------------------------------------
// Basic Device Settings:
//-----------------------------------------------------------
session->write(CenterFreq); // Setting the center frequency
session->write(Span); // Setting the span
session->write(Rbw); // Setting the RBW
session->write(Vbw); // Setting the VBW
session->write("CALC:DELT1 OFF"); // Removes/resets MARK2 position
session->write("CALC:DELT2 OFF"); // Removes/resets MARK2 position
session->write("CALC:MARK1 OFF"); // Removes/resets MARK1 position
session->write("CALC:MARK2 OFF"); // Removes/resets MARK1 position
session->write("*OPC?"); // Using *OPC? waits until all the instrument settings are finished
//-----------------------------------------------------------
// Setting marker to min and reading the marker Y
//-----------------------------------------------------------
session->write("CALC:DELT1:MIN");
session->write("CALC:MARK1:MAX");
session->write("*OPC?");
session->write("CALC:DELT1:Y?\n");
std::string min = session->read();
int iMin = abs(std::stoi(min));
// std::cout << "iMin : " << iMin << '\n';
//-----------------------------------------------------------
// Disconnect from Device
//-----------------------------------------------------------
// rm.disconnect(session);
return iMin;
}
catch (VisaException ex)
{
std::cout << ex.what() << " -- " << VisaResourceManager::statusDescription(ex.getErrorCode()) << std::endl;
return 0;
}
catch (...){
std::cout << "Encountered an unknown error" << std::endl;
return 0;
}
}
This usually shows me the correct values, but when the analyzer has been on for a while it starts
malfunctioning/reporting absurd values that are too high.
What am I doing wrong ?
Please forgive me if I'm missing something simple, this is my first time doing anything with messaging and I inherited this codebase from someone else.
I am trying to send a message from a windows machine with an IP of 10.10.10.200 to an Ubuntu machine with an IP of 10.10.10.15.
I got the following result when running TCPView from the Windows machine, which makes me suspect that the problem lies in the Ubuntu machine. If I'm reading that right, then my app on the windows machine has created a connection on port 5556 which is what it is supposed to do. In case I'm wrong, I'll include the windows code too.
my_app.exe 5436 TCP MY_COMPUTER 5556 MY_COMPUTER 0 LISTENING
Windows app code:
void
NetworkManager::initializePublisher()
{
globalContext = zmq_ctx_new();
globalPublisher = zmq_socket(globalContext, ZMQ_PUB);
string protocol = "tcp://*:";
string portNumber = PUBLISHING_PORT; //5556
string address = protocol + portNumber;
char *address_ptr = new char[address.size() + 1];
strncpy_s(address_ptr, address.size() + 1, address.c_str(), address.size());
int bind_res = zmq_bind(globalPublisher, address_ptr);
if (bind_res != 0)
{
cerr << "FATAL: couldn't bind to port[" << portNumber << "] and protocol [" << protocol << "]" << endl;
}
cout << " Connection: " << address << endl;
}
void
NetworkManager::publishMessage(MESSAGE msgToSend)
{
// Get the size of the message to be sent
int sizeOfMessageToSend = MSG_MAX_SIZE;//sizeof(msgToSend);
// Copy IDVS message to buffer
char buffToSend[MSG_MAX_SIZE] = "";
// Pack the message id
size_t indexOfId = MSG_ID_SIZE + 1;
size_t indexOfName = MSG_NAME_SIZE + 1;
size_t indexOfdata = MSG_DATABUFFER_SIZE + 1;
memcpy(buffToSend, msgToSend.get_msg_id(), indexOfId - 1);
// Pack the message name
memcpy(buffToSend + indexOfId, msgToSend.get_msg_name(), indexOfName - 1);
// Pack the data buffer
memcpy(buffToSend + indexOfId + indexOfName, msgToSend.get_msg_data(), indexOfdata - 1);
// Send message
int sizeOfSentMessage = zmq_send(globalPublisher, buffToSend, MSG_MAX_SIZE, ZMQ_DONTWAIT);
getSubscriptionConnectionError();
// If message size doesn't match, we have an issue, otherwise, we are good
if (sizeOfSentMessage != sizeOfMessageToSend)
{
int errorCode = zmq_errno();
cerr << "FATAL: couldn't not send message." << endl;
cerr << "ERROR: " << errorCode << endl;
}
}
I can include more of this side's code if you think it's needed, but the error is popping up on the Ubuntu side, so I'm going to focus there.
The problem is when I call zmq_recv it returns -1 and when I check zmq_errno I get EAGAIN (Non-blocking mode was requested and no messages are available at the moment.) I also checked with netstat and I didn't see anything on port 5556
First is the function to connect to the publisher, then the function to get data, followed by main.
Ubuntu side code:
void
*connectoToPublisher()
{
void *context = zmq_ctx_new();
void *subscriber = zmq_socket(context, ZMQ_SUB);
string protocol = "tcp://";
string ipAddress = PUB_IP; //10.10.10.15
string portNumber = PUB_PORT; // 5556
string address = protocol + ipAddress + ":" + portNumber;
cout << "Address: " << address << endl;
char *address_ptr = new char[address.size() + 1];
strcpy(address_ptr, address.c_str());
// ------ Connect to Publisher ------
bool isConnectionEstablished = false;
int connectionStatus;
while (isConnectionEstablished == false)
{
connectionStatus = zmq_connect(subscriber, address_ptr);
switch (connectionStatus)
{
case 0: //we are good.
cout << "Connection Established!" << endl;
isConnectionEstablished = true;
break;
case -1:
isConnectionEstablished = false;
cout << "Connection Failed!" << endl;
getSubscriptionConnectionError();
cout << "Trying again in 5 seconds..." << endl;
break;
default:
cout << "Hit default connecting to publisher!" << endl;
break;
}
if (isConnectionEstablished == true)
{
break;
}
sleep(5); // Try again
}
// by the time we get here we should have connected to the pub
return subscriber;
}
static void *
getData(void *subscriber)
{
const char *filter = ""; // Get all messages
int subFilterResult = zmq_setsockopt(subscriber, ZMQ_SUBSCRIBE, filter, strlen(filter));
// ------ Get in main loop ------
while (1)
{
//get messages from publisher
char bufferReceived[MSG_MAX_SIZE] = "";
size_t expected_messageSize = sizeof(bufferReceived);
int actual_messageSize = zmq_recv(subscriber, bufferReceived, MSG_MAX_SIZE, ZMQ_DONTWAIT);
if (expected_messageSize == actual_messageSize)
{
MESSAGE msg = getMessage(bufferReceived); //Uses memcpy to copy id, name, and data strutct data from buffer into struct of MESSAGE
if (strcmp(msg.get_msg_id(), "IDXY_00000") == 0)
{
DATA = getData(msg); //Uses memcpy to copy data from buffer into struct of DATA
}
} else
{
// Something went wrong
getReceivedError(); //This just calls zmq_errno and cout the error
}
usleep(1);
}
}
int main (int argc, char*argv[])
{
//Doing some stuff...
void *subscriber_socket = connectoToHeadTrackerPublisher();
// Initialize Mux Lock
pthread_mutex_init(&receiverMutex, NULL);
// Initializing some variables...
// Launch Thread to get updates from windows machine
pthread_t publisherThread;
pthread_create(&publisherThread,
NULL, getData, subscriber_socket);
// UI stuff
zmq_close(subscriber_socket);
return 0;
}
If you cannot provide a solution, then I will accept identifying the problem as a solution. My main issue is that I don't have the knowledge or experience with messaging or networking to correctly identify the issue. Typically if I know what is wrong, I can fix it.
Ok, this has nothing to do with signalling / messaging framework
Your Ubuntu code instructs the ZeroMQ Context()-instance engine to create a new SUB-socket instance and next the code insist this socket to try to _connect() ( to setup a tcp:// transport-class connection towards the peering counterparty ) to "opposite" access-point, sitting on an address of the Ubuntu localhost:port# that was setup as 10.10.10.15:5556, while the intended PUB-side archetype access-point actually lives not on this Ubuntu machine, but on another, Windows host, IP:port# of which is 10.10.10.200:5556
This seems to be the root-cause of the problem, so change it accordingly to match the physical layout and you may get the toys work.
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.
I have two applications which work like a TCP client/server.
First application is the client which uses OpenCV to detect and send commands via TCP to the Server which controls a mobile robot.
My applications work well if I'm in my developing computer, but when I test it in real world with my robot, i realize that I have some delays with the data exchanged between client and server.
This happens because the computer where iItest the applications is a little bit slow compared to my developing computer which is faster and give no problems. In real world case, server doesn't receive packets from client in real time so it execute the operations with a delay.
So, the problem is when the client loose the detection and send commands to the server in order to stop it. The server receives packets with a delay so when clients sends stop (heading = 0, distance = 0, nodetection) server doesn't receive the command immediately because it is receiving previous command packets and so it stop only after few meters.
I'd like to find a solution in order to stop immediately the server and discard all the packages about the moving information because they are useless if the robot has to stop.
In order to stop the robot I send a nodetecting package which unfortunately is not received in real time so the robot continue to move for a while.
(I'm doing this test on the same machine, so I connect on localhost)
At the moment, client uses this code:
while (key_mode!='q')
{
//wait and error processing
context.WaitAnyUpdateAll();
// obtain al the metadata image,depthmap and scene
Mat frame = getImageFromKinect();
// do detection and tracking
switch(mode)
{
..
case ROBOT_CONTROL:
{
// Connect to the server
using boost::asio::ip::tcp;
boost::asio::io_service io_service;
tcp::resolver resolver(io_service);
tcp::resolver::query query(tcp::v4(), server, boost::lexical_cast<string>(porta));
tcp::resolver::iterator iterator = resolver.resolve(query);
tcp::socket s(io_service);
try
{
s.connect(*iterator);
}
catch (boost::system::system_error const& e)
{
std::cout << "Warning: could not connect to the server\n" << e.what() << "\nPossible Solution: try to check is Server is UP\n" << std::endl;
}
..
..
float delta = heading - last_heading;
if (!is_equal(delta, 0.0)){
// heading_data = send_heading + token + boost::lexical_cast<std::string>(delta);
// heading_length = strlen(heading_data.c_str());
try
{
// boost::asio::write(s, boost::asio::buffer(heading_data, heading_length));
}
catch (boost::system::system_error const& e)
{
std::cout << "Warning: could not send commands : " << e.what() << std::endl;
}
}
last_heading = heading; // store current for next subtraction
#endif
#if 1
heading_scalato = heading / 3.0;
heading_data = send_heading + token + boost::lexical_cast<std::string>(heading_scalato);
heading_length = strlen(heading_data.c_str());
try
{
boost::asio::write(s, boost::asio::buffer(heading_data, heading_length));
}
catch (boost::system::system_error const& e)
{
std::cout << "Warning: could not send commands : " << e.what() << std::endl;
}
#endif
distance_data = send_distance + token + boost::lexical_cast<std::string>(distance);
distance_length = strlen(distance_data.c_str());
try
{
boost::asio::write(s, boost::asio::buffer(distance_data, distance_length));
}
catch (boost::system::system_error const& e)
{
std::cout << "Warning: could not connect : " << e.what() << std::endl;
}
..
..
// if it has to stop:
else
{
// stop rover
//control.setHeading(0.0);
//control.setDistance(0.0);
float heading = 0.0;
float distance = 0.0;
heading_data = send_heading + token + boost::lexical_cast<std::string>(heading);
distance_data = send_distance + token + boost::lexical_cast<std::string>(distance);
heading_length = heading_data.size();//strlen(heading_data.c_str());
distance_length = strlen(distance_data.c_str());
try
{
boost::asio::write(s, boost::asio::buffer(heading_data, heading_length));
boost::asio::write(s, boost::asio::buffer(distance_data, distance_length));
}
catch (boost::system::system_error const& e)
{
std::cout << "Warning: could not send commands : " << e.what() << std::endl;
}
// write info on image
char text[100];
sprintf(text,"ROBOT CONTROL: No detection");
putText(hogResultFrame,text,Point(4,89),FONT_HERSHEY_PLAIN,1,Scalar(0,0,0));
putText(hogResultFrame,text,Point(5,90),FONT_HERSHEY_PLAIN,1,Scalar(100,100,255));
nodetection_length = nodetection.size();
try
{
boost::asio::write(s, boost::asio::buffer(nodetection, nodetection_length));
}
catch (boost::system::system_error const& e)
{
std::cout << "Warning: could not send commands : " << e.what() << std::endl;
}
In server, i use:
void* runThread(void*)
{
while(Aria::getRunning())
{
if(start_routine){
if(temp_heading < 0.0){
printf("\n\nStarting Discovering routine, then sleeping 3 seconds.\a\n\n");
robot.setRotVel(5.0);
ArUtil::sleep(3000);
temp_heading = -1;
}
else if(temp_heading >= 0.0) {
printf("\n\nStarting Clockwise Discovering routine, then sleeping 3 seconds.\a\n\n");
robot.setRotVel(-5.0);
ArUtil::sleep(3000);
temp_heading = 1;
}
}
if( !flag_heading && !flag_distance)
{
myMutex.lock();
temp_heading=m_heading;
temp_distance=m_distance;
myMutex.unlock();
if (is_equal(temp_heading, 0.0)){
robot.setRotVel(0.0);
}
else robot.setRotVel(-ArMath::radToDeg(temp_heading));
if(temp_distance <= distanza_minima || is_equal(temp_distance, 0.0))
robot.setVel(0.0);
else
robot.setVel(float(temp_distance/20));
printf("runThread:: heading= %f distance = %f rob_vel = %f rob_rot_vel = %f\n",ArMath::radToDeg(temp_heading),temp_distance, robot.getVel(),robot.getRotVel());
flag_heading = true;
flag_distance = true;
start_routine = false;
}
ArUtil::sleep(100);
}
}
DataLine GetValueFromLine(const std::string& sData) {
std::string sName, sInteger;
std::stringstream ss;
DataLine Result;
size_t sz = sData.find('#');
sName = sData.substr(0,sz); // Just in case you need it later
Result.sName = sName;
sInteger = sData.substr(sz + 1,sData.length() - sz);
ss.str(sInteger);
ss >> Result.nNumber;
if (ss.fail()) {
// something went wrong, probably not an integer
}
return Result;
}
void session(socket_ptr sock)
{
try
{
for (;;)
{
char data[max_length];
boost::system::error_code error;
size_t length = sock->read_some(boost::asio::buffer(data), error);
data[length] = 0;
if (error == boost::asio::error::eof)
break; // Connection closed cleanly by peer.
else if (error)
throw boost::system::system_error(error); // Some other error.
output = GetValueFromLine(data);
std::cout << "*******************\n";
comando = output.sName;
valore = output.nNumber;
if (output.sName == "nodetection"){
start_routine = true;
std::cout << "\nSto ricevendo: " << output.sName;
}
else if (output.sName == "heading"){
start_routine = false;
control.setHeading(output.nNumber);
std::cout << "\nSto ricevendo: " << output.sName << "e heading: " << output.nNumber;
}
else if (output.sName == "distance"){
start_routine = false;
control.setDistance(output.nNumber);
std::cout << "\nSto ricevendo: " << output.sName << "e distance: " << output.nNumber;
}
// boost::asio::write(*sock, boost::asio::buffer(data, length));
}
}
catch (std::exception& e)
{
std::cerr << "Exception in thread: " << e.what() << "\n";
}
}
void server(boost::asio::io_service& io_service, short port)
{
tcp::acceptor a(io_service, tcp::endpoint(tcp::v4(), port));
for (;;)
{
socket_ptr sock(new tcp::socket(io_service));
a.accept(*sock);
boost::thread t(boost::bind(session, sock));
}
}
int main(int argc, char **argv)
{
// control server initialitation..
....
boost::asio::io_service io_service;
server(io_service, porta);
return 0;
}
I was thinking to force the client to close the TCP connection when it reaches a no detecting condition in order to force the server to reject the pending packets, but how I can do this?
How to destroy s pointer in boost?
Are there any other solutions?
If I close the connection, does the server reject the pending packets?
As I understand your problem, you have a message that you intend your application to abandon all current processing and flush the input queue. However, because the application is busy receiving and processing previous messages, it does not receive the abandon and flush message until all previous messages are processed - which makes abandon and flush a no operation
IMHO you need to design and code a multithreaded application.
One thread, as light weight as possible, reads the incoming messages as fast as possible and quickly checks for the abandon and flush message. If the message is OK, then it is added to a queue and the next message is checked.
The second thread pulls the messages from the queue where the messages are stored by the first thread and processes them, perhaps taking a long time to do so. From time time it checks for an abandon and flush signal from the first thread.
Another approach to consider: the application that sends the messages maintains the queue. When the application which receives the messages finishes processing a message, it sends a request for the next message. The sender only sends messages when requested. If the abandon and flush condition arises, the application sending the messages looks after this. The application receiving the messages only has to deal with one at a time. This approach greatly simplify the message receiver, at the cost of complexity in the sending application, a more elaborate communications protocol and, possibly, a reduction in maximum throughput.