在 C++ 中的套接字上发送和接收数据
Sending and receiving data on a socket in c++
我目前正在学习 C++ 并试图了解套接字。我以前用 C# 和 Java 中的高级语言做过套接字,但在 C++ 中遇到过问题。
我已经完成了 https://msdn.microsoft.com/en-us/library/windows/desktop/ms737593(v=vs.85).aspx 中的示例。
除了有一件事我不确定之外,我的代码几乎可以正常工作。当我编写其他套接字应用程序时,通常在收到换行符后才会收到响应。
我想也许 C++ 一次添加 1 个接收到的字符并添加到缓冲区,所以当我关闭套接字时,缓冲区将包含接收到的所有内容,但它似乎是一个十六进制值,但是ascii 转换器中的十六进制似乎打印出乱码而不是套接字数据。
下面是我的代码
const int DEFAULT_BUF_LEN = 525;
InitialiseLibrary initLibrary("tunnel.conf");
if (initLibrary.initialise(0))
{
cout << "Failed to initialise library" << endl;
}
BitsLibrary bitsLibrary;
StaticSettings staticSettings(&bitsLibrary, "tunnel.conf");
//Open a socket connection
WSAData wsaData;
int iResult;
SOCKET listenSocket = INVALID_SOCKET;
SOCKET clientSocket = INVALID_SOCKET;
struct addrinfo *result = NULL;
struct addrinfo hints;
int iSendResult;
char recvBuf[DEFAULT_BUF_LEN];
int recBufLen = DEFAULT_BUF_LEN;
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0)
{
cout << "WSAStartup Failed with error: " << iResult << endl;
return EXIT_FAILURE;
}
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
iResult = getaddrinfo(NULL, "500", &hints, &result);
if (iResult != 0)
{
cout << "getaddrinfo failed with error: " << iResult << endl;
return EXIT_FAILURE;
}
listenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (listenSocket == INVALID_SOCKET)
{
cout << "Socket failed with error: " << WSAGetLastError() << endl;
freeaddrinfo(result);
WSACleanup();
return EXIT_FAILURE;
}
iResult = ::bind(listenSocket, result->ai_addr, (int)result->ai_addrlen);
if (iResult != 0)
{
cout << "bind failed with error: " << iResult << endl;
FreeAddrInfo(result);
closesocket(listenSocket);
WSACleanup();
return EXIT_FAILURE;
}
freeaddrinfo(result);
iResult = listen(listenSocket, SOMAXCONN);
if (iResult == SOCKET_ERROR)
{
cout << "Listen failed with error: " << WSAGetLastError() << endl;
closesocket(listenSocket);
WSACleanup();
return EXIT_SUCCESS;
}
sockaddr_in clientAddr;
socklen_t sin_size = sizeof(struct sockaddr_in);
clientSocket = accept(listenSocket, (struct sockaddr*)&clientAddr, &sin_size);
send(clientSocket, "welcome", string("welcome").length(),0);
if (clientSocket == INVALID_SOCKET)
{
cout << "accept failed with error: " << WSAGetLastError() << endl;
closesocket(listenSocket);
WSACleanup();
return EXIT_FAILURE;
}
closesocket(listenSocket);
do {
iResult = recv(clientSocket, recvBuf, recBufLen, 0);
if (iResult > 0)
{
cout << "Bytes received: " << iResult << endl;
cout << "Received: " << recvBuf << endl;
iSendResult = send(clientSocket, recvBuf, iResult, 0);
if (iSendResult == SOCKET_ERROR)
{
cout << "send failed with error: " << WSAGetLastError() << endl;
closesocket(clientSocket);
WSACleanup();
return EXIT_FAILURE;
}
cout << "Bytes sent " << iSendResult << endl;
}
else if (iResult == 0)
{
cout << "Connection closing" << endl;
}
else
{
cout << "Recv failed with error: " << WSAGetLastError() << endl;
closesocket(clientSocket);
WSACleanup();
return EXIT_FAILURE;
}
} while (iResult > 0);
iResult = shutdown(clientSocket, SD_SEND);
if (iResult == SOCKET_ERROR)
{
cout << "shutdown failed with error: " << WSAGetLastError() << endl;
closesocket(clientSocket);
WSACleanup();
return EXIT_FAILURE;
}
closesocket(clientSocket);
WSACleanup();
cout << "Message was: " << recvBuf << endl;
return EXIT_SUCCESS;
如果我没记错的话,如果我在 C# 中执行此操作并通过 telnet 连接到我的套接字,我可以发送 Hello
但在我发送 \r\n
之前应用程序不会执行任何操作或者缓冲区已满。
但是,在我的 C++ 应用程序中,只要我输入 H
,我的应用程序就会立即响应并打印出只包含 H
的 recvBuf 加上一堆空白字符(在 cmd 中显示为正方形) ) 我假设它是数组缓冲区的空白部分,并发送回复。
套接字不关心换行符或空值。只有更高级别的流式 API 才能做到这一点。重要的是 recv()
的 return 值。这会准确告诉您接收到的字节数。缓冲区中超出该计数的任何内容均无效。
在流媒体协议中,建立一个确定内容长度的协议是很有用的。
例如,客户端可以发送内容长度,然后发送数据,服务器将首先读取内容长度,然后循环读取套接字并缓冲数据,直到接收到内容长度字节数.
我目前正在学习 C++ 并试图了解套接字。我以前用 C# 和 Java 中的高级语言做过套接字,但在 C++ 中遇到过问题。
我已经完成了 https://msdn.microsoft.com/en-us/library/windows/desktop/ms737593(v=vs.85).aspx 中的示例。
除了有一件事我不确定之外,我的代码几乎可以正常工作。当我编写其他套接字应用程序时,通常在收到换行符后才会收到响应。
我想也许 C++ 一次添加 1 个接收到的字符并添加到缓冲区,所以当我关闭套接字时,缓冲区将包含接收到的所有内容,但它似乎是一个十六进制值,但是ascii 转换器中的十六进制似乎打印出乱码而不是套接字数据。
下面是我的代码
const int DEFAULT_BUF_LEN = 525;
InitialiseLibrary initLibrary("tunnel.conf");
if (initLibrary.initialise(0))
{
cout << "Failed to initialise library" << endl;
}
BitsLibrary bitsLibrary;
StaticSettings staticSettings(&bitsLibrary, "tunnel.conf");
//Open a socket connection
WSAData wsaData;
int iResult;
SOCKET listenSocket = INVALID_SOCKET;
SOCKET clientSocket = INVALID_SOCKET;
struct addrinfo *result = NULL;
struct addrinfo hints;
int iSendResult;
char recvBuf[DEFAULT_BUF_LEN];
int recBufLen = DEFAULT_BUF_LEN;
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0)
{
cout << "WSAStartup Failed with error: " << iResult << endl;
return EXIT_FAILURE;
}
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
iResult = getaddrinfo(NULL, "500", &hints, &result);
if (iResult != 0)
{
cout << "getaddrinfo failed with error: " << iResult << endl;
return EXIT_FAILURE;
}
listenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (listenSocket == INVALID_SOCKET)
{
cout << "Socket failed with error: " << WSAGetLastError() << endl;
freeaddrinfo(result);
WSACleanup();
return EXIT_FAILURE;
}
iResult = ::bind(listenSocket, result->ai_addr, (int)result->ai_addrlen);
if (iResult != 0)
{
cout << "bind failed with error: " << iResult << endl;
FreeAddrInfo(result);
closesocket(listenSocket);
WSACleanup();
return EXIT_FAILURE;
}
freeaddrinfo(result);
iResult = listen(listenSocket, SOMAXCONN);
if (iResult == SOCKET_ERROR)
{
cout << "Listen failed with error: " << WSAGetLastError() << endl;
closesocket(listenSocket);
WSACleanup();
return EXIT_SUCCESS;
}
sockaddr_in clientAddr;
socklen_t sin_size = sizeof(struct sockaddr_in);
clientSocket = accept(listenSocket, (struct sockaddr*)&clientAddr, &sin_size);
send(clientSocket, "welcome", string("welcome").length(),0);
if (clientSocket == INVALID_SOCKET)
{
cout << "accept failed with error: " << WSAGetLastError() << endl;
closesocket(listenSocket);
WSACleanup();
return EXIT_FAILURE;
}
closesocket(listenSocket);
do {
iResult = recv(clientSocket, recvBuf, recBufLen, 0);
if (iResult > 0)
{
cout << "Bytes received: " << iResult << endl;
cout << "Received: " << recvBuf << endl;
iSendResult = send(clientSocket, recvBuf, iResult, 0);
if (iSendResult == SOCKET_ERROR)
{
cout << "send failed with error: " << WSAGetLastError() << endl;
closesocket(clientSocket);
WSACleanup();
return EXIT_FAILURE;
}
cout << "Bytes sent " << iSendResult << endl;
}
else if (iResult == 0)
{
cout << "Connection closing" << endl;
}
else
{
cout << "Recv failed with error: " << WSAGetLastError() << endl;
closesocket(clientSocket);
WSACleanup();
return EXIT_FAILURE;
}
} while (iResult > 0);
iResult = shutdown(clientSocket, SD_SEND);
if (iResult == SOCKET_ERROR)
{
cout << "shutdown failed with error: " << WSAGetLastError() << endl;
closesocket(clientSocket);
WSACleanup();
return EXIT_FAILURE;
}
closesocket(clientSocket);
WSACleanup();
cout << "Message was: " << recvBuf << endl;
return EXIT_SUCCESS;
如果我没记错的话,如果我在 C# 中执行此操作并通过 telnet 连接到我的套接字,我可以发送 Hello
但在我发送 \r\n
之前应用程序不会执行任何操作或者缓冲区已满。
但是,在我的 C++ 应用程序中,只要我输入 H
,我的应用程序就会立即响应并打印出只包含 H
的 recvBuf 加上一堆空白字符(在 cmd 中显示为正方形) ) 我假设它是数组缓冲区的空白部分,并发送回复。
套接字不关心换行符或空值。只有更高级别的流式 API 才能做到这一点。重要的是 recv()
的 return 值。这会准确告诉您接收到的字节数。缓冲区中超出该计数的任何内容均无效。
在流媒体协议中,建立一个确定内容长度的协议是很有用的。
例如,客户端可以发送内容长度,然后发送数据,服务器将首先读取内容长度,然后循环读取套接字并缓冲数据,直到接收到内容长度字节数.