web-dev-qa-db-ja.com

ソケットを介したstd :: stringの送受信

私はSO=で同様の質問を見ましたが、私の質問には答えません。ここで私は文字列を送信して受信しようとしています:

Std :: stringを送信しています:

if( (bytecount=send(hsock, input_string.c_str(), input_string.length(),0))== -1)

これで正しく受け取れますか?

if ((bytecount = recv(*csock, rcv.c_str(), rcv.length(), 0)) == -1)

エラーが発生します:

エラー:受信ラインで「const void *」から「void *」[-fpermissive]」への変換が無効です!

14
Catty

いいえ、できません。 c_str() は_const char*_を返します。つまり、ポインタの内容を上書きすることはできません。

データを受け取りたい場合は、バッファを作成する必要があります。 _std::vector_ を使用し、それを使用して_std::string_を作成します。

_// create the buffer with space for the data
const unsigned int MAX_BUF_LENGTH = 4096;
std::vector<char> buffer(MAX_BUF_LENGTH);
std::string rcv;   
int bytesReceived = 0;
do {
    bytesReceived = recv(*csock, &buffer[0], buffer.size(), 0);
    // append string from buffer.
    if ( bytesReceived == -1 ) { 
        // error 
    } else {
        rcv.append( buffer.cbegin(), buffer.cend() );
    }
} while ( bytesReceived == MAX_BUF_LENGTH );
// At this point we have the available data (which may not be a complete
// application level message). 
_

上記のコードは一度に4096バイトを受け取ります。 4Kを超えるデータが送信された場合は、ループし続け、データがなくなるまでデータをrecvに追加します。

また、buffer.data()ではなく_&buffer[0]_を使用していることに注意してください。最初の要素のアドレスを取ることは、非constポインターにアクセスし、未定義の動作を回避する方法です。

12
Steve

最善の方法は、最初に文字列データの長さを固定形式で送信することです(例:uint32_t(ネットワークバイト順)。次に、受信者はこれを最初に読み取り、適切なサイズのバッファを割り当ててから、後で送信されるシリアル化されたメッセージを受信します。

sdおよびcsdは、すでに存在するソケット記述子であると見なされます。

Sender.cpp

std::string dataToSend = "Hello World! This is a string of any length ...";

uint32_t dataLength = htonl(dataToSend.size()); // Ensure network byte order 
                                                // when sending the data length

send(sd,&dataLength ,sizeof(uint32_t) ,MSG_CONFIRM); // Send the data length
send(sd,dataToSend.c_str(),dataToSend.size(),MSG_CONFIRM); // Send the string 
                                                           // data 

Receiver.cpp

uint32_t dataLength;
recv(csd,&rcvDataLength,sizeof(uint32_t),0); // Receive the message length
dataLength = ntohl(dataLength ); // Ensure Host system byte order

std::vector<uint8_t> rcvBuf;    // Allocate a receive buffer
rcvBuf.resize(dataLength,0x00); // with the necessary size

recv(csd,&(rcvBuf[0]),dataLength,0); // Receive the string data

std::string receivedString;                        // assign buffered data to a 
receivedString.assign(&(rcvBuf[0]),rcvBuf.size()); // string

利点は。バッファリングされた複数の読み取りや、受信した文字列へのコピーをいじる必要はありません。さらに、送信されたデータが最終的に完成したときに受信側で知ることができます。

短所は、長さを最初に送信するときに「プロトコル」のようなものを導入することです。

8

いいえ、 std::string::c_str()const char*を返します。つまり、読み取り専用です。 recvが正常に戻った後、ローカルバッファを割り当て、ローカルバッファから文字列オブジェクトを作成できます。

特定の長さのデータを読み取るようにrecv関数に指示する必要があります。たとえば、毎回512バイトを読み取る必要があります。

#define DEFAULT_BUFLEN 512
char recvbuf[DEFAULT_BUFLEN];

recv(*csock, recvbuf, DEFAULT_BUFLEN, 0);
6
billz

_error: invalid conversion from ‘const void*’ to ‘void*’ [-fpermissive]_ recv行に!

この特定の質問にダイヤルして、あなたは書いた(sansifステートメント):

_bytecount = recv(*csock, rcv.c_str(), rcv.length(), 0)
_

rcv.c_str()は、_const char*_ポインターを取得します。 _const char*_は_const void*_に強制変換されました。非constポインターを取得して未定義の動作を回避する唯一の方法は、_std::string_または_std::vector_の最初の要素のアドレスを取得することです。

_bytecount = recv(*csock, &rcv[0], rcv.length(), 0)
_

このような非constポインターの取得は、連続したメモリを提供するSTLコンテナーに対してのみ有効です。このトリックは、mapmultimap、または他の連想コンテナでは機能しません。

@πάντα-ῥεῖがそれに答えた唯一の答えですが、彼は要点を強調しませんでした。

0
jww