0

echo_server と echo_client のコードは以下に掲載されています。echo_client に特定の長さを超えるメッセージを入力すると、サーバーがメッセージの末尾を切り捨て、その一部のみをエコー バックすることに気付きました。バッファ サイズは 1024 バイトで、入力しているメッセージはその長さよりもはるかに短いものです。何が起きてる?この問題を解決して、サーバーが長さ制限内の完全なメッセージをエコー バックするようにするにはどうすればよいですか?

サーバーコード:

#include "stdafx.h"
#include <iostream>
#include <string>
#include <string.h>


#ifndef UNICODE
#define UNICODE
#endif
#define WIN32_LEAN_AND_MEAN

#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")

using namespace std;


static int MAXPENDING = 5;


int main(int argc, char *argv[])

{

WSADATA wsaData;
int iResult;
int optv = 0;
bool connected = false;
char *optval = (char*)&optv;
int optlen = sizeof(optval);
string Q = "quit";
const char *exit =Q.c_str();




iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != NO_ERROR) {
    wprintf(L"WSAStartup function failed with error: %d\n", iResult);
    return 1;
}


if(argc!=2){
    printf("Error: incorrect number of arguments.  ");
    return 1;
}


SOCKET servSock;
servSock = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(servSock==INVALID_SOCKET){
    printf("Socket function failed with error: %d\n",GetLastError());
    return 1;
}


u_int servPort = atoi(argv[1]);

sockaddr_in servAddr;
servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
servAddr.sin_port = htons(servPort);

int opt = setsockopt(servSock,SOL_SOCKET,SO_REUSEADDR,optval,sizeof(optval));


if(bind(servSock,(sockaddr*)&servAddr,sizeof(servAddr))<0){
    printf("Bind function failed with error: %d\n", GetLastError());
    return 1;
}



for(;;){


    if(listen(servSock,MAXPENDING) < 0){
    printf("Listen function failed with error: %d/n",GetLastError());
    return 1;
    }else{
        char *str = new char[5];

        printf("Server listening on port %d\n",servPort);
    }

    SOCKET clientSock;
    sockaddr_in clientAddr;
    socklen_t caddrlen = sizeof(clientAddr);



    clientSock = accept(servSock,(sockaddr*)&clientAddr,&caddrlen);

    if(clientSock < 0){
       printf("Accept() function failed with error: %d/n", GetLastError());
       goto QUIT;

    }else if(clientSock >=0){
        connected = true;
    }



    char cName[INET_ADDRSTRLEN];

    if(inet_ntop(AF_INET,&clientAddr.sin_addr,cName,sizeof(cName))!=NULL){
        printf("Handling client %s/%d\n", cName,ntohs(clientAddr.sin_port));
    }else{
        printf("Error: Unable to get client address");
    }


    char buffer[1024];


    while(connected==true){



        long nbytesrcvd = recv(clientSock,buffer,sizeof(buffer),0);




        if(nbytesrcvd==0){
            connected = false;
            cout << endl;
            cout << cName << ": client disconnected" << endl;                   
            cout << endl;
            break;
        }



        if(nbytesrcvd < 0){
            printf("Error: recv() failed");
            cout << endl;
            goto QUIT;
        }


         if(nbytesrcvd > 0){

            long nbytessent = send(clientSock,buffer,nbytesrcvd,0);
            if(nbytessent < 0){
                cout << "Error: send() failed" << endl;
                cout << endl;
                goto QUIT;          

            }else if(nbytessent!=nbytesrcvd){
                cout << "send() error: sent unexpected # of bytes" << endl;
                cout << endl;
                goto QUIT;

            }                       
         }

    }



    QUIT:

        int iResult = closesocket(clientSock);
        if (iResult == SOCKET_ERROR) {
            printf("closesocket function failed with error: %d\n",GetLastError());
        }



    }




}

そしてクライアントコード:

#include "stdafx.h"
#include <iostream>
#include <string>
#include <string.h>


#ifndef UNICODE
#define UNICODE
#endif

#define WIN32_LEAN_AND_MEAN

#include <Windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>


#define BUFFSIZE 1024

#pragma comment(lib, "ws2_32.lib")

using namespace std;


int main(int argc,char* argv[])
{


WSADATA wsaData;
int result;
bool connected = false;
hostent *rhost;
char buffer[BUFFSIZE];
string Q = "quit";
const char *exit =Q.c_str();






result = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (result != NO_ERROR) {
    printf("WSAStartup function failed with error: %d\n", result);
    return 1;
}




SOCKET connector;
connector = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (connector == INVALID_SOCKET) {
    wprintf(L"socket function failed with error: %ld\n", WSAGetLastError());
    WSACleanup();
    return 1;
}

string hname;
cout << "Enter host name(URL): ";
cin >> hname;
cout << endl;

string portnum;
cout << "Enter the port number you wish to connect on: " ;
cin >> portnum;
cout << endl;

char *hostname = const_cast<char*>(hname.c_str());
char *hostPort = const_cast<char*>(portnum.c_str());



rhost = gethostbyname(hostname);
in_addr addr;
addr.s_addr = *(u_long *)rhost->h_addr_list[0];



sockaddr_in clientserv;
clientserv.sin_family = AF_INET;
clientserv.sin_addr.s_addr = addr.s_addr;
clientserv.sin_port = htons(atoi(hostPort));

if(connect(connector,(sockaddr*)&clientserv,sizeof(clientserv))==SOCKET_ERROR){
    printf("connect function failed with error: %d\n", GetLastError());
    if(closesocket(connector)<0){
        printf("closesocket function failed %d\n", GetLastError());
    }
    return 1;
}else{
    connected = true;
    cout << "Connected to host " << hname << " on port " << portnum << endl;
    cout << "Type 'quit' to exit the program " << endl;
}


while(connected==true){

   int nbr = 0;
   string msg;

   cout << ">";
   getline(cin,msg);
   cout << endl;




   if(msg=="quit"){

       connected = false;
       goto quit;
   }


   int len = sizeof(msg);
   const char *message = msg.c_str();
   strncpy_s(buffer,message,sizeof(msg));         
   long nbs = send(connector,buffer,len,0);
   if(nbs < 0){
        printf("send() failed", GetLastError());
        return 1;
   }

   while(nbr < nbs){
        nbr = recv(connector,buffer,len,0);
        if(nbr < 0){
            printf("recv() failed", GetLastError());
            return 1;

        }else if(nbr==0){
            printf("recv() failed: connection closed prematurely", GetLastError());
            return 1;               
        }else if(nbr > 0){
            string str(buffer);
            cout << ">> " << str << endl;
            cout << endl;
        }

   }



}

quit:

    if (closesocket(connector) == SOCKET_ERROR) {
        printf("closesocket function failed with error: %ld\n", GetLastError());
        WSACleanup();
        return 1;
    }


WSACleanup();

return 0;

}

4

2 に答える 2

1

recv一度にすべてのデータを受信できない場合があることに注意してください。これはrecv、サーバーでの呼び出しが完全なメッセージを取得しない可能性があることを意味しますが、取得したものを送り返し、クライアントはサーバーから送信されたものの一部のみを受信する可能性があります。

これは、クライアントで1 回だけ受信してから別のことを続けない限り、問題にはなりません。あなたと同じように。

これには 2 つの解決策があります。

  1. 各メッセージの先頭にメッセージ長を追加するか、特別なメッセージ終了マーカーを付ける単純なプロトコルを作成します。最初のケースでは、データの量が常にわかり、すべてが受信されるまでループで読み取ることができます。2 番目のケースでは、メッセージの終わりマーカーを取得するまでループで受信します。

  2. ソケットを非ブロッキングにrecvし、エラーが返されるまでループで読み取りますWSAEWOULDBLOCK。その後、読み取るデータはもうありません。


クライアントの間違い:

int len = sizeof(msg);

この行は、オブジェクト内に含まれる文字列ではなく、文字列オブジェクトのサイズを返します。長さを取得するために使用する必要がありますmsg.length()。これをいくつかの場所で使用します。

std::string適切に使用すれば、a を使用してもまったく問題ありません。例えば:

long nbs = send(connector, msg.c_str(), msg.length(), 0);

別物:

nbr = recv(connector, buffer, len, 0);

ここでも間違ったサイズを使用しています。これは、以前の誤った の使用によって提供されたものですsizeof(msg)。代わりに、実際のバッファーのサイズを指定する必要があります。は適切な配列であるため、たとえばここでbuffer使用できます。sizeof(buffer) - 1これ-1は、クライアントから送信するデータがゼロで終わる文字列ではないためです。そのため、文字列のように終了するには、受信データにスペースを予約する必要があります。

buffer[nbr] = '\0';
于 2013-05-04T17:31:02.523 に答える