I am trying to implement a simple server, using c++ and winsock2 in VSC, that can accept and communicate with multiple clients at the same time. I tried to use multithreading and an array of client and it seems that I can connect to multiple clients but I can communicate only with one at a time. As soon as I close the connection the next client connects. I believe it has something to do with the join function of the thread. But I couldnĀ“t find a way to solve it differently.
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdlib.h>
#include <stdio.h>
#include <thread>
#pragma comment(lib, "Ws2_32.lib")
#define DEFAULT_BUFLEN 512
#define DEFAULT_PORT "27015"
#define MAXIMUM_CONNECTIONS 10
int connectionsCount = 0;
SOCKET clients[MAXIMUM_CONNECTIONS];
using namespace std;
void communicatingWithClient(SOCKET*);
void listenForConnection(SOCKET*);
int main()
{
WSADATA wsaData;
int iResult;
SOCKET ListenSocket = INVALID_SOCKET;
struct addrinfo *result = NULL;
struct addrinfo hints;
// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
if (iResult != 0)
{
printf("WSAStartup failed with error: %d\n", iResult);
WSACleanup();
return 1;
}
printf("WSA started\n");
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 local address and port to be used by the server
iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
if (iResult != 0)
{
printf("getaddrinfo failed: %d\n", iResult);
freeaddrinfo(result);
WSACleanup();
return 1;
}
// Create a SOCKET for the server to listen for client connections
ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (ListenSocket == INVALID_SOCKET)
{
printf("Error at socket(): %d\n", WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
return 1;
}
printf("ListenSocket created\n");
// 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);
thread listenThread(listenForConnection, &ListenSocket);
listenThread.join();
closesocket(ListenSocket);
WSACleanup();
return 0;
}
void listenForConnection(SOCKET *ListenSocket)
{
int iResult;
// Listen on the ServerSocket forever
while(true)
{
printf("While Loop for listening started...\n");
iResult = listen( *ListenSocket, SOMAXCONN);
if (iResult == SOCKET_ERROR )
{
printf( "Listen failed with error: %d\n", WSAGetLastError() );
closesocket(*ListenSocket);
WSACleanup();
return;
}
printf("Accepting client socket..\n");
// Accept a client socket
if (connectionsCount < 10)
{
clients[connectionsCount] = accept(*ListenSocket, NULL, NULL);
if (clients[connectionsCount] == INVALID_SOCKET)
{
printf("accept failed: %d\n", WSAGetLastError());
closesocket(*ListenSocket);
closesocket(clients[connectionsCount]);
WSACleanup();
return;
}
printf("Client accepted\n");
}
else
{
continue;
}
// Receive until the peer shuts down the connection
thread communicationThread(communicatingWithClient, &clients[connectionsCount++]);
communicationThread.join();
}
}
void communicatingWithClient(SOCKET *ClientSocket)
{
int iResult;
int iSendResult;
char recvbuf[DEFAULT_BUFLEN];
int recvbuflen = DEFAULT_BUFLEN;
do
{
printf("Receiving bytes from client...\n");
iResult = recv(*ClientSocket, recvbuf, recvbuflen, 0);
if (iResult > 0)
{
printf("Bytes received: %d\n", iResult);
// Echo the buffer back to the sender
iSendResult = send(*ClientSocket, recvbuf, iResult, 0);
if (iSendResult == SOCKET_ERROR)
{
printf("send failed: %d\n", WSAGetLastError());
closesocket(*ClientSocket);
WSACleanup();
return;
}
printf("Bytes sent: %d\n", iSendResult);
}
else if (iResult == 0)
{
printf("Connection closing...\n");
}
else
{
printf("recv failed: %d\n", WSAGetLastError());
closesocket(*ClientSocket);
WSACleanup();
return;
}
}
while (iResult > 0);
// shutdown the send half of the connection since no more data will be sent
iResult = shutdown(*ClientSocket, SD_SEND);
printf("ClientSocket shut down\n");
if (iResult == SOCKET_ERROR)
{
printf("shutdown failed: %d\n", WSAGetLastError());
closesocket(*ClientSocket);
WSACleanup();
return;
}
closesocket(*ClientSocket);
}
Your problem is caused by this part :
// Receive until the peer shuts down the connection
thread communicationThread(communicatingWithClient, &clients[connectionsCount++]);
communicationThread.join();
More precisely, by this call communicationThread.join();
If you wait until finish communication with a client, you will not be able to communicate with another one. (because you have to wait for the communication thread to finish before you can create a new one)
your code as it is written, is therefore sequential
The usefulness of multithreaind is when threads are allowed to run in parallel. If you synchronize immediately with each creation of a thread, you will not be able to use multithreading and your code can be replaced by a sequential one
for best performance you must use async sockets, select method (FD_WRITE, FD_READ, FD_CLOSE...). If you use one thread per socket (sync), sooner and later you will have overhead between threads if you manage thousand of connections, because the OS will be wasting CPU/cycles changing context (thread ID, stack variable...) than doing real task or work.
You can't know when a thread will be doing real work, you don't know when a socket will have data (recv), but you can ask (select) if there is data pending, and as fast as you can, switch to another socket in the same thread.
In the other hand, if you have data pending, other sockets must wait before you do 'recv' in the current socket in the same thread.
There is no a one solution, depends on project, but you can create a pool of threads, and one thread can handle a dozen of connected sockets. If you have more physical cores (CPU), you can create more threads, but watch out with logical cores, they share physical components and you can have bad performance.
When you have already registers on the behavior of the project, you can optimize or learn and improve it. So, Big Data + Machine Learning is the next step.
This is the way most of frameworks work.
Related
I need to send some data to a device. For that, I'm trying to use a WinSock socket client. The connection in principal works, but I get no data back, only an ACK. I tried this same test with a Putty client, there I was receiving data. I was looking with Wireshark and I saw that my WinSock client is sending a FIN after transmitting the data. That is not the case with Putty. I know the FIN will be sent because of shutdown(SD_SEND), but how can I handle this in another way? You can see this in the Pictures below.
Wireshark for WinSock:
Wireshark for Putty:
Here is my client code:
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdlib.h>
#include <stdio.h>
// Need to link with Ws2_32.lib, Mswsock.lib, and Advapi32.lib
#pragma comment (lib, "Ws2_32.lib")
#pragma comment (lib, "Mswsock.lib")
#pragma comment (lib, "AdvApi32.lib")
#define DEFAULT_BUFLEN 512
#define DEFAULT_PORT "8000"
#define DEFAULT_IP "127.0.0.1"
//int __cdecl main(int argc, char** argv)
int __cdecl main()
{
WSADATA wsaData;
SOCKET ConnectSocket = INVALID_SOCKET;
struct addrinfo* result = NULL,
* ptr = NULL,
hints;
const char* sendbuf = "M:UA 111111 SEP1,00000";
char recvbuf[DEFAULT_BUFLEN];
int iResult;
int recvbuflen = DEFAULT_BUFLEN;
// Validate the parameters
// if (argc != 2) {
// printf("usage: %s server-name\n", argv[0]);
// return 1;
// }
// 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(argv[1], DEFAULT_PORT, &hints, &result);
iResult = getaddrinfo(DEFAULT_IP, 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;
}
// Send an initial buffer
iResult = send(ConnectSocket, sendbuf, (int)strlen(sendbuf), 0);
if (iResult == SOCKET_ERROR) {
printf("send failed with error: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
printf("Bytes Sent: %ld\n", iResult);
// 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;
}
// Receive until the peer closes the connection
do {
iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0);
if (iResult > 0)
printf("Bytes received: %d\n", iResult);
else if (iResult == 0)
printf("Connection closed\n");
else
printf("recv failed with error: %d\n", WSAGetLastError());
} while (iResult > 0);
// cleanup
closesocket(ConnectSocket);
WSACleanup();
return 0;
}
Putty is sending 2 extra bytes after each text. Presumably a CRLF line ending. Your Winsock code is not sending those 2 bytes. Chances are, the server is expecting a line break to arrive before sending data back to the client.
Try changing this:
const char* sendbuf = "M:UA 111111 SEP1,00000";
To this:
const char* sendbuf = "M:UA 111111 SEP1,00000\r\n";
And no, Putty is not sending a FIN, because it is a user-controlled terminal, and the user (you) hasn't indicated to it that further text will not be entered, so it just sits there waiting for more text to send.
Thanks for the fast respond. I tried this, but it will not work. Also I have looked into Wireshark for the Putty package, there were not two extra Bytes on the end.
I was trying to write some C++ code to control my Philips Hue lights. After using the browser debug tool to figure out my bridge's IP and adding a user to control the lights with, I tried to replicate the messages sent by my browser in code to create my own routines.
This is the code I'm using:
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <string>
#pragma comment(lib, "Ws2_32.lib")
#define PORT "80"
int main() {
const char* adress = "192.168.178.x";
// Initializing Winsock
WSADATA wsaData;
int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); //MAKEWORD(2, 2) specifies the version
if (iResult != 0)
{
printf("WSAStartup failed: %d\n", iResult);
return 1;
}
// Data structure to hold info for the socket
struct addrinfo *result = NULL,
*ptr = NULL,
hints;
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET; // Only using IPv4 adresses
hints.ai_socktype = SOCK_STREAM; // TCP socket
hints.ai_protocol = IPPROTO_TCP;
// Converting adress to readable format and stuffing it into result.
iResult = getaddrinfo(adress, PORT, &hints, &result);
if (iResult != 0)
{
printf("getaddrinfo failed: %d\n", iResult);
WSACleanup();
return 1;
}
// Actually creating a socket
SOCKET ConnectSocket = INVALID_SOCKET;
ptr = result;
// Creating socket with previously initialized data
ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
if (ConnectSocket == INVALID_SOCKET)
{
printf("Error at socket(): %ld\n", WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
return 1;
}
// Connecting to the server socket
iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
// Error checking
if (iResult == SOCKET_ERROR)
{
closesocket(ConnectSocket);
ConnectSocket = INVALID_SOCKET;
}
freeaddrinfo(result);
if (ConnectSocket == INVALID_SOCKET)
{
printf("Unable to connect to server!\n");
WSACleanup();
return 1;
}
/////////////////////////////////////////////////////////
// ACTUAL MESSAGE
std::string request = "GET /api/<username>/lights/2 HTTP/1.1\r\n";
request.append("Host: 192.168.178.x\r\n");
request.append("\r\n");
////////////////////////////////////////////////////////
printf("Request:\n%s", request.c_str());
// Send message
iResult = send(ConnectSocket, request.c_str(),(int) request.size(), 0);
// Error check
if (iResult == SOCKET_ERROR)
{
printf("send failed: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
printf("Bytes Sent: %ld\n", iResult);
// Shutting down socket since it won't be used anymore (can still receive data)
iResult = shutdown(ConnectSocket, SD_SEND);
if (iResult == SOCKET_ERROR)
{
printf("shutdown failed: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
const int buflen = 8196;
char recvbuf[buflen];
ZeroMemory(recvbuf, buflen);
// Keep socket open until connection is closed
do
{
iResult = recv(ConnectSocket, recvbuf, buflen, 0);
if (iResult > 0)
{
printf("Bytes received: %d\n", iResult);
printf("\nResponse:\n%s\n", recvbuf);
}
else if (iResult == 0)
{
printf("Connection closed\n");
}
else
{
printf("recv failed: %d\n", WSAGetLastError());
}
} while (iResult > 0);
closesocket(ConnectSocket);
WSACleanup();
return 0;
}
If you look at the edit history of this question, you will see that I previously thought that something was wrong with my HTTP messages but I found some weird behavior:
After every reboot of my system I get a proper response the first time I sent my message. But all following attempts result in an immediate closed connection.
Still no clue what's causing this, but I guess something isn't closed or terminated properly?
After some trial and error I found something that made it work.
If I put this part:
iResult = shutdown(ConnectSocket, SD_SEND);
if (iResult == SOCKET_ERROR)
{
printf("shutdown failed: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
after the part that is receiving messages it works.
My guess is that although the documentation says that the socket can still receive messages after being shut down, it changes something that makes the server think that the connection should be closed.
No idea whether that is correct though.
If somebody knows the actual reason then feel free to add a comment.
I'm just happy I don't have to deal with this anymore.
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;
// 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;
}
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.
MY EFFORTS TO SOLVE THIS:
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.
QUESTIONS:
How can I bypass my "solution" of adding shutdown after send ?
If the above is the only way, should I do the same after recv and what argument should I send (SD_SEND, SD_RECEIVE or SD_BOTH) ?
Again, I apologize for lengthy post, but being new to this I have tried to provide as much info as possible in order to make your task easier.
After a brief glance at your code I would venture to guess that the following code block in the client:
while (iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0), iResult > 0)
{
printf("%s", recvbuf);
memset(recvbuf, '\0', sizeof(recvbuf));
}
is actually your issue. You mentioned that shutting down the socket on the serverside fixes the problem. With sockets, the recv call is going to block as long as the socket is alive or until data comes through, but when the socket is closed you will get a recv of 0.
Instead of looping on recv, if you want to just receive one message you should either loop back to the recv call after processing the first recv or you should poll on the socket to see if there is actually data available first.
Hello again wonderful stackoverflow community! Last time I asked a question, it was resolved quickly, right off the back and I hope this will go the same way. :)
So I'm toying with winsock, and I want to be able to connect multiple clients to my server simultaneously. I feel that this could be achieved with a loop on thread creation, socket creation, binding, and listening every time a client connects, but my efforts to do so have only turned up with "listen failed with error"'s. Two different ones depending on what I had tried.
I googled it only to find the advanced MSDN samples the easiest, and still way too hard, examples out there.
Anyone have any simple suggestions?
(Side question: I can't seem to get "mrecv()" to return the whole "recvbuf" variable. All I get is one letter. I know this is a newbie mistake I'm making, but I just can't figure it out. :/ This problem can wait until later, however.)
(Here's the server code so far:)
#undef UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
// Need to link with Ws2_32.lib
#pragma comment (lib, "Ws2_32.lib")
// #pragma comment (lib, "Mswsock.lib")
int minitialize();
int msend(char msendbuf[512]);
char mrecv();
int mshutdown();
#define DEFAULT_BUFLEN 512
#define DEFAULT_PORT "10150"
WSADATA wsaData;
int iResult;
SOCKET ListenSocket = INVALID_SOCKET;
SOCKET ClientSocket = INVALID_SOCKET;
struct addrinfo *result = NULL;
struct addrinfo hints;
int iSendResult;
char recvbuf[DEFAULT_BUFLEN];
int recvbuflen = DEFAULT_BUFLEN;
int main(void)
{
minitialize();
mrecv();
char mmessage[512];
if (strncmp(mmessage,"shutdown",(strlen(mmessage))) == 0) {mshutdown();}
std::cin.getline(mmessage, 512);
msend(mmessage);
// shutdown the connection since we're done
mshutdown();
std::cin.ignore();
return 0;
}
int msend(char msendbuf[512]) // Send a message
{
int iResult3 = send( ClientSocket, msendbuf, 512, 0 );
if (iResult3 == SOCKET_ERROR) {
printf("send failed with error: %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return 1;
}
printf("Bytes Sent: %ld\n", iResult);
std::cout<<"msendbuf: "<<msendbuf<<"\n";
std::cin.ignore();
}
char mrecv() //Recieve a message
{
int iResult2 = recv(ClientSocket, recvbuf, 512, 0);
if (iResult2 > 0) {
printf("Bytes received: %d\n", iResult2);
std::cout<<"recvbuf: "<<recvbuf<<"\n";
}
else if (iResult2 == 0)
printf("Connection closing...\n");
else {
printf("recv failed with error: %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return 1;
}
return *recvbuf;
}
int minitialize() //initialize the winsock server
{
// 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
closesocket(ListenSocket);
}
int mshutdown() //shutdown the server
{
iResult = shutdown(ClientSocket, SD_SEND);
if (iResult == SOCKET_ERROR) {
printf("shutdown failed with error: %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return 1;
}
// cleanup
closesocket(ClientSocket);
WSACleanup();
return 0;
}
Yes, if you're wondering, I am planning on putting all those global variables in their respective local functions. I just need them there to clear a little clutter for now, and it doesn't seem to be causing any issues.
mrecv() is defined as
char mrecv().
In the function mrecv(), it returns a string. Because mrecv() by definition should return a char, the first char of recvbuf is returned. The compiler will not complain of return *recvbuf. This is syntactically correct even when the intent is to return a single character.
accept() returns a file descriptor to the client's connection. Upon return of accept(), you may spawn a new process to process the message using this descriptor while the original process goes back to accepting connections. Or you may create a new thread to process the message from this descriptor while the original thread resumes accepting connections. You will need to loop on accept().
Here's a skeletal template you can use for server. You already got everything correctly on minitialize(). In the sample code below, mrecv() accepts a parameter, the file descriptor of the connected client, newconn.
socket()
bind()
listen(mysock, 10);
while(keep listening) {
newconn = accept(mysock, &peeraddr, &peeraddrlen)
if(newconn > 0) {
if(CreateThread(<security attribute>, <stack size>, (void *)&mrecv, (void *)&newconn, <creation flag>, <threadid>))
perror("Unable to create thread\n");
}
}
closesocket(mysock)
I feel that this could be achieved with a loop on thread creation, socket creation, binding, and listening every time a client connects
No. All you have to to is accept the client connection and start a thread with that socket. Leave the listening socket strictly alone. It isn't affected by the accept() operation and there is no need to rebuild it in any way.
I have written a simple single threaded client server application in Visual C++. When I run my server I wait for some time that it accepts some request from the client.
When I connect the client to the server. The log message on the client reports 14 bytes are send and there is no message on the server. I have written a print statement to print whatever is accepted by the server on the console, but the server doesn't print anything.
It seems to me like the client has connected to some other server.
The client and server code is below.
Server code
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
#include <stdio.h>
#include <conio.h>
#define DEFAULT_PORT "27015"
#define DEFAULT_BUFLEN 512
#pragma comment(lib, "Ws2_32.lib")
int main() {
WSADATA wsdata;
int result = WSAStartup(MAKEWORD(2,2),&wsdata);
if (result != 0){
printf("%s","Unable to initilize windows socket\n");
getch();
return 1;
}
struct addrinfo *addrResult = NULL,hints;
ZeroMemory(&hints, sizeof (hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
result = getaddrinfo(NULL,DEFAULT_PORT,&hints,&addrResult);
if (result != 0){
printf("%s","Error in getaddrInfo");
getch();
return 1;
}
SOCKET listenSocket = INVALID_SOCKET;
listenSocket = socket(addrResult->ai_family,addrResult->ai_socktype,addrResult->ai_protocol);
if (listenSocket == INVALID_SOCKET){
printf("%s","Error in creating socket object\n");
getch();
closesocket(listenSocket);
WSACleanup();
return 1;
}
sockaddr_in service;
service.sin_family = AF_INET;
service.sin_addr.s_addr = inet_addr("127.0.0.1");
service.sin_port = htons(27015);
if (bind(listenSocket,addrResult->ai_addr,
(int)addrResult->ai_addrlen) == SOCKET_ERROR){
//if(bind(listenSocket,(SOCKADDR*) &service,
// sizeof(service)) == SOCKET_ERROR){
printf("bind failed: %d\n", WSAGetLastError());
freeaddrinfo(addrResult);
closesocket(listenSocket);
WSACleanup();
getch();
return 1;
}
freeaddrinfo(addrResult);
if (listen(listenSocket,SOMAXCONN) == SOCKET_ERROR){
printf("%s","Error in listening socket");
getch();
closesocket(listenSocket);
WSACleanup();
}
SOCKET clientSocket = INVALID_SOCKET;
clientSocket = accept((listenSocket,NULL,NULL);
if (clientSocket == INVALID_SOCKET){
closesocket(listenSocket);
WSACleanup();
}
char recvbuf[DEFAULT_BUFLEN];
int iRecvResult, iSendResult;
int recvbuflen = DEFAULT_BUFLEN;
do{
iRecvResult = 0;
iSendResult = 0;
iRecvResult = recv(clientSocket,recvbuf,recvbuflen,0);
if (iRecvResult > 0){
printf("Bytes received: %d\n", iRecvResult);
getch();
// Echo the buffer back to the sender
iSendResult = send(clientSocket, recvbuf, iRecvResult, 0);
if (iSendResult == SOCKET_ERROR) {
printf("send failed: %d\n", WSAGetLastError());
closesocket(clientSocket);
WSACleanup();
return 1;
}
printf("Bytes sent: %d\n", iSendResult);
}
else if (iRecvResult == 0){
printf("Connection closing...\n");
}
else{
printf("recv failed: %d\n", WSAGetLastError());
closesocket(clientSocket);
WSACleanup();
return 1;
}
}while(iRecvResult > 0);
getch();
return 0;
}
Client code
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
#include <stdio.h>
#include <conio.h>
#define DEFAULT_PORT "27015"
#define DEFAULT_BUFLEN 512
#pragma comment(lib, "Ws2_32.lib")
int main(){
WSADATA wsdata;
WSAStartup(MAKEWORD(2,2),&wsdata);
struct addrinfo *addrResult = NULL,hints;
ZeroMemory(&hints, sizeof (hints));
hints.ai_protocol = IPPROTO_TCP;
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = AF_UNSPEC;
int result = 0;
if (getaddrinfo("127.0.0.1",DEFAULT_PORT,
&hints,&addrResult)){
printf("%s","Error in getaddrInfo\n");
WSACleanup();
getch();
return 1;
}
SOCKET connectingSocket = INVALID_SOCKET;
connectingSocket = socket(addrResult->ai_family,addrResult->ai_socktype,
addrResult->ai_protocol);
if (connectingSocket == INVALID_SOCKET){
printf("%s","Error in creating socket\n");
freeaddrinfo(addrResult);
WSACleanup();
getch();
return 1;
}
if (connect(connectingSocket,addrResult->ai_addr, (int)addrResult->ai_addrlen) != 0){
closesocket(connectingSocket);
connectingSocket = INVALID_SOCKET;
WSACleanup();
}
freeaddrinfo(addrResult);
int recvbuflen = DEFAULT_BUFLEN;
char *sendbuf = "this is a test";
char recvbuf[DEFAULT_BUFLEN];
int iResult;
// Send an initial buffer
iResult = send(connectingSocket, sendbuf, (int) strlen(sendbuf), 0);
if (iResult == SOCKET_ERROR) {
printf("send failed: %d\n", WSAGetLastError());
closesocket(connectingSocket);
WSACleanup();
return 1;
}
printf("Bytes Sent: %ld\n", iResult);
// shutdown the connection for sending since no more data will be sent
// the client can still use the connectingSocket for receiving data
iResult = shutdown(connectingSocket, SD_SEND);
if (iResult == SOCKET_ERROR) {
printf("shutdown failed: %d\n", WSAGetLastError());
closesocket(connectingSocket);
WSACleanup();
return 1;
}
// Receive data until the server closes the connection
do {
iResult = recv(connectingSocket, recvbuf, recvbuflen, 0);
if (iResult > 0)
printf("Bytes received: %d\n", iResult);
else if (iResult == 0)
printf("Connection closed\n");
else
printf("recv failed: %d\n", WSAGetLastError());
} while (iResult > 0);
getch();
return 0;
}
Hmm...after fixing the obvious syntax error (the un-matched open-parenthesis in clientSocket = accept((listenSocket,NULL,NULL);), it seems to work reasonably well for me. On the server I get:
Bytes received: 14
Bytes sent: 14
Connection closing...
and on the client:
Bytes Sent: 14
Bytes received: 14
recv failed: 10054
So, the client sends 14 bytes to the server, (you press enter), the server echoes those 14 bytes back to the client, (you press enter again), and the server closes the connection. The client reads the 14 bytes, then further reading fails when the connection is closed.
Edit: I ran the code by opening two Windows SDK command prompt windows, running server in one and client in the other -- nothing particularly exciting or unusual about either one.
You can confirm what is happening in your server code when the client tries to send by enhancing the diagnostics. More output at important times (e.g. successful connect(), accept(), close(), ALL socket errors, an optional send/recv traffic log file maybe) would likely quickly point the way to a solution. Make sure you check for and output any errors on ALL socket API calls.
You can check for other systems listening on your target port/address using netstat.
I can't see anything obvious wrong fwiw.
I'd suggest you to do the following:
Remove all getch() from code for the sake of experiment
Make sure that neither your tcp server nor tcp client apps are runnung
Remove all rules for your apps in windows firewall settings
run the following in CMD shell - you'll need to make GREP available:
netstat -a -b | grep -e "27015" --after=1
If output of this command is not empty - you'll see the name of an application which is the source of the problem.
Otherwise, try reproducing your problem by running the server and then - the client. Monitor the state of the server by the netstat command as above (or by tcpview program). It's interesting what the results would be.
Offtopic note:
your disconnect sequence is incomplete. Google for graceful disconnect. Usually it looks like this:
[client] emit shutdown (sd_send) and wait for remaining data from server
[server] if recv()==0 then { send(remaining data); emit shutdown(sd_send); closesocket }
[client] receive remaining data; if recv()==0 then closesocket
In the client you are shutting down the connection for send:
// shutdown the connection for sending since no more data will be sent
// the client can still use the connectingSocket for receiving data
iResult = shutdown(connectingSocket, SD_SEND);
if (iResult == SOCKET_ERROR) {
printf("shutdown failed: %d\n", WSAGetLastError());
closesocket(connectingSocket);
WSACleanup();
return 1;
}
The server detects the disconnection (recv return 0) and finalize without shutting down the connection on its side, that is the reason why you are receiving the error 100054.