INTRODUCTION:
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:
- send message from server
- receive and display that message on client
- send response from client
- receive and display the response from client
RELEVANT INFORMATION
I apologize in advance for the lengthy code, but i
strongly believe it is relevant for me to submit
SSCCE[
^] for both client and the server, in order for community to stand a chance for solving the problem.
I have tried to keep the code as minimal as possible, but did not want to omit basic error checking.
You can copy/paste both in single
.cpp
file, and they should compile and run without problem:
Server code:
#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;
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;
iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
if ( iResult != 0 ) {
printf("getaddrinfo failed with error: %d\n", iResult);
WSACleanup();
return 1;
}
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;
}
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;
}
ClientSocket = accept(ListenSocket, NULL, NULL);
if (ClientSocket == INVALID_SOCKET) {
printf("accept failed with error: %d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
closesocket(ListenSocket);
std::string message = "Test message from server !!!";
int total = message.size();
const int messageLength = message.size();
while (iResult = send( ClientSocket,
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;
}
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;
}
closesocket(ClientSocket);
WSACleanup();
getchar(); 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;
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;
iResult = getaddrinfo("127.0.0.1", DEFAULT_PORT, &hints, &result);
if ( iResult != 0 )
{
printf("getaddrinfo failed with error: %d\n", iResult);
WSACleanup();
return 1;
}
for(ptr=result; ptr != NULL ;ptr=ptr->ai_next)
{
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;
}
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;
}
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;
}
std::string message = "Client response...";
int total = message.size();
const int messageLength = message.size();
while (iResult = send( ConnectSocket,
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;
}
iResult = shutdown(ConnectSocket, SD_SEND);
if (iResult == SOCKET_ERROR) {
printf("shutdown failed with error: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
closesocket(ConnectSocket);
WSACleanup();
getchar(); return 0;
}
PROBLEM:
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.