gpt4 book ai didi

c++ - 使用套接字 send()/recv()(winsock) 回显服务器/客户端消息截断

转载 作者:行者123 更新时间:2023-11-28 07:37:56 24 4
gpt4 key购买 nike

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;

最佳答案

请记住,recv 可能不会一次接收到所有数据。这意味着服务器中的 recv 调用可能无法获得完整的消息,但会发回它获得的消息,而客户端可能只会收到从服务器发送的消息的一部分。

这不会有问题,除非您在客户端中只收到一次,然后继续做其他事情。就像你一样。

有两种解决方法:

  1. 制定一个简单的协议(protocol),要么在每条消息前加上消息长度,要么有一个特殊的消息结束标记。在第一种情况下,您将始终知道有多少数据并且可以在循环中读取,直到收到所有数据;在第二种情况下,您将在循环中接收,直到获得消息结束标记。

  2. 使套接字成为非阻塞的,并循环读取直到recv 返回错误,错误为WSAEWOULDBLOCK。那么就没有更多的数据可以读取了。


客户端的错误:

int len = sizeof(msg);

此行返回字符串对象 的大小,而不是包含在对象中的字符串。您应该使用 msg.length() 来获取长度。您可以在几个地方使用它。

如果使用得当,使用 std::string 是完全没问题的。例如:

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';

关于c++ - 使用套接字 send()/recv()(winsock) 回显服务器/客户端消息截断,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16376979/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com