Here's my function's I'm using to do the send() and recv()'ing.
bool Socket::send( const std::string s ) const noexcept {
int status = ::send ( m_sock, s.c_str(), s.size(), MSG_NOSIGNAL );
if(status == -1)
return false;
else
return true;
}
int Socket::recv( std::string& s ) const noexcept {
char buf [ MAXRECV + 1 ];
s = "";
memset ( buf, 0, MAXRECV + 1 );
int status = ::recv ( m_sock, buf, MAXRECV, 0 );
if ( status == -1 ) {
std::cout << "status == -1 errno == " << errno << " in Socket::recv\n";
return 0;
}
else if ( status == 0 ) {
return 0;
}
else {
s = buf;
return status;
}
}
The server accepts a client then calls send() with a test message.
std::cout << server.send(s) << std::endl;
Which is always returning false (writing 0 to cout). The client recv()'s by doing the following.
std::string s;
int i = 0;
while (s.empty()) {
i = k.read(s);
}
And it never leaves this loop. Both are set blocking by calling this function with false sent as the parameter.
void Socket::set_non_blocking ( const bool b ) noexcept {
int opts;
opts = fcntl(m_sock, F_GETFL);
if(opts < 0)
return;
if( b )
opts = ( opts | O_NONBLOCK );
else
opts = ( opts & ~O_NONBLOCK );
fcntl( m_sock, F_SETFL,opts );
}
Related
I am trying to communicate via SPI in Linux user space using the spidev driver. Physically, this SPI interface is a 4 bus (SCLK, CS, MOSI, MISO).
For this purpose I have attached to my dts the relevant node in the corresponding chip select. Once Linux boots I have the device available.
Using c++ and according to the spi-test example I have treated it as a character device, so I open it, require a full duplex transfer and close it (with the relevant calls to open, ioctl/spi_ioc_transfer and close primitives). The set bus configuration is 8 bits per word, at a maximum frequency of 1.29 MHz and in mode 0 (in other words, CPHA=CPOL=0).
Then, using the oscilloscope I see how my request is present in the MOSI, but I don't see any type of response in the MISO. What am I missing?
In case it helps, I see that the clock signal drops the level immediately after sending the data through the MOSI, so it makes sense that I am not getting any data in the MISO.
In addition, I have checked that the maximum frequency as well as all settings have been set correctly. I also have a baremetal application using the bus correctly, so any HW failure is ruled out.
Spi.hpp
#pragma once
#include <string>
#include <sys/ioctl.h>
#include <cstring>
#include <fcntl.h>
#include <unistd.h>
#include <linux/spi/spidev.h>
#include <drivers/common/literals.hpp>
#include <drivers/common/Utils.hpp>
namespace interfaces
{
namespace spi
{
namespace lnx
{
enum class Mode
{
ZERO = SPI_MODE_0,
ONE = SPI_MODE_1,
TWO = SPI_MODE_2,
THREE = SPI_MODE_3
};
enum class Bpw
{
BITS_8 = 8,
BITS_16 = 16
};
enum class State
{
CLOSED,
OPEN
};
class Spi
{
public:
explicit Spi( const std::string& aDevice )
{
memset( &theFrame, 0, sizeof( struct spi_ioc_transfer ) );
theFileDescriptor = -1;
theState = State::CLOSED;
theFrame[0].delay_usecs = 0;
// Do keep CS activated
theFrame[0].cs_change = 0;
if( setDevice( aDevice ) != ErrorCode::OPERATION_SUCESS )
{
trace( DEBUG_GENERAL, "error setting device\r\n" );
}
if( setMode( Mode::ZERO ) != ErrorCode::OPERATION_SUCESS )
{
trace( DEBUG_GENERAL, "error setting mode\r\n" );
}
if( setBitsPerWord( Bpw::BITS_8 ) != ErrorCode::OPERATION_SUCESS )
{
trace( DEBUG_GENERAL, "error setting bpw\r\n" );
}
// Set the SPI device pre-scaler to divide by 128
// (SPI_CLK_FREQ_HZ = 166 MHz) to transfer below 2MHz clock rate.
if( setMaxSpeedHz( ( 166 / 128.0 ) * 1000000 ) != ErrorCode::OPERATION_SUCESS )
{
trace( DEBUG_GENERAL, "error setting maximum speed\r\n" );
}
}
virtual ~Spi( void )
{
if( theState != State::CLOSED && theFileDescriptor != -1 )
{
if( close() != ErrorCode::OPERATION_SUCESS )
{
trace(
DEBUG_GENERAL,
"error closing device %s\r\n",
theDevice.c_str() );
}
}
}
Spi( const Spi& ) = delete;
Spi& operator=( const Spi& ) = delete;
inline std::string getDevice( void )
{
return this->theDevice;
}
inline ErrorCode setDevice( const std::string& aDevice )
{
ErrorCode anErrorCode = ErrorCode::CANT_CONFIGURE_INTERFACE;
if( theState != State::CLOSED && theFileDescriptor != -1 )
{
if ( close() != ErrorCode::OPERATION_SUCESS )
{
goto exit;
}
}
this->theDevice = aDevice;
anErrorCode = open();
exit:
return std::move( anErrorCode );
}
inline ErrorCode setMode( const Mode& aMode )
{
ErrorCode anErrorCode = ErrorCode::CANT_CONFIGURE_INTERFACE;
if( theState != State::CLOSED && theFileDescriptor != -1 )
{
if( ::ioctl(
theFileDescriptor,
SPI_IOC_WR_MODE32,
&aMode ) != -1 )
{
auto aResult = getMode();
if( aResult.first == ErrorCode::OPERATION_SUCESS &&
aResult.second == aMode )
{
anErrorCode = ErrorCode::OPERATION_SUCESS;
trace(
DEBUG_INFO,
"mode set to %d\r\n",
aMode );
}
else
{
close();
trace(
DEBUG_GENERAL,
"mode incongruence\r\n" );
}
}
else
{
close();
trace(
DEBUG_GENERAL,
"failed setting mode\r\n" );
}
}
return std::move( anErrorCode );
}
inline std::pair<ErrorCode, Mode> getMode( void )
{
Mode aMode;
ErrorCode anErrorCode = ErrorCode::CANT_CONFIGURE_INTERFACE;
if( theState != State::CLOSED && theFileDescriptor != -1 )
{
if( ::ioctl(
theFileDescriptor,
SPI_IOC_RD_MODE32,
&aMode ) != -1 )
{
anErrorCode = ErrorCode::OPERATION_SUCESS;
}
else
{
trace(
DEBUG_GENERAL,
"failed reading mode\r\n" );
}
}
return std::make_pair( anErrorCode, aMode );
}
inline ErrorCode setBitsPerWord( const Bpw& aBitsPerWord )
{
ErrorCode anErrorCode = ErrorCode::CANT_CONFIGURE_INTERFACE;
if( theState != State::CLOSED && theFileDescriptor != -1 )
{
if( ::ioctl(
theFileDescriptor,
SPI_IOC_WR_BITS_PER_WORD,
&aBitsPerWord ) != -1 )
{
auto aResult = getBitsPerWord();
if( aResult.first == ErrorCode::OPERATION_SUCESS &&
aResult.second == aBitsPerWord )
{
anErrorCode = ErrorCode::OPERATION_SUCESS;
theFrame[0].bits_per_word =
static_cast<uint8_t>( aBitsPerWord );
trace(
DEBUG_INFO,
"bpw set to %d\r\n",
aBitsPerWord );
}
else
{
close();
trace(
DEBUG_GENERAL,
"bpw incongruence\r\n" );
}
}
else
{
close();
trace(
DEBUG_GENERAL,
"failed setting bpw\r\n" );
}
}
return std::move( anErrorCode );
}
inline std::pair<ErrorCode, Bpw> getBitsPerWord( void )
{
uint8_t aBitsPerWord;
ErrorCode anErrorCode = ErrorCode::CANT_CONFIGURE_INTERFACE;
if( theState != State::CLOSED && theFileDescriptor != -1 )
{
if( ::ioctl(
theFileDescriptor,
SPI_IOC_RD_BITS_PER_WORD,
&aBitsPerWord ) != -1 )
{
anErrorCode = ErrorCode::OPERATION_SUCESS;
}
else
{
trace(
DEBUG_GENERAL,
"failed reading bpw\r\n" );
}
}
return std::make_pair(
anErrorCode,
static_cast<Bpw>( aBitsPerWord ) );
}
inline ErrorCode setMaxSpeedHz( const uint32_t aSpeed )
{
ErrorCode anErrorCode = ErrorCode::CANT_CONFIGURE_INTERFACE;
if( theState != State::CLOSED && theFileDescriptor != -1 )
{
if( ::ioctl(
theFileDescriptor,
SPI_IOC_WR_MAX_SPEED_HZ,
&aSpeed ) != -1 )
{
auto aResult = getMaxSpeedHz();
if( aResult.first == ErrorCode::OPERATION_SUCESS &&
aResult.second == aSpeed )
{
anErrorCode = ErrorCode::OPERATION_SUCESS;
theFrame[0].speed_hz = aSpeed;
trace(
DEBUG_INFO,
"maximum speed set to %d Hz\r\n",
aSpeed );
}
else
{
close();
trace(
DEBUG_GENERAL,
"maximum speed incongruence\r\n" );
}
}
else
{
close();
trace(
DEBUG_GENERAL,
"failed setting maximum speed\r\n" );
}
}
return std::move( anErrorCode );
}
inline std::pair<ErrorCode, uint32_t> getMaxSpeedHz( void )
{
uint32_t aSpeed;
ErrorCode anErrorCode = ErrorCode::CANT_CONFIGURE_INTERFACE;
if( theState != State::CLOSED && theFileDescriptor != -1 )
{
if( ::ioctl(
theFileDescriptor,
SPI_IOC_RD_MAX_SPEED_HZ,
&aSpeed ) != -1 )
{
anErrorCode = ErrorCode::OPERATION_SUCESS;
}
else
{
trace(
DEBUG_GENERAL,
"failed setting maximum speed\r\n" );
}
}
return std::make_pair( anErrorCode, aSpeed );
}
std::pair<ErrorCode, std::vector<uint8_t>> transfer(
const std::vector<uint8_t>& aBuffer,
uint32_t aLenght );
std::pair<ErrorCode, std::vector<uint8_t>>
read( uint32_t aLenght );
ErrorCode write( const std::vector<uint8_t>& aBuffer );
private:
ErrorCode open( void );
ErrorCode close( void );
int32_t theFileDescriptor;
std::string theDevice;
struct spi_ioc_transfer theFrame[1];
State theState;
};
} // end namespace lnx
} // end namespace spi
} // end namespace interfaces
Spi.cpp
#include <interfaces/lnx/Spi.hpp>
namespace interfaces
{
namespace spi
{
namespace lnx
{
ErrorCode Spi::open( void )
{
ErrorCode anErrorCode = ErrorCode::CANT_OPEN_DEV;
if( !theDevice.empty() )
{
theFileDescriptor = ::open(
theDevice.c_str(), O_SYNC | O_RDWR );
if( theFileDescriptor != -1 )
{
anErrorCode = ErrorCode::OPERATION_SUCESS;
theState = State::OPEN;
trace(
DEBUG_INFO,
"device %s opened\r\n",
theDevice.c_str() );
}
else
{
trace(
DEBUG_GENERAL,
"device %s could not be opened\r\n",
theDevice.c_str() );
}
}
return std::move( anErrorCode );
}
ErrorCode Spi::close( void )
{
ErrorCode anErrorCode = ErrorCode::CANT_CLOSE_DEV;
if( theState != State::CLOSED && theFileDescriptor != -1 )
{
if( ::close( theFileDescriptor ) == 0 )
{
anErrorCode = ErrorCode::OPERATION_SUCESS;
theFileDescriptor = -1;
theState = State::CLOSED;
trace(
DEBUG_INFO,
"device %s closed\r\n",
theDevice.c_str() );
}
}
return std::move( anErrorCode );
}
std::pair<ErrorCode, std::vector<uint8_t>> Spi::transfer(
const std::vector<uint8_t>& aRequest, uint32_t aLenght )
{
std::vector<uint8_t> aBuffer( aLenght, 0xFF );
ErrorCode anErrorCode = ErrorCode::CANT_READ_FROM_INTERFACE;
if( aRequest.empty() || aLenght <= 0 )
{
trace(
DEBUG_GENERAL,
"provided arguments not complains the conditions\r\n" );
goto exit;
}
if( theState != State::CLOSED && theFileDescriptor != -1 )
{
theFrame[0].tx_buf = reinterpret_cast<uintptr_t>( aRequest.data() );
theFrame[0].rx_buf = reinterpret_cast<uintptr_t>( &aBuffer[0] );
// Length of the command to write/read
theFrame[0].len = aLenght;
if( ::ioctl(
theFileDescriptor,
SPI_IOC_MESSAGE( 1 ),
&theFrame ) < 1 )
{
trace(
DEBUG_GENERAL,
"failed doing full duplex transfer\r\n" );
}
else
{
anErrorCode = ErrorCode::OPERATION_SUCESS;
vprint( aBuffer.data(), aBuffer.size() );
}
}
exit:
return std::make_pair( anErrorCode, aBuffer );
}
std::pair<ErrorCode, std::vector<uint8_t>>
Spi::read( uint32_t aLenght )
{
std::vector<uint8_t> aBuffer( aLenght, 0xFF );
ErrorCode anErrorCode = ErrorCode::CANT_READ_FROM_INTERFACE;
if( aLenght <= 0 )
{
trace(
DEBUG_GENERAL,
"provided arguments not complains the conditions\r\n" );
goto exit;
}
if( theState != State::CLOSED && theFileDescriptor != -1 )
{
theFrame[0].tx_buf = static_cast<uintptr_t>( NULL );
theFrame[0].rx_buf = reinterpret_cast<uintptr_t>( &aBuffer[0] );
theFrame[0].len = aLenght;
if( ::ioctl(
theFileDescriptor,
SPI_IOC_MESSAGE( 1 ),
&theFrame ) < 1 )
{
trace(
DEBUG_GENERAL,
"failed receiving message\r\n" );
}
else
{
anErrorCode = ErrorCode::OPERATION_SUCESS;
vprint( aBuffer.data(), aBuffer.size() );
}
}
exit:
return std::make_pair( anErrorCode, aBuffer );
}
ErrorCode Spi::write( const std::vector<uint8_t>& aBuffer )
{
ErrorCode anErrorCode = ErrorCode::CANT_WRITE_TO_INTERFACE;
if( aBuffer.empty() )
{
trace(
DEBUG_GENERAL,
"provided arguments not complains the conditions\r\n" );
goto exit;
}
if( theState != State::CLOSED && theFileDescriptor != -1 )
{
theFrame[0].tx_buf = reinterpret_cast<uintptr_t>( aBuffer.data() );
theFrame[0].rx_buf = static_cast<uintptr_t>( NULL );
theFrame[0].len = aBuffer.size();
if( ::ioctl(
theFileDescriptor,
SPI_IOC_MESSAGE( 1 ),
&theFrame ) < 1 )
{
trace(
DEBUG_GENERAL,
"failed sending message\r\n" );
}
else
{
anErrorCode = ErrorCode::OPERATION_SUCESS;
vprint( aBuffer.data(), aBuffer.size() );
}
}
exit:
return std::move( anErrorCode );
}
} // end namespace lnx
} // end namespace spi
} // end namespace interfaces
myboard.dts
...
&spi0 {
num-cs = <2>;
spi-cpol = <0>;
spi-cpha = <0>;
status = "okay";
spidev#0 {
compatible = "xlnx,spidev";
reg = <0>;
spi-max-frequency = <1296875>;
};
};
...
I use the transfer function to achieve my purpose over lnx-xlnx 4.14
I am facing a very odd problem in my C++ project. I have a client and a server that send char arrays of data to each other. While the initial sending/reception works well (see: Client.firstConnection() in MVE), in the following messages the client appears to drop all but the 12th through 15th chars in the array as it sends it to the server. The server then only receives this partially filled array.
Example:
I want to send the buffer 4_user53:6134;*0/ from the client to the server.
Before sending, my variable, when printed character by character, shows 4_user53:6134;*0/
On the server side, the buffer is received but when printed character by character, shows 4;*0
On the client side, immediately after sending the buffer, the variable, when printed character by character, shows 4;*0.
I do not know why these characters are disappearing. All advice is appreciated, thank you.
Please find the minimal viable example below, I have done my best to annotate points of interest as well as illustrate the results of the print functions.
MVE:
Compiled with VS 2017, C++ 11.
//Header file information
// all imports:
#include <cstdlib>
#include <iostream>
#include <map>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <random>
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
// Global vars
#define DEFAULT_BUFLEN 512
#define DEFAULT_PORT "6000"
#define MAX_CONS 1 //Total No of Clients allowed
//Client
class Client{
bool Client::firstConnection()
{
WSADATA wsaData;
SOCKET ConnectSocket = INVALID_SOCKET;
struct addrinfo *result = NULL,
*ptr = NULL,
hints;
int iResult;
// Initialize Winsock
iResult = WSAStartup( MAKEWORD( 2, 2 ), &wsaData );
if( iResult != 0 )
{
printf( "WSAStartup failed with error: %d\n", iResult );
return 1;
}
ZeroMemory( &hints, sizeof( hints ) );
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
// Resolve the server address and port
iResult = getaddrinfo( "127.0.0.1", DEFAULT_PORT, &hints, &result );
if( iResult != 0 )
{
printf( "getaddrinfo failed with error: %d\n", iResult );
WSACleanup();
return 1;
}
// Attempt to connect to an address until one succeeds
for( ptr = result; ptr != NULL; ptr = ptr->ai_next )
{
// Create a SOCKET for connecting to server
this->ConnectSocket = socket( ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol );
if( this->ConnectSocket == INVALID_SOCKET )
{
printf( "socket failed with error: %ld\n", WSAGetLastError() );
WSACleanup();
return 1;
}
// Connect to server.
iResult = connect( this->ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen );
if( iResult == SOCKET_ERROR )
{
closesocket( this->ConnectSocket );
this->ConnectSocket = INVALID_SOCKET;
continue;
}
break;
}
freeaddrinfo( result );
if( this->ConnectSocket == INVALID_SOCKET )
{
printf( "Unable to connect to server!\n" );
WSACleanup();
return 1;
}
std::cout << "Connection to server successful!\n";
// Send an initial buffer
int iResult = send( this->ConnectSocket, ( this->username ).c_str(), (int)strlen( ( this->username ).c_str() ), 0 ); //username is a perfectly fine string
// -> This instance of send does NOT drop any data! <- //
if( iResult == SOCKET_ERROR )
{
printf( "send failed with error: %d\n", WSAGetLastError() );
closesocket( this->ConnectSocket );
WSACleanup();
return false;
}
return true;
}
bool Client::sendCommand( std::string command )
{
// Send a buffer
char* tmp = this->gameinfo.SerializeToArray( DEFAULT_BUFLEN ); // see below
const char* send_buf = tmp; // have tried with and without const, results are the same
for( int i = 0; i < (int)strlen( send_buf ); ++i )
{
printf( "Buffer[%d] = [%c]\n", i, send_buf[i] );
}
// Output (literally copy/pasted):
/*
Buffer[0] = [4]
Buffer[1] = [_]
Buffer[2] = [u]
Buffer[3] = [s]
Buffer[4] = [e]
Buffer[5] = [r]
Buffer[6] = [5]
Buffer[7] = [3]
Buffer[8] = [:]
Buffer[9] = [6]
Buffer[10] = [1]
Buffer[11] = [3]
Buffer[12] = [4]
Buffer[13] = [;]
Buffer[14] = [*]
Buffer[15] = [0]
Buffer[16] = [/]
*/
// This is correct and the desired output!!! ^
printf( "Sending: %s, length = %d\n", send_buf, (int)strlen( send_buf ) );
// output (literally copy/pasted):
/*
Sending: 4_user53:6134;*0/, length = 17
*/
// This is correct and the desired output!!!
int iResult = send( this->ConnectSocket, send_buf, (int)strlen( send_buf ), 0 );
for( int i = 0; i < iResult; ++i )
{
printf( "Buffer[%d] = [%c]\n", i, send_buf[i] );
}
//Output (literally copy/pasted):
/*
Buffer[0] = [ ]
Buffer[1] = [ ]
Buffer[2] = [ ]
Buffer[3] = [ ]
Buffer[4] = [ ]
Buffer[5] = [ ]
Buffer[6] = [ ]
Buffer[7] = [ ]
Buffer[8] = [ ]
Buffer[9] = [ ]
Buffer[10] = [ ]
Buffer[11] = [ ]
Buffer[12] = [4]
Buffer[13] = [;]
Buffer[14] = [*]
Buffer[15] = [0]
Buffer[16] = [ ]
*/
// Here we only see the values 12-15. This is not the desired output. This is the issue I want to solve.
printf( "Iresult = %d\n", iResult );
//output= 17. This is correct.
if( iResult == SOCKET_ERROR )
{
printf( "\nSend failed with error: %d\n", WSAGetLastError() );
closesocket( ConnectSocket );
WSACleanup();
return false;
}
return true;
}
bool Client::receiveMessage()
{
char recvbuf[DEFAULT_BUFLEN];
int recvbuflen = DEFAULT_BUFLEN;
int iResult = recv( this->ConnectSocket, recvbuf, recvbuflen, 0 );
if( iResult > 0 )
{
printf("Message received OK!\n");
}
else if( iResult == 0 )
{
printf( "\nConnection closed\n" );
}
else
{
printf( "\nrecv failed with error: %d\n", WSAGetLastError() );
}
return true;
}
}
int main()
{
std::string in = "";
int n = 0;
bool rec = false;
if( this->firstConnection() )
{
while( in != "Q" )
{
std::cin >> in;
this->sendCommand( in );
while( !rec )
{
rec=this->receiveMessage();
}
}
return this->clientClose();
}
else
{
return false;
}
}
//Server
class Server {
Server::Server()
{
this->currentConnections = 0;
}
void Server::accept_clients()
{
for( int i = 0; i < MAX_CONS; i++ )
{
if( !this->client[i].con ) //i.e a client has not connected to this slot
{
if( this->accept_client( &( this->client[i] ) ) )
{
//update server status
this->Server_Status( CLIENT_CON );
}
}
}
}
int Server::accept_client( _client* x )
{
x->i = sizeof( sockaddr );
x->cs = accept( this->ListenSocket, (sockaddr*)&x->addr, &x->i );
if( x->cs == 0 || x->cs == SOCKET_ERROR )
{
printf( "SOCKET ERROR\n" );
return false;
}
x->con = true;
FD_ZERO( &x->set );
FD_SET( x->cs, &x->set );
printf( "Client accepted \n" );
// declaring character array
char char_array[DEFAULT_BUFLEN];
strcpy_s( char_array, result.c_str() );
this->send_clients( char_array );
return true;
}
void Server::send_clients( char* s )
{
int len = (int)strlen( s );
for( int i = 0; i < MAX_CONS; i++ )
{
if( this->client[i].con ) //valid slot,i.e a client has parked here
{
this->send_client( &( this->client[i] ), s, len );
}
}
}
int Server::send_client( _client* x, char* buffer, int sz )
{
char* send_buf;
if( strchr( buffer, ';' ) == NULL )
{
send_buf = x->gameinfo.SerializeToArray( DEFAULT_BUFLEN ); // see below
}
else
{
send_buf = buffer;
}
x->i = send( x->cs, send_buf, DEFAULT_BUFLEN, 0 );
if( x->i == SOCKET_ERROR || x->i == 0 )
{
printf( "Error: disconnecting client\n" );
disconnect_client( x );
return ( false );
}
else
{
return ( true );
}
}
void Server::recv_clients()
{
char buffer[DEFAULT_BUFLEN] = { 0 };
for( int i = 0; i < MAX_CONS; i++ )
{
if( client[i].con ) //valid slot,i.e a client has parked here
{
try
{
if( !this->recv_client( &( this->client[i] ), buffer, DEFAULT_BUFLEN ) )
{
//Error receiving
printf( "Error receiving message from client!\n" );
}
}
catch( ... )
{
printf( "Error\n" );
}
}
}
}
int Server::recv_client( _client* x, char* buffer, int sz )
{
if( FD_ISSET( x->cs, &x->set ) )
{
x->i = recv( x->cs, buffer, sz, 0 );
// checking contents
for( int i = 0; i < sz+1; ++i )
{
printf( "Buffer[%d] = [%c]\n", i, send_buf[i] );
}
// output is (literally copy/pasted):
/*
Buffer[0] = [ ]
Buffer[1] = [ ]
Buffer[2] = [ ]
Buffer[3] = [ ]
Buffer[4] = [ ]
Buffer[5] = [ ]
Buffer[6] = [ ]
Buffer[7] = [ ]
Buffer[8] = [ ]
Buffer[9] = [ ]
Buffer[10] = [ ]
Buffer[11] = [ ]
Buffer[12] = [4]
Buffer[13] = [;]
Buffer[14] = [*]
Buffer[15] = [0]
Buffer[16] = [ ]
*/
if( x->i == 0 )
{
disconnect_client( x );
return false;
}
std::string result = this->update( x, buffer, sz ); // returns a perfectly normal std::string - no issues here!!!
// declaring character array
char char_array[DEFAULT_BUFLEN];
strcpy_s( char_array, result.c_str() );
this->send_clients( char_array );
return true;
}
return false;
}
}
int main()
{
Server gameServer = Server();
while( true )
{
gameServer.accept_clients(); //Receive connections
gameServer.recv_clients(); //Receive and respond to data from clients
}
}
//Game info functions shared between client and server (only relevant ones)
char* GameInformation::SerializeToArray( int buflen )
{
std::string s_out = "Hello world;50*0/";
char out[512]; // more than enough space, no overflow issues here I promise
strcpy_s( out, s_out.c_str() );
return out;
}
In case you still don't believe me, here are screenshots of the prints:
Client:
(while debugging I set the first iterator length to 20, that's why there are 20 values, this is of course inaccurate and I apologise for any inconvenience)
Server:
The function GameInformation::SerializeToArray is bad because it is returning a pointer to non-static local array. The life of the array ends when returning from function and dereferencing pointers pointing at elements of the array after returning is illegal.
Instead of that, you should allocate a buffer on the heap and return a pointer to that.
//Game info functions shared between client and server (only relevant ones)
char* GameInformation::SerializeToArray( int buflen )
{
std::string s_out = "Hello world;50*0/";
char* out = new char[512]; // more than enough space, no overflow issues here I promise
strcpy_s( out, 512, s_out.c_str() );
return out;
}
Note that now you should free (via delete[]) the returned array after finished using that.
Another option is returning std::vector instead of plain array. This will eliminate needs to manually management memory regions.
//Game info functions shared between client and server (only relevant ones)
std::vector<char> GameInformation::SerializeToArray( int buflen )
{
std::string s_out = "Hello world;50*0/";
std::vector<char> out(512); // more than enough space, no overflow issues here I promise
strcpy_s( &out[0], 512, s_out.c_str() );
return out;
}
And the caller in this case will be like this:
// Send a buffer
std::vector<char> tmp = this->gameinfo.SerializeToArray( DEFAULT_BUFLEN );
const char* send_buf = tmp.data();
The declaration of GameInformation::SerializeToArray should also be changed to return std::vector<char> in this case.
I am writing a program to transmit data between two machines with tcp/ip socket in C/C++. The programs checks the size of each read and write to make sure the correct size of data being transferred. I also implement a checksum verification to make sure the correct data are transferred. Most of time there is no problem. Once a while, like, once in 50,000 times, I notice the data received is the same length as the sending side but some of its content were altered. The altered portion is always 64 bytes long. Below is my code of send and read. Please help me figure out how this can happen and how to prevent it. Thanks!
bool WriteSocket(int pSocket, unsigned char* pData, uint32_t pNumBytes, uint32_t &pNumBytesWritten, uint32_t &pNumWrites)
{
int rc;
unsigned char* currPos = pData;
uint32_t numFailures = 0;
pNumBytesWritten = 0;
pNumWrites = 0;
while ( true )
{
rc = write(pSocket, currPos, pNumBytes);
pNumWrites++;
if ( rc < 0 )
{
if ( errno == EINTR )
continue;
numFailures++;
if ( numFailures >= 5 )
break;
else
continue;
}
pNumBytesWritten += rc;
if ( rc == pNumBytes )
return true;
pNumBytes -= rc;
currPos += rc;
numFailures = 0;
};
return false;
};
bool ReadSocket(int pSocket, unsigned char* pData, uint32_t pNumBytes)
{
int rc;
unsigned char* currPos = pData;
uint32_t numFailures = 0;
while ( true )
{
rc = read(pSocket, currPos, pNumBytes);
if ( rc < 0 )
{
if ( errno == EINTR )
continue;
numFailures++;
if ( numFailures >= 5 )
break;
else
continue;
}
if ( rc == pNumBytes )
return true;
pNumBytes -= rc;
currPos += rc;
numFailures = 0;
};
return false;
};
If a socket read/write fails for any reason other than EINTR or EAGAIN/EWOULDBLOCK, you should consider that as a fatal error. Do not retry the operation, just stop and close the socket. But your code doesn't do that, it keeps trying to repeat the failed operation.
Also, when reading, you are not handling the case where the read returns 0 when the peer disconnects gracefully. A socket error is not reported in that case.
Try something more like this instead:
bool WriteSocket(int pSocket, unsigned char* pData, uint32_t pNumBytes, uint32_t &pNumBytesWritten, uint32_t &pNumWrites)
{
int rc;
uint32_t numRetries = 0;
pNumBytesWritten = 0;
pNumWrites = 0;
while ( pNumBytes > 0 )
{
rc = write(pSocket, pData, pNumBytes);
if ( rc < 0 )
{
if ( errno == EINTR )
continue;
if ( (errno == EAGAIN) || (errno == EWOULDBLOCK) )
{
++numRetries;
if ( numRetries < 5 )
{
// TODO: use select() or epoll() w/ timeout to
// detect when then socket is writable again...
continue;
}
}
return false;
}
++pNumWrites;
pNumBytesWritten += rc;
pNumBytes -= rc;
pData += rc;
numRetries = 0;
}
return true;
}
bool ReadSocket(int pSocket, unsigned char* pData, uint32_t pNumBytes)
{
int rc;
uint32_t numRetries = 0;
while ( pNumBytes > 0 )
{
rc = read(pSocket, pData, pNumBytes);
if ( rc < 0 )
{
if ( errno == EINTR )
continue;
if ( (errno == EAGAIN) || (errno == EWOULDBLOCK) )
{
++numRetries;
if ( numRetries < 5 )
{
// TODO: use select() or epoll() w/ timeout to
// detect when then socket is readable again...
continue;
}
}
return false;
}
else if ( rc == 0 )
{
return false;
}
else
{
pNumBytes -= rc;
pData += rc;
numRetries = 0;
}
}
return true;
}
I have a C++ program where I connect to my server with a socket and I need to set the overlapped for the socket. Doing the following does not work:
Function
int set_wsa_proxy_client ( proxy_client *node ) {
WSABUF wbuf;
DWORD bytes, flags;
int BufLen = 1024;
wbuf.buf = node->buf;
wbuf.len = node->len;
flags = 0;
int rr = WSARecv ( node->s , &wbuf , 1 , &bytes , &flags , &node->ov , NULL );
if (rr == FALSE) {
if (WSAGetLastError() != WSA_IO_PENDING) {
printf("PostRecv: WSARecv* failed: %d\n", WSAGetLastError());
if ( WSAGetLastError() == ERROR_SUCCESS ) { // this means it completed right away ...
//cout << endl << "ERROR_SUCCESS - set_wsa_lobby_client completed" << endl;
//cout << endl << "BYTES: " << node->len << endl;
return 0;
}
return WSAGetLastError();
}
}
return 0;
}
Extension
typedef struct OverlappedEx : OVERLAPPED {
int id;
} OverlappedEx;
proxy_client struct
struct proxy_client {
// ... blah blah blah
SOCKET s;
OverlappedEx ov;
// ... blah blah blah
}
Main
HANDLE ServerCompletionPort = CreateIoCompletionPort ( INVALID_HANDLE_VALUE , NULL , (ULONG_PTR)NULL , 0 );
if ( ServerCompletionPort == NULL ) { fprintf( stderr , "CreateIoCompletionPort failed: %d\n" , GetLastError() ); return -1; }
proxy_client *new_c = new proxy_client;
memset(&new_c->ov , 0 , sizeof(new_c->ov));
new_c->ov.hEvent = ServerCompletionPort;
new_c->s = (make socket)
// ... Connect and other stuff ...
HANDLE hrc = CreateIoCompletionPort( (HANDLE)new_c->s, new_c->ov.hEvent, (ULONG_PTR)pc, 0 ); // pc is the global struct of proxy_client
if (hrc == NULL)
fprintf(stderr, "CompletionThread: CreateIoCompletionPort failed: %d\n", GetLastError());
int r = 0;
if ( ( r = set_wsa_proxy_client ( new_c ) ) != 0 ) {
//
} else {
//
}
This does not seem to trigger the socket when I GetQueuedCompletionStatus for ServerCompletionPort, after sending the socket data (from the server). I was wondering how I can set an IO for a socket! Thank for the help! :-)
I have an error during compiling:
raja#raja-desktop:~/socket$ g++ Socket.cpp
Socket.cpp: In member function ‘int Socket::recv(std::string&) const’:
Socket.cpp:135: error: ‘cout’ is not a member of ‘std’
Source of Socket.cpp:
// Implementation of the Socket class.
#include "Socket.h"
#include "string.h"
#include <string.h>
#include <errno.h>
#include <fcntl.h>
Socket::Socket() :
m_sock ( -1 )
{
memset ( &m_addr,
0,
sizeof ( m_addr ) );
}
Socket::~Socket()
{
if ( is_valid() )
::close ( m_sock );
}
bool Socket::create()
{
m_sock = socket ( AF_INET,
SOCK_STREAM,
0 );
if ( ! is_valid() )
return false;
// TIME_WAIT - argh
int on = 1;
if ( setsockopt ( m_sock, SOL_SOCKET, SO_REUSEADDR, ( const char* ) &on, sizeof ( on ) ) == -1 )
return false;
return true;
}
bool Socket::bind ( const int port )
{
if ( ! is_valid() )
{
return false;
}
m_addr.sin_family = AF_INET;
m_addr.sin_addr.s_addr = INADDR_ANY;
m_addr.sin_port = htons ( port );
int bind_return = ::bind ( m_sock,
( struct sockaddr * ) &m_addr,
sizeof ( m_addr ) );
if ( bind_return == -1 )
{
return false;
}
return true;
}
bool Socket::listen() const
{
if ( ! is_valid() )
{
return false;
}
int listen_return = ::listen ( m_sock, MAXCONNECTIONS );
if ( listen_return == -1 )
{
return false;
}
return true;
}
bool Socket::accept ( Socket& new_socket ) const
{
int addr_length = sizeof ( m_addr );
new_socket.m_sock = ::accept ( m_sock, ( sockaddr * ) &m_addr, ( socklen_t * ) &addr_length );
if ( new_socket.m_sock <= 0 )
return false;
else
return true;
}
bool Socket::send ( const std::string s ) const
{
int status = ::send ( m_sock, s.c_str(), s.size(), MSG_NOSIGNAL );
if ( status == -1 )
{
return false;
}
else
{
return true;
}
}
int Socket::recv ( std::string& s ) const
{
char buf [ MAXRECV + 1 ];
s = "";
memset ( buf, 0, MAXRECV + 1 );
int status = ::recv ( m_sock, buf, MAXRECV, 0 );
if ( status == -1 )
{
std::cout << "status == -1 errno == " << errno << " in Socket::recv\n";
return 0;
}
else if ( status == 0 )
{
return 0;
}
else
{
s = buf;
return status;
}
}
bool Socket::connect ( const std::string host, const int port )
{
if ( ! is_valid() ) return false;
m_addr.sin_family = AF_INET;
m_addr.sin_port = htons ( port );
int status = inet_pton ( AF_INET, host.c_str(), &m_addr.sin_addr );
if ( errno == EAFNOSUPPORT ) return false;
status = ::connect ( m_sock, ( sockaddr * ) &m_addr, sizeof ( m_addr ) );
if ( status == 0 )
return true;
else
return false;
}
void Socket::set_non_blocking ( const bool b )
{
int opts;
opts = fcntl ( m_sock,
F_GETFL );
if ( opts < 0 )
{
return;
}
if ( b )
opts = ( opts | O_NONBLOCK );
else
opts = ( opts & ~O_NONBLOCK );
fcntl ( m_sock,
F_SETFL,opts );
}
And Socket.h:
// Definition of the Socket class
#ifndef Socket_class
#define Socket_class
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#include <string>
#include <arpa/inet.h>
const int MAXHOSTNAME = 200;
const int MAXCONNECTIONS = 5;
const int MAXRECV = 500;
class Socket
{
public:
Socket();
virtual ~Socket();
// Server initialization
bool create();
bool bind ( const int port );
bool listen() const;
bool accept ( Socket& ) const;
// Client initialization
bool connect ( const std::string host, const int port );
// Data Transimission
bool send ( const std::string ) const;
int recv ( std::string& ) const;
void set_non_blocking ( const bool );
bool is_valid() const { return m_sock != -1; }
private:
int m_sock;
sockaddr_in m_addr;
};
#endif
Add this at the top of the file:
#include <iostream>
To add to John's answer, also fix the top to this:
// string.h is deprecated
#include <string>
// or
#include <cstring>