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. 使套接字非阻塞,并在循环中读取,直到recv返回错误,错误为WSAEWOULDBLOCK. 然后没有更多的数据要读取。


客户端错误:

int len = sizeof(msg);

此行返回字符串object的大小,而不是对象中包含的字符串。您应该使用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 回答
0

我看到你的代码有几个问题。

您的服务器正在循环listen()内调用。accept()它不属于这里。之后只调用listen()一次bind()

send()可以接受的字节数少于请求的字节数。发送缓冲区时,send()循环调用,直到整个缓冲区用完。您的客户端和服务器代码都受此影响。

您的客户端代码将用户的输入复制到缓冲区中,然后发送整个缓冲区,即使输入小于缓冲区也是如此。如果输入大于缓冲区,这也会截断输入。在这两种情况下,您根本不需要那个缓冲区。您可以send()使用字符串的c_str()length()方法将输入字符串直接传递给 。这样,无论其实际长度如何,都会发送整个字符串。

在将回recv()显数据转换为string. 数据不是以空值结尾的,因此您需要将nbr变量作为string构造函数的第二个参数传递。

于 2013-05-04T17:47:28.310 回答