问题描述
我研究了 []和 []。
我想尝试一些简单的东西,以便修改这些例子来创建简单的聊天应用程序。
我试图实现以下,首先:
- 从服务器发送消息
- 在客户端接收并显示该消息
- 从客户端发送响应
- 接收并显示来自客户的回复
- send message from server
- receive and display that message on client
- send response from client
- receive and display the response from client
I have studied the MSDN examples for blocking TCP server[^] and blocking TCP client[^].
I wanted to try something simple, in view of modifying those examples to create simple chat application.
I have tried to implement the following, for a start:
#undef UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <string>
#pragma comment (lib, "Ws2_32.lib")
#define DEFAULT_BUFLEN 512
#define DEFAULT_PORT "27015"
int __cdecl main(void)
{
WSADATA wsaData;
SOCKET ListenSocket = INVALID_SOCKET;
SOCKET ClientSocket = INVALID_SOCKET;
struct addrinfo *result = NULL;
struct addrinfo hints;
int iResult;
char recvbuf[DEFAULT_BUFLEN] = "";
int recvbuflen = DEFAULT_BUFLEN;
// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed with error: %d\n", iResult);
return 1;
}
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
// Resolve the server address and port
iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
if ( iResult != 0 ) {
printf("getaddrinfo failed with error: %d\n", iResult);
WSACleanup();
return 1;
}
// Create a SOCKET for connecting to server
ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (ListenSocket == INVALID_SOCKET) {
printf("socket failed with error: %ld\n", WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
return 1;
}
// Setup the TCP listening socket
iResult = bind( ListenSocket, result->ai_addr, (int)result->ai_addrlen);
if (iResult == SOCKET_ERROR) {
printf("bind failed with error: %d\n", WSAGetLastError());
freeaddrinfo(result);
closesocket(ListenSocket);
WSACleanup();
return 1;
}
freeaddrinfo(result);
iResult = listen(ListenSocket, SOMAXCONN);
if (iResult == SOCKET_ERROR) {
printf("listen failed with error: %d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
// Accept a client socket
ClientSocket = accept(ListenSocket, NULL, NULL);
if (ClientSocket == INVALID_SOCKET) {
printf("accept failed with error: %d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
// No longer need server socket,
// because I want to accept only 1 client
closesocket(ListenSocket);
// ===================== let us try to send a message...
std::string message = "Test message from server !!!";
int total = message.size();
const int messageLength = message.size();
while (iResult = send( ClientSocket,
// send only the missing part of the string, if send failed to deliver entire packet:
// we move the start of the string forward by messageLength - total
// while we send remaining number of bytes, which is held in total
message.substr(messageLength - total, total).c_str(), total, 0),
iResult > 0)
{
total -= iResult;
}
if (iResult == SOCKET_ERROR) {
printf("send failed with error: %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return 1;
}
/* // adding this, seems to solve the problem ???
iResult = shutdown(ClientSocket, SD_SEND);
if (iResult == SOCKET_ERROR) {
printf("shutdown failed with error: %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return 1;
}
*/
// receive response from client...
while (iResult = recv(ClientSocket, recvbuf, recvbuflen, 0), iResult > 0)
{
printf("%s", recvbuf);
memset(recvbuf, '\0', sizeof(recvbuf));
}
if(iResult < 0)
{
printf("recv failed with error: %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return 1;
}
// cleanup
closesocket(ClientSocket);
WSACleanup();
getchar(); // so I can stop the console from immediately closing...
return 0;
}
客户代码:
Client code:
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <string>
#pragma comment (lib, "Ws2_32.lib")
#define DEFAULT_BUFLEN 512
#define DEFAULT_PORT "27015"
int __cdecl main(int argc, char **argv)
{
WSADATA wsaData;
SOCKET ConnectSocket = INVALID_SOCKET;
struct addrinfo *result = NULL,
*ptr = NULL,
hints;
char recvbuf[DEFAULT_BUFLEN] = "";
int iResult;
int recvbuflen = DEFAULT_BUFLEN;
// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
if (iResult != 0)
{
printf("WSAStartup failed with error: %d\n", iResult);
return 1;
}
ZeroMemory( &hints, sizeof(hints) );
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
// Resolve the server address and port
iResult = getaddrinfo("127.0.0.1", DEFAULT_PORT, &hints, &result);
if ( iResult != 0 )
{
printf("getaddrinfo failed with error: %d\n", iResult);
WSACleanup();
return 1;
}
// Attempt to connect to an address until one succeeds
for(ptr=result; ptr != NULL ;ptr=ptr->ai_next)
{
// Create a SOCKET for connecting to server
ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
if (ConnectSocket == INVALID_SOCKET)
{
printf("socket failed with error: %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
// Connect to server.
iResult = connect( ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
if (iResult == SOCKET_ERROR)
{
closesocket(ConnectSocket);
ConnectSocket = INVALID_SOCKET;
continue;
}
break;
}
freeaddrinfo(result);
if (ConnectSocket == INVALID_SOCKET)
{
printf("Unable to connect to server!\n");
WSACleanup();
return 1;
}
// receive message from server...
while (iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0), iResult > 0)
{
printf("%s", recvbuf);
memset(recvbuf, '\0', sizeof(recvbuf));
}
if(iResult < 0)
{
printf("recv failed with error: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
// ===================== let us try to send a message...
std::string message = "Client response...";
int total = message.size();
const int messageLength = message.size();
while (iResult = send( ConnectSocket,
// send only the missing part of the string, if send failed to deliver entire packet:
// we move the start of the string forward by messageLength - total
// while we send remaining number of bytes, which is held in total
message.substr(messageLength - total, total).c_str(), total, 0),
iResult > 0)
{
total -= iResult;
}
if (iResult == SOCKET_ERROR) {
printf("send failed with error: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
// shutdown the connection since no more data will be sent
iResult = shutdown(ConnectSocket, SD_SEND);
if (iResult == SOCKET_ERROR) {
printf("shutdown failed with error: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
// cleanup
closesocket(ConnectSocket);
WSACleanup();
getchar(); // so I can stop the console from immediately closing...
return 0;
}
我已经实现了解决方案,但没有得到预期的结果。
服务器发送消息,客户端成功接收并显示它,然后客户端被无限制地卡住,而不是将它的响应发送到服务器,服务器也无限制地阻止服务器。
我尝试过:
首先尝试:
使用调试器,我在客户收到后放置了断点阻止只是为了确定客户端在收到第一条消息后永远不会到达那里。
我相信while循环应该再次调用recv,它应该返回0,从而迫使循环结束。
在我点击继续后,调试器甚至没有继续显示客户端接收缓冲区的内容,而是显示我此时无法描述的行为,因为我是不是以英语为母语的人。
第二次尝试:
我也尝试使用CreateThread将服务器的接收循环放入线程,但是这样做了也没有帮助。
我也尝试将接收循环从客户端放入线程,但也失败了。
我试图将客户端和服务器接收循环放入线程中,但也失败了。
第三次尝试:
最后,我在服务器代码中添加了对shutdown(ClientSocket,SD_SEND)的调用,你应该在代码的下半部分找到它,它被注释掉了。
这似乎解决了这个问题,但我不确定这是否是正确的解决方案因为我刚开始使用Winsock。
I have implemented the solution, but did not get the expected result.
Server sends the message, client successfully receives and displays it, but then client gets stuck infinitely, instead of sending it's response to the server, which blocks the server infinitely as well.
What I have tried:
First try:
Using the Debugger, I have placed breakpoint after client's receive block only to determine client never gets there after it receives first message.
I believe while loop should call recv again, which should return 0, thus forcing the loop to end.
Debugger doesn't even continue to show the content of client's receive buffer after I hit Continue, instead it exhibits behavior I can not describe at this moment since I am not a native English speaker.
Second try:
I have also tried to put receiving loop from server into thread, using CreateThread, but that did not help either.
I have also tried to put receiving loop from the client into thread, but that failed too.
I have tried to put both client and server receiving loops into thread, but that failed too.
Third try:
Finally, I have added the call to shutdown( ClientSocket, SD_SEND) in the server code, you shall find it at the lower part of the code, it is commented out.
This seems to fix the problem, but i am not sure if this is the right solution since i am just starting with Winsock.
推荐答案
这篇关于客户端和服务器无法交换数据,因为两者都“卡住”的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!