I'm working on an server-client structure, where I need to send a strings between the server and client, to do so I create a struct containing an int (packet type) and the string, with I then serialize and send over a tcp connection to the client with then deserialize it.
My problem is that I get the wrong string on the client side, the string on the server side is: "PO-1-25-25\nPO-2-50-50\n", but the string I get on the client side is "\x18=$".
The struct I'm sending (same struct on the client side as well)
struct Packet {
unsigned int packet_type;
std::string message;
void serialize(char * data) {
memcpy(data, this, sizeof(Packet));
}
void deserialize(char * data) {
memcpy(this, data, sizeof(Packet));
}
};
My code for sending the struct:
void sendGameStatePacket(unsigned int receiver){
const unsigned int packet_size = sizeof(Packet);
char packet_data[packet_size];
Packet packet;
packet.packet_type = GAME_STATE;
packet.message = message;
packet.serialize(packet_data);
network->sendToOne(packet_data, packet_size, receiver);
}
void sendToOne(char * packets, int totalSize, unsigned int socketID){
SOCKET currentSocket = sessions[socketID];
int iSendResult;
iSendResult = NetworkServices::sendMessage(currentSocket, packets,totalSize);
}
int sendMessage(SOCKET curSocket, char * message, int messageSize)
{
return send(curSocket, message, messageSize, 0);
}
Client side code for receiving the struct:
char network_data[MAX_PACKET_SIZE]; //MAX_PACKET_SIZE = 1000000
void AClientGame::update()
{
Packet packet;
int data_length = network->receivePackets(network_data);
unsigned int i = 0;
while (i < (unsigned int)data_length)
{
packet.deserialize(&(network_data[i]));
i += sizeof(Packet);
FString message;
message = packet.message.c_str();
UE_LOG(LogTemp, Warning, TEXT("Test: %s"), *message);
}
}
int receivePackets(char * recvbuf)
{
iResult = NetworkServices::receiveMessage(ConnectSocket, recvbuf, MAX_PACKET_SIZE);
}
int receiveMessage(SOCKET curSocket, char * buffer, int bufSize)
{
return recv(curSocket, buffer, bufSize, 0);
}
I've already checked that the package.message on the client side is the "\x18=$" string so the problem does not lie in the conversion from string to FString.
My Socket configuration is the following:
Server:
network::ServerNetwork::ServerNetwork(void)
{
// create WSADATA object
WSADATA wsaData;
// our sockets for the server
ListenSocket = INVALID_SOCKET;
ClientSocket = INVALID_SOCKET;
// address info for the server to listen to
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);
exit(1);
}
// set address information
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP; // TCP connection!!!
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();
exit(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();
exit(1);
}
// Set the mode of the socket to be nonblocking
u_long iMode = 1;
iResult = ioctlsocket(ListenSocket, FIONBIO, &iMode);
if (iResult == SOCKET_ERROR) {
printf("ioctlsocket failed with error: %d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
exit(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();
exit(1);
}
// no longer need address information
freeaddrinfo(result);
// start listening for new clients attempting to connect
iResult = listen(ListenSocket, SOMAXCONN);
if (iResult == SOCKET_ERROR) {
printf("listen failed with error: %d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
exit(1);
}
}
Client:
ClientNetwork::ClientNetwork()
{
// create WSADATA object
WSADATA wsaData;
// socket
ConnectSocket = INVALID_SOCKET;
// holds address info for socket to connect to
struct addrinfo *result = NULL,
*ptr = NULL,
hints;
// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed with error: %d\n", iResult);
exit(1);
}
// set address info
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP; //TCP connection!!!
//resolve 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();
exit(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();
exit(1);
}
// Connect to server.
iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
if (iResult == SOCKET_ERROR)
{
closesocket(ConnectSocket);
ConnectSocket = INVALID_SOCKET;
printf("The server is down... did not connect");
}
}
// no longer need address info for server
freeaddrinfo(result);
// if connection failed
if (ConnectSocket == INVALID_SOCKET)
{
printf("Unable to connect to server!\n");
WSACleanup();
exit(1);
}
// Set the mode of the socket to be nonblocking
u_long iMode = 1;
iResult = ioctlsocket(ConnectSocket, FIONBIO, &iMode);
if (iResult == SOCKET_ERROR)
{
printf("ioctlsocket failed with error: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
exit(1);
}
//disable nagle
char value = 1;
setsockopt(ConnectSocket, IPPROTO_TCP, TCP_NODELAY, &value, sizeof(value));
}
If anyone could explain why it is not working and how to fix it would be a great help
To start with, the expression sizeof(packet) will not give you the size of the structure including the string, because the string is most likely just a pointer.
And as the string is just a pointer, the data you copy in the serialization function is not the string but the pointer, and you can't send pointers over a network as they are unique for a single process on a single system.
You have to get the actual size of the string, and then allocate that amount of memory (plus whatever else you need to send) and use that size. And of course since the string can be of variable size, you need to send the actual size of the message as well in a message header.
struct Packet {
unsigned int packet_type;
std::string message;
void serialize(char * data) {
memcpy(data, this, sizeof(Packet));
}
void deserialize(char * data) {
memcpy(this, data, sizeof(Packet));
}
};
There is so much wrong with this, it's hard to know where to start. First, how would the caller of deserialize know how many bytes to pass it? Second, where's the code to actually serialize the message? Where's the code to compute the size of the structure with the data in it?
When you "serialize" something, you have to arrange it into a specific format of bytes. What is this format? There's no code to convert the message into any particular format at all.
This is code that expects things to work by magic.
If you're going to use TCP, before you write even a single line of code, write out a specification for the protocol you're going to use to exchange data at the byte level. Cover how messages will be delimited, which side will transmit when, and so on. You can look at some existing specifications for things like HTTP and SMTP to see what a specification should look like.
Serialization code should produce the precise byte format the specification calls for. Deserialization code should follow the specification's rules for delimiting messages.
you can make it so that in front of the "string" bytes you add for example two other bytes which would represent the "string" length (how many characters). so length would be first_byte*256+second_byte
this would be good for when you would send multiple strings from client to server (or other direction) in a single packet. you then simply calculate offsets.
Related
I have maybe a strange problem, but I can't get sockets working. I need to connect c++ app with node.js server, which has open socket. I have tried many sources, but none of them are working. One of that, that I have at the moment:
#define DEFAULT_BUFLEN 512
#define DEFAULT_PORT "3000"
int sock()
{
int argc = 2;
const char* argv[3];
argv[0] = "127.0.0.1";
argv[1] = "127.0.0.1";
WSADATA wsaData;
SOCKET ConnectSocket = INVALID_SOCKET;
struct addrinfo* result = NULL,
* ptr = NULL,
hints;
const char* sendbuf = "this is a test";
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);
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;
}
This is official microsoft code for the winsock client. Also, I have tried socket client code from here: https://www.geeksforgeeks.org/socket-programming-cc/
From the microsoft code I am getting that, data was sent, but node.js console remains empty: https://prnt.sc/zdhpsi
Node.js server code:
var port = 3000;
var io = require('socket.io')().listen(port);
console.log("Listening on port " + port);
/* Socket.IO events */
io.on("connection", function(socket){
console.log("new connection");
socket.on('test_text', (...args) => {
console.log("test text event received.", args);
});
socket.on('test_binary', (...args) => {
console.log("test binary event received", args);
if(args[0] instanceof Buffer)
{
console.log("test binary event received,binary length:"+ args[0].length);
}
});
socket.on('test ack',function()
{
var args =Array.prototype.slice.call(arguments);
if('object' == typeof args[0])
{
console.log("test combo received,object:");
console.log(JSON.stringify(args[0]));
}
if(args.length>1 && 'function' == typeof args[args.length - 1])
{
console.log('need ack for test combo');
var fn = args[args.length - 1];
fn('Got bin length:' + args[0].bin.length);//invoke ack callback function.
}
});
});
On c++ side I don't need to recieve anything, I just need to send the data
Maybe you know how this could be done? Thanks :)
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.
I am current writing a tcp server class and a ClientHandle class for a Project.
Is it possible, that the server class simply call the select on a fd_set only containing the listensocket to check if a new client connects?
Inside of the Handle i would like to do similar with the socket to check if I got a new request or if it is writeable before sending data to it.
Something like this?
int TCPServer::start()
{
WSADATA wsaData;
int iResult;
// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0)
{
printf("WSAStartup failed: %d\n", iResult);
return 1;
}
struct addrinfo *result = nullptr, *ptr = nullptr, 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(nullptr, DEFAULT_PORT, &hints, &result);
if (iResult != 0)
{
printf("getaddrinfo failed: %d\n", iResult);
WSACleanup();
return 1;
}
// Create a SOCKET for the server to listen for client connections
//create socket to listen for clients
m_listensocket = INVALID_SOCKET;
m_listensocket = socket(result->ai_family, result->ai_socktype,
result->ai_protocol);
if (m_listensocket == INVALID_SOCKET)
{
printf("Error at socket(): %ld\n", WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
return 1;
}
// Setup the TCP listening socket
iResult = bind(m_listensocket, result->ai_addr, (int)result->ai_addrlen);
if (iResult == SOCKET_ERROR)
{
printf("bind failed with error: %d\n", WSAGetLastError());
freeaddrinfo(result);
closesocket(m_listensocket);
WSACleanup();
return 1;
}
//free the result
freeaddrinfo(result);
//start listening
if (listen(m_listensocket, SOMAXCONN) == SOCKET_ERROR)
{
printf("Listen failed with error: %ld\n", WSAGetLastError());
closesocket(m_listensocket);
WSACleanup();
return 1;
}
//clear master set
FD_ZERO(&m_set);
//add the listen socket to the set
FD_SET(m_listensocket, &m_set);
return 0;
}
int TCPServer::acceptClient()
{
auto temp_set = m_set;
//setup the timeout to 100ms
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 100;
if (select(1, &temp_set, nullptr, nullptr, &tv) == -1)
{
//failed to select
perror("fail in select method!");
exit(4);
}
if (FD_ISSET(m_listensocket, &temp_set))
{
//we can accept a new
SOCKET ClientSocket;
ClientSocket = INVALID_SOCKET;
// Accept a client socket
struct sockaddr_storage their_addr; //stored address
int sin_size = sizeof their_addr;
ClientSocket = accept(m_listensocket,
reinterpret_cast<struct sockaddr *>(&their_addr),
&sin_size);
if (ClientSocket == INVALID_SOCKET)
{
printf("accept failed: %d\n", WSAGetLastError());
closesocket(m_listensocket);
WSACleanup();
return 1;
}
auto handle = new ClientHandle(ClientSocket);
//now do whatever you want
}
}
Is it possible, that the server class simply call the select on a fd_set only containing the listensocket to check if a new client connects?
Yes, that's what it's for.
Inside of the Handle i would like to do similar with the socket to check if I got a new request or if it is writeable before sending data to it.
Yes, that's what it's for.
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.