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.
Related
So I wanted to learn more on how the TCP protocol works and started messing with windows sockets (WinSocks2), I managed to write a basic server and client from the Microsoft documentation.
I expected to see a 3-way handshake in Wireshark, and I did but not in the way I imagined, it was actually duplicated (The server sends the same packets as the client back to the client).
So my question is, is this normal, or am I doing something wrong?
Here is my source code of the server:
// This should be placed before #include <Windows.h> if the windows include is used
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <cstdio>
#include <Windows.h>
#include <WinSock2.h>
#include <WS2tcpip.h>
/// TEST CODE /// REMOVE AFTER TESTING WAS DONE ///
int main()
{
WSADATA wsaData;
int iResult;
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0)
{
printf("WSAStartup failed: %d\n", iResult);
return 1;
}
struct addrinfo* result = NULL, * ptr = 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;
// Resolve the local address and port to be used by the server
iResult = getaddrinfo("0.0.0.0", "1337", &hints, &result);
if (iResult != 0)
{
printf("getaddrinfo failed: %d\n", iResult);
WSACleanup();
return 1;
}
SOCKET ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (ListenSocket == INVALID_SOCKET)
{
printf("Error at socket(): %ld\n", WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
}
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);
if (listen(ListenSocket, SOMAXCONN) == SOCKET_ERROR)
{
printf("Listen failed with error: %ld\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
// Handling pending connections
puts("Waiting for a connection..");
SOCKET ClientSocket = accept(ListenSocket, NULL, NULL);
if (ClientSocket == INVALID_SOCKET)
{
printf("accept failed: %d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
#define DEFAULT_BUFLEN 512
char recvbuf[DEFAULT_BUFLEN];
int iSendResult;
int recvbuflen = DEFAULT_BUFLEN;
int size =0 ;
// Receive until the peer shuts down the connection
do
{
iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);
if (iResult > 0)
{
printf("Bytes received: %d\n", iResult);
size += iResult;
// I thought this was the problem but after I removed it from the code it still was the same.
//
// 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 1;
}*/
//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 1;
}
}
while (iResult > 0);
recvbuf[size] = 0;
printf("Data received: %s\n", recvbuf);
// Stops responding to requests, still can accept tho
iResult = shutdown(ClientSocket, SD_SEND);
if (iResult == SOCKET_ERROR)
{
printf("shutdown failed: %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return 1;
}
// Close socket, no receiving/sending
closesocket(ClientSocket);
WSACleanup();
}
And here is the client:
// This should be placed before #include <Windows.h> if the windows include is used
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <cstdio>
#include <Windows.h>
#include <WinSock2.h>
#include <WS2tcpip.h>
/// TEST CODE /// REMOVE AFTER TESTING WAS DONE ///
int main()
{
WSADATA wsaData;
int iResult;
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0)
{
printf("WSAStartup failed: %d\n", iResult);
return 1;
}
struct addrinfo* result = NULL, * ptr = 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;
// Resolve the local address and port to be used by the server
iResult = getaddrinfo("109.xxx.xxx.xxx", "1337", &hints, &result);
if (iResult != 0)
{
printf("getaddrinfo failed: %d\n", iResult);
WSACleanup();
return 1;
}
SOCKET ConnectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (ConnectSocket == INVALID_SOCKET)
{
printf("Error at socket(): %ld\n", WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
}
iResult = connect(ConnectSocket, result->ai_addr, (int)result->ai_addrlen);
if (iResult == SOCKET_ERROR)
{
closesocket(ConnectSocket);
ConnectSocket = INVALID_SOCKET;
}
// Should really try the next address returned by getaddrinfo
// if the connect call failed
// But for this simple example we just free the resources
// returned by getaddrinfo and print an error message
freeaddrinfo(result);
if (ConnectSocket == INVALID_SOCKET)
{
printf("Unable to connect to server!\n");
WSACleanup();
return 1;
}
#define DEFAULT_BUFLEN 512
int recvbuflen = DEFAULT_BUFLEN;
const char* sendbuf = "this is a test";
char recvbuf[DEFAULT_BUFLEN];
// Send an initial buffer
iResult = send(ConnectSocket, sendbuf, (int)strlen(sendbuf), 0);
if (iResult == SOCKET_ERROR)
{
printf("send failed: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
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 ConnectSocket for receiving data
iResult = shutdown(ConnectSocket, SD_SEND);
if (iResult == SOCKET_ERROR)
{
printf("shutdown failed: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
// Receive data until the server 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: %d\n", WSAGetLastError());
}
while (iResult > 0);
closesocket(ConnectSocket);
WSACleanup();
}
Here is the packets captured from Wireshark, where the IP address starting with 109.xxx.xxx.xxx is the server:
No, the duplicate packets are not normal. But that is not related to your code. It is more likely an issue with your network setup, or just a side effect of how you are capturing the packets.
Unrelated to the duplicate packets, there are other problems with the code shown.
In your server:
your if (ListenSocket == INVALID_SOCKET) block is missing a return, so if socket() fails then you still call bind() with an invalid SOCKET and are dereferencing an invalid result pointer.
Once you reach your recv() loop, you are never closing the ListenSocket before calling WSACleanup().
your printf() of recvbuf needs to be inside the loop when iResult > 0. And if you are going to null-terminate recvbuf then you need to use recvbuflen-1 when calling recv(), or else you will lose the last byte received. Otherwise, pass iResult as a parameter to printf() so you don't need to null-terminate recvbuf at all.
In your client:
do not use the AI_PASSIVE flag when calling getaddrinfo(). That flag is meant for listening servers, not outbound clients.
your if (ConnectSocket == INVALID_SOCKET) block is missing a return, so if socket() fails then you still call connect() with an invalid SOCKET and are dereferencing an invalid result pointer.
Try this code instead:
server:
// This should be placed before #include <Windows.h> if the windows include is used
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <cstdio>
#include <Windows.h>
#include <WinSock2.h>
#include <WS2tcpip.h>
/// TEST CODE /// REMOVE AFTER TESTING WAS DONE ///
int main()
{
WSADATA wsaData;
int iResult;
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0)
{
printf("WSAStartup failed: %d\n", iResult);
return 1;
}
struct addrinfo* result = 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;
// Resolve the local address and port to be used by the server
iResult = getaddrinfo(NULL, "1337", &hints, &result);
if (iResult != 0)
{
printf("getaddrinfo failed: %d\n", iResult);
WSACleanup();
return 1;
}
SOCKET 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;
}
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);
if (listen(ListenSocket, SOMAXCONN) == SOCKET_ERROR)
{
printf("Listen failed with error: %d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
// Handling pending connections
puts("Waiting for a connection..");
SOCKET ClientSocket = accept(ListenSocket, NULL, NULL);
if (ClientSocket == INVALID_SOCKET)
{
printf("accept failed: %d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
#define DEFAULT_BUFLEN 512
char recvbuf[DEFAULT_BUFLEN];
int iSendResult;
// Receive until the peer shuts down the connection
do
{
iResult = recv(ClientSocket, recvbuf, sizeof(recvbuf), 0);
if (iResult > 0)
{
printf("Bytes received: %d\n", iResult);
printf("Data received: %.*s\n", iResult, recvbuf);
// Echo the buffer back to the sender
/*iSendResult = send(ClientSocket, recvbuf, iResult, 0);
if (iSendResult == SOCKET_ERROR)
{
printf("send failed: %d\n", WSAGetLastError());
break;
}
printf("Bytes sent: %d\n", iSendResult);*/
}
else if (iResult == 0)
{
printf("Connection closing...\n");
}
else
{
printf("recv failed: %d\n", WSAGetLastError());
}
}
while (iResult > 0);
// Close socket, no receiving/sending
shutdown(ClientSocket, SD_BOTH);
closesocket(ClientSocket);
WSACleanup();
return 0;
}
client:
// This should be placed before #include <Windows.h> if the windows include is used
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <cstdio>
#include <Windows.h>
#include <WinSock2.h>
#include <WS2tcpip.h>
/// TEST CODE /// REMOVE AFTER TESTING WAS DONE ///
int main()
{
WSADATA wsaData;
int iResult;
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0)
{
printf("WSAStartup failed: %d\n", iResult);
return 1;
}
struct addrinfo* result = NULL, hints;
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
// Resolve the local address and port to be used by the server
iResult = getaddrinfo("109.xxx.xxx.xxx", "1337", &hints, &result);
if (iResult != 0)
{
printf("getaddrinfo failed: %d\n", iResult);
WSACleanup();
return 1;
}
SOCKET ConnectSocket = INVALID_SOCKET;
for(struct addrinfo* ptr = result; ptr != NULL; ptr = ptr->ai_next)
{
ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
if (ConnectSocket == INVALID_SOCKET)
{
printf("Error at socket(): %d\n", WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
return 1;
}
iResult = connect(ConnectSocket, result->ai_addr, (int)result->ai_addrlen);
freeaddrinfo(result);
if (iResult != SOCKET_ERROR)
break;
closesocket(ConnectSocket);
ConnectSocket = INVALID_SOCKET;
}
if (ConnectSocket == INVALID_SOCKET)
{
printf("Unable to connect to server!\n");
WSACleanup();
return 1;
}
#define DEFAULT_BUFLEN 512
const char* sendbuf = "this is a test";
char recvbuf[DEFAULT_BUFLEN];
// Send an initial buffer
iResult = send(ConnectSocket, sendbuf, (int)strlen(sendbuf), 0);
if (iResult == SOCKET_ERROR)
{
printf("send failed: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
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 ConnectSocket for receiving data
iResult = shutdown(ConnectSocket, SD_SEND);
if (iResult == SOCKET_ERROR)
{
printf("shutdown failed: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
// Receive data until the server closes the connection
do
{
iResult = recv(ConnectSocket, recvbuf, sizeof(recvbuf), 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);
shutdown(ConnectSocket, SD_RECEIVE);
closesocket(ConnectSocket);
WSACleanup();
return 0;
}
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'm working on a server that just connects a client and sends back what they type. So far it works well, but I want to make it multi-threaded so I can connect more clients, so they can "Talk." Can you guys refer me to any source code that I can look over to make some adjustments to my own code? Thanks. I would also like this to work on different networks, and I am not sure how to do this so some source code to that as well would work. Also I'm in eighth grade and haven't had a lot of experience so a well-explained answer would be appreciated.
int main() {
WSADATA wsaData;
// Initialize Winsock
int iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
if(iResult != 0)
{
printf("WSAStartup failed: %d\n", iResult);
return 1;
}
struct addrinfo *result = NULL,
hints;
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET; // Internet address family is unspecified so that either an IPv6 or IPv4 address can be returned
hints.ai_socktype = SOCK_STREAM; // Requests the socket type to be a stream socket for the TCP protocol
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);
WSACleanup();
return 1;
}
SOCKET ListenSocket = INVALID_SOCKET;
// 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;
}
// Setup the TCP listening socket
iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);
if (iResult == SOCKET_ERROR)
{
printf("bind failed: %d", WSAGetLastError());
freeaddrinfo(result);
closesocket(ListenSocket);
WSACleanup();
return 1;
}
freeaddrinfo(result);
// To listen on a socket
if ( listen(ListenSocket, SOMAXCONN) == SOCKET_ERROR)
{
printf("listen failed: %d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
SOCKET ClientSocket;
ClientSocket = INVALID_SOCKET;
// Accept a client socket
ClientSocket = accept(ListenSocket, NULL, NULL);
if (ClientSocket == INVALID_SOCKET)
{
printf("accept failed: %d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
char recvbuf[DEFAULT_BUFFER_LENGTH];
int iSendResult;
// reveice until the client shutdown the connection
do {
iResult = recv(ClientSocket, recvbuf, DEFAULT_BUFFER_LENGTH, 0);
if (iResult > 0)
{
char msg[DEFAULT_BUFFER_LENGTH];
memset(&msg, 0, sizeof(msg));
strncpy(msg, recvbuf, iResult);
printf("Received: %s\n", msg);
iSendResult = send(ClientSocket, recvbuf, iResult, 0);
if (iSendResult == SOCKET_ERROR)
{
printf("send failed: %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return 1;
}
printf("Bytes sent: %ld\n", iSendResult);
}
else if (iResult == 0)
printf("Connection closed\n");
else
{
printf("recv failed: %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
//return 1;
}
} while (iResult > 0);
// Free the resouces
closesocket(ListenSocket);
WSACleanup();
getchar();
return 0;
}
There are dozens of ways how to implement this solution.
First of all, it is better to design what approach is preferable for your educational purposes.
Considering network programming:
One of the best guides for network programming I can recommend is Beej's Guide.
Considering server-side architecture:
I would like to recommend C++ Concurrency in Action: Practical Multithreading by Anthony Williams for further reading.
Before you, a whole sea of possibilities - go for it!
Ps: considering general questions like this
It is better to look for an answer to such questions in softwareengineering forum.
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.