PDA

View Full Version : [C/C++] ottenere un istream* da un socket


TRF83
18-02-2010, 12:10
Come da titolo..si può ottenere un istream* a partire da un socket? Se sì, qualcuno saprebbe dirmi come?
Grazie in anticipo!

ps: si parla di Linux..non windows..ma in questo caso, non dovrebbe cambiare poi molto..

tomminno
18-02-2010, 14:21
Come da titolo..si può ottenere un istream* a partire da un socket? Se sì, qualcuno saprebbe dirmi come?
Grazie in anticipo!

ps: si parla di Linux..non windows..ma in questo caso, non dovrebbe cambiare poi molto..

Da un socket direttamente non puoi.
Dovresti farti una classe wrapper sulle funzionalità dei socket e a questa aggiungere gli operatori stream che si appoggiano su recv/recvfrom send/sendto.

TRF83
18-02-2010, 15:41
Stavo pensando anch'io ad una classe wrapper, ma purtroppo, devo dare in pasto l'istream ad uno scanner Flex, che accetta solo quel tipo di formato.. Grazie per la risposta! :)

fero86
19-02-2010, 02:31
devi implementare uno streambuf che faccia I/O sul socket e poi costruire l'iostream al di sopra di esso. l'ho fatto anche io in passato, ecco il codice.


SocketBuffer.h:
#pragma once


#define SOCKET_INPUT_BUFFER_SIZE ((size_t)0x1000)
#define SOCKET_OUTPUT_BUFFER_SIZE ((size_t)0x1000)


class CSocketBuffer :
public streambuf
{
private:
SOCKET m_s;

char m_aInput[SOCKET_INPUT_BUFFER_SIZE];
char m_aOutput[SOCKET_OUTPUT_BUFFER_SIZE + 1];

private:
CSocketBuffer(const CSocketBuffer&);
CSocketBuffer &operator = (const CSocketBuffer&);

protected:
virtual int sync();
virtual int underflow();
virtual int overflow(int c = EOF);

public:
explicit CSocketBuffer(SOCKET s);
virtual ~CSocketBuffer();

string GetPeerName();

};



SocketBuffer.cpp:
#include <StdAfx.h>
#include <SocketBuffer.h>


CSocketBuffer::CSocketBuffer(const CSocketBuffer&)
{
}

CSocketBuffer &CSocketBuffer::operator = (const CSocketBuffer&)
{
return *this;
}


int CSocketBuffer::sync()
{
if (overflow() != EOF)
{
return 0;
}
else
{
return -1;
}
}

int CSocketBuffer::underflow()
{
ssize_t cbRead = ::recv(m_s, m_aInput, SOCKET_INPUT_BUFFER_SIZE, 0);
if (cbRead <= 0)
{
return EOF;
}

setg(m_aInput, m_aInput, m_aInput + cbRead);

return m_aInput[0];
}

int CSocketBuffer::overflow(int c)
{
size_t cbToWrite = pptr() - pbase();
if (c != EOF)
{
*pptr() = c;
cbToWrite++;
}

size_t cbWritten = 0;
while (cbWritten < cbToWrite)
{
ssize_t nResult = ::send(m_s, pbase() + cbWritten, cbToWrite - cbWritten, 0);
if (nResult <= 0)
{
return EOF;
}
cbWritten += nResult;
}

pbump(pbase() - pptr());
return 0;
}


CSocketBuffer::CSocketBuffer(SOCKET a_s) :
m_s(a_s)
{
setg(m_aInput, m_aInput, m_aInput);
setp(m_aOutput, m_aOutput + SOCKET_OUTPUT_BUFFER_SIZE);
}

CSocketBuffer::~CSocketBuffer()
{
sync();

int nResult = ::shutdown(m_s, SD_BOTH);
assert(!nResult);
nResult = ::closesocket(m_s);
assert(!nResult);
}


string CSocketBuffer::GetPeerName()
{
struct sockaddr_in sa;
int cbsa;
if (::getpeername(m_s, (struct sockaddr*)&sa, &cbsa) < 0)
{
cerr << "system error " << errno << " on getpeername" << endl;
throw false;
}
return ::inet_ntoa(sa.sin_addr);
}

TRF83
21-02-2010, 14:17
Grazie mille, fre86, ma avrei ancora una domanda: per costruirci l'istream sopra questa..glielo passo nel costruttore di copia? qualcosa del tipo
streambuf buffer;
istream *inputFile(&buffer);?

Sempre sperando che lo scanner non richieda la fine del file, prima di ritornare i valori al parser..se no sono comunque nella c..ioccolata!

fero86
21-02-2010, 18:15
Grazie mille, fre86, ma avrei ancora una domanda: per costruirci l'istream sopra questa..glielo passo nel costruttore di copia? qualcosa del tipo
streambuf buffer;
istream *inputFile(&buffer);? si, ma non é un costruttore di copia, ed inoltre non devi costruire un puntatore. la dichiarazione dovrebbe essere questa:
SOCKET s = ... ;
CSocketBuffer Buffer(s);
istream is(&Buffer);


Sempre sperando che lo scanner non richieda la fine del file, prima di ritornare i valori al parser..se no sono comunque nella c..ioccolata! non va bene restituire l'EOF in corrispondenza della chiusura del socket?

fero86
21-02-2010, 18:16
se usi la mia classe fai attenzione al fatto che il suo distruttore si occupa anche di chiudere il socket; non chiamare tu la closesocket, la chiama il distruttore.

TRF83
22-02-2010, 03:13
non va bene restituire l'EOF in corrispondenza della chiusura del socket?
Il problema è che in pratica, il programma deve elaborare gli input "real time", non aspettare la chiusura del socket. Sostanzialmente devo realizzare un progetto in flex/bison che legga dei pacchetti EtherCAT dal socket e simuli un PLC collegato in cascata agli altri (stavolta fisici). Flex deve occuparsi di dirmi se il pacchetto è corretto (praticamente sempre, dato che viene mandato da un server già funzionante! ^^) e bison deve occuparsi di verificare che il destinatario del pacchetto sia "io", che i pacchetti siano incapsulati correttamente (vale il discorso di prima: praticamente sempre!) e restituirmi i comandi che sono incapsulati nel pacchetto. Il tutto minimizzando al massimo le latenze (il dispositivo fisico induce dei ritardi prossimi a 50ns..motivo per cui abbiamo deciso - è una tesina da due studenti - di farlo in flex/bison/c++ invece di JFlex/cup/Java.. Anche se il prof non conosce flex e bison, essendo orientato solo al mondo Java..quanta fatica per 5 miseri punti! ^^), quindi non si può attendere la disconnessione.. Stavo pensando, altrimenti, di fare un file in memoria e chiamare scanner e parser in modalità multithreaded..ma la creazione di un file e di un thread, richiedono parecchio tempo in più rispetto al passaggio di uno streambuf come quello presentato da te..(senza contare che i risultati delle operazioni, finirebbero su di una struttura comune, e quindi con le varie CriticalSections che introducono ulteriori notevoli ritardi..) Considerando la durata media di un test prossima all'ora..questi ritardi aggiuntivi, inizierebbero a diventare pesantini..

(ps per il distruttore..no problem..c'ero arrivato! Almeno qui! ;))