Here is a chat application code below which I'm having troubles with.
The chat system works by having a master server, where all clients connect to. So, here is the code for the master server.
#pragma comment(lib, "Ws2_32.lib")
#include <WinSock2.h>
#include <Windows.h>
#include <iostream>
using namespace std;
SOCKADDR_IN addr;
SOCKET sListen;
SOCKET sConnect;
SOCKET* Connections;
int addrlen = sizeof(addr);
int ConCounter = 0;
struct Buffer
{
int ID;
char Message[256];
};
int ServerThread(int ID)
{
Buffer sbuffer;
char* Recv = new char[256];
ZeroMemory(Recv, 256);
char* Send = new char[sizeof(Buffer)];
ZeroMemory(Send, sizeof(Buffer));
for(;; Sleep(10))
{
if(recv(Connections[ID], Recv, 256, NULL))
{
sbuffer.ID = ID;
memcpy(sbuffer.Message, Recv, 256);
memcpy(Send, &sbuffer, sizeof(Buffer));
for(int a = 0; a != ConCounter; a++)
{
if(Connections[a] == Connections[ID])
{
}
else
{
send(Connections[a], Send, sizeof(Buffer), NULL);
}
}
ZeroMemory(Recv, 256);
}
}
return 0;
}
int InitWinSock()
{
int RetVal = 0;
WSAData wsaData;
WORD DllVersion = MAKEWORD(2,1);
RetVal = WSAStartup(DllVersion, &wsaData);
return RetVal;
}
int main()
{
int RetVal = 0;
RetVal = InitWinSock();
if(RetVal != 0)
{
MessageBoxA(NULL, "Winsock startup failed", "Error", MB_OK | MB_ICONERROR);
exit(1);
}
Connections = (SOCKET*)calloc(64, sizeof(SOCKET));
sListen = socket(AF_INET, SOCK_STREAM, NULL);
sConnect = socket(AF_INET, SOCK_STREAM, NULL);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_port = htons(1234);
addr.sin_family = AF_INET;
bind(sListen, (SOCKADDR*)&addr, sizeof(addr));
listen(sListen, 64);
for(;; Sleep(50))
{
if(sConnect = accept(sListen, (SOCKADDR*)&addr, &addrlen))
{
Connections[ConCounter] = sConnect;
char* ID = new char[64];
ZeroMemory(ID, sizeof(ID));
itoa(ConCounter, ID, 10);
send(Connections[ConCounter], ID, sizeof(ID), NULL);
ConCounter = ConCounter + 1;
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE) ServerThread, (LPVOID)(ConCounter - 1), NULL, NULL);
}
}
return 0;
}
Here is the source for the client chat:
#pragma comment(lib, "Ws2_32.lib")
#include <WinSock2.h>
#include <Windows.h>
#include <iostream>
using namespace std;
SOCKADDR_IN addr;
SOCKET sConnect;
// For this we need to send two information at one time:
// 1. The main message
// 2. The ID
// To send more than one information I will use a struct
struct Buffer
{
int ID;
char Message[256];
};
int ClientThread()
{
Buffer sbuffer;
char buffer[sizeof(sbuffer)] = {0};
for(;; Sleep(10))
{
// The server will send a struct to the client
// containing message and ID
// But send only accepts a char as buffer parameter
// so here we need to recv a char buffer and then
// we copy the content of this buffer to our struct
if(recv(sConnect, buffer, sizeof(sbuffer), NULL))
{
memcpy(&sbuffer, buffer, sizeof(sbuffer));
cout << "<Client " << sbuffer.ID << ":> " << sbuffer.Message <<endl;
}
}
return 0;
}
int main()
{
system("cls");
int RetVal = 0;
WSAData wsaData;
WORD DllVersion = MAKEWORD(2,1);
RetVal = WSAStartup(DllVersion, &wsaData);
if(RetVal != 0)
{
MessageBoxA(NULL, "Winsock startup failed", "Error", MB_OK | MB_ICONERROR);
exit(1);
}
sConnect = socket(AF_INET, SOCK_STREAM, NULL);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_port = htons(1234);
addr.sin_family = AF_INET;
cout << "Connect to Masterserver? [ENTER]" <<endl;
getchar();
RetVal = connect(sConnect, (SOCKADDR*)&addr, sizeof(addr));
if(RetVal != 0)
{
MessageBoxA(NULL, "Could not connect to server", "Error", MB_OK | MB_ICONERROR);
main();
}
else
{
int ID;
char* cID = new char[64];
ZeroMemory(cID, 64);
recv(sConnect, cID, 64, NULL);
ID = atoi(cID);
cout << "Connected" <<endl;
cout << "You are Client No: " << ID <<endl;
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE) ClientThread, NULL, NULL, NULL);
for(;; Sleep(10))
{
char* buffer = new char[256];
ZeroMemory(buffer, 256);
cin >> buffer;
getchar();
send(sConnect, buffer, 256, NULL);
}
}
return 0;
}
Now, everything works fine except when you connect for example with two clients (run app twice) and close one of them the client that closed the application spam the chat with infinite messages which never stop! Any help fixing that?
I would like to ask someone if possible to help me comment the source code!
Updated code:
#pragma comment(lib, "Ws2_32.lib")
#include <WinSock2.h>
#include <Windows.h>
#include <iostream>
#include <string>
using namespace std;
SOCKADDR_IN addr;
SOCKET sConnect;
struct Buffer
{
int ID;
char Message[256];
};
int ClientThread()
{
Buffer sbuffer;
string buffer;
//char buffer[sizeof(sbuffer)] = {0};
for(;; Sleep(10))
{
if(recv(sConnect, buffer.c_str(), sizeof(sbuffer), NULL)!=SOCKET_ERROR)
{
strncpy(sbuffer.Message, buffer.c_str(), sizeof(sbuffer.Message));
cout << "<Client " << sbuffer.ID << ":> " << sbuffer.Message <<endl;
}
}
return 0;
}
int main()
{
system("cls");
int RetVal = 0;
WSAData wsaData;
WORD DllVersion = MAKEWORD(2,1);
RetVal = WSAStartup(DllVersion, &wsaData);
if(RetVal != 0)
{
MessageBoxA(NULL, "Winsock startup failed", "Error", MB_OK | MB_ICONERROR);
exit(1);
}
sConnect = socket(AF_INET, SOCK_STREAM, NULL);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_port = htons(1234);
addr.sin_family = AF_INET;
cout << "Connect to Masterserver? [ENTER]" <<endl;
getchar();
RetVal = connect(sConnect, (SOCKADDR*)&addr, sizeof(addr));
if(RetVal != 0)
{
MessageBoxA(NULL, "Could not connect to server", "Error", MB_OK | MB_ICONERROR);
main();
}
else
{
int ID;
char* cID = new char[64];
ZeroMemory(cID, 64);
recv(sConnect, cID, 64, NULL);
ID = atoi(cID);
cout << "Connected" <<endl;
cout << "You are Client No: " << ID <<endl;
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE) ClientThread, NULL, NULL, NULL);
for(;; Sleep(10))
{
char* buffer = new char[256];
ZeroMemory(buffer, 256);
getline(cin,buffer);
//cin >> buffer;
getchar();
send(sConnect, buffer, 256, NULL);
}
}
return 0;
}
When a client disconnects gracefully, recv() will return 0. When a client disconnects abnormally, or any other error occurs for that matter, recv() will return SOCKET_ERROR and you can then use WSAGetLastError() to find out why it errored. You need to handle both conditions and make the server "forget about the client" when recv() returns <= 0 (except in the specific case of SOCKET_ERROR/WSAEWOULDBLOCK, which is not a fatal error). Currently you are treating a socket error as if you had instead really received data from the client.
You are testing the return value of recv() against zero, but that is not what recv() returns on an error (SOCKET_ERROR is actually an alias for -1, and if (-1) evaluates as true, not false).
The reason your server gets stuck in a loop is because you are not using the return value of recv() correctly.
There are other problems with your code as well (abusing CreateThread(), for starters).
Try this instead:
#pragma comment(lib, "Ws2_32.lib")
#include <WinSock2.h>
#include <Windows.h>
#include <iostream>
using namespace std;
SOCKADDR_IN addr;
int addrlen;
SOCKET sListen;
SOCKET sConnect;
SOCKET Connections[64];
struct Buffer
{
int ID;
char Message[256];
};
bool doSend(SOCKET s, void *data, int datalen)
{
char pdata = (char*) data;
while (datalen > 0)
{
int numSent = send(s, pdata, datalen, NULL);
if (numSent < 1)
return false;
pdata += numSent;
datalen -= numSent;
}
return true;
}
DWORD WINAPI ServerThread(LPVOID lpParam)
{
int ID = (int) lpParam;
SOCKET sThisClient = Connections[ConID];
char cID[64];
ZeroMemory(cID, sizeof(cID));
itoa(ID, cID, 10);
if (doSend(sThisClient, cID, sizeof(cID)))
{
Buffer sbuffer;
sbuffer.ID = ID;
ZeroMemory(sbuffer.Message, sizeof(sbuffer.Message));
for (;; Sleep(10))
{
int numRecv = recv(sThisClient, sbuffer.Message, sizeof(sbuffer.Message), NULL);
if (numRecv < 1)
break;
for (int a = 0; a < 64; a++)
{
SOCKET sOtherClient = Connections[a];
if ((sOtherClient != INVALID_SOCKET) && (sOtherClient != sClient))
doSend(sOtherClient, &sbuffer, sizeof(Buffer));
}
}
ZeroMemory(sbuffer.Message, sizeof(sbuffer.Message));
}
closesocket(Connections[ID]);
Connections[ID] = INVALID_SOCKET;
return 0;
}
int main()
{
for (int i = 0; i < 64; ++i)
Connections[i] = INVALID_SOCKET;
WSAData wsaData;
int RetVal = WSAStartup(MAKEWORD(2,1), &wsaData);
if (RetVal != 0)
{
MessageBoxA(NULL, "Winsock startup failed", "Error", MB_OK | MB_ICONERROR);
exit(1);
}
sListen = socket(AF_INET, SOCK_STREAM, NULL);
if (sListen == INVALID_SOCKET)
{
MessageBoxA(NULL, "Socket create failed", "Error", MB_OK | MB_ICONERROR);
exit(1);
}
sConnect = socket(AF_INET, SOCK_STREAM, NULL);
if (sConnect == INVALID_SOCKET)
{
MessageBoxA(NULL, "Socket create failed", "Error", MB_OK | MB_ICONERROR);
exit(1);
}
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_port = htons(1234);
addr.sin_family = AF_INET;
if (bind(sListen, (SOCKADDR*)&addr, sizeof(addr)) != 0)
{
MessageBoxA(NULL, "bind failed", "Error", MB_OK | MB_ICONERROR);
exit(1);
}
if (listen(sListen, 64) != 0)
{
MessageBoxA(NULL, "listen failed", "Error", MB_OK | MB_ICONERROR);
exit(1);
}
for(;; Sleep(50))
{
addrlen = sizeof(addr);
sConnect = accept(sListen, (SOCKADDR*)&addr, &addrlen);
if (sConnect != INVALID_SOCKET)
{
int ConID = -1;
for (int i = 0; i < 64; ++i)
{
if (Connections[i] == INVALID_SOCKET);
{
ConID = i;
break;
}
}
if (ConID == -1)
{
closesocket(sConnect);
continue;
}
Connections[ConID] = sConnect;
HANDLE hThread = CreateThread(NULL, NULL, &ServerThread, (LPVOID)ConID, NULL, NULL);
if (!hThread)
{
closesocket(sConnect);
Connections[ConID] = INVALID_SOCKET;
continue;
}
CloseHandle(hThread);
}
}
return 0;
}
.
#pragma comment(lib, "Ws2_32.lib")
#include <WinSock2.h>
#include <Windows.h>
#include <iostream>
using namespace std;
SOCKADDR_IN addr;
SOCKET sConnect;
struct Buffer
{
int ID;
char Message[256];
};
bool doRecv(SOCKET s, void *data, int datalen)
{
char pdata = (char*) data;
while (datalen > 0)
{
int numRecv = recv(s, pdata, datalen, NULL);
if (numRecv < 1)
return false;
pdata += numRecv;
datalen -= numRecv;
}
return true;
}
DWORD WINAPI ClientThread(LPVOID lpParam)
{
Buffer sbuffer;
for(;; Sleep(10))
{
if (!doRecv(sConnect, &sbuffer, sizeof(sbuffer)))
break;
cout << "<Client " << sbuffer.ID << ":> " << sbuffer.Message << endl;
}
return 0;
}
int main()
{
system("cls");
WSAData wsaData;
int RetVal = WSAStartup(MAKEWORD(2,1), &wsaData);
if (RetVal != 0)
{
MessageBoxA(NULL, "Winsock startup failed", "Error", MB_OK | MB_ICONERROR);
exit(1);
}
sConnect = socket(AF_INET, SOCK_STREAM, NULL);
if (sConnect == INVALID_SOCKET)
{
MessageBoxA(NULL, "Socket create failed", "Error", MB_OK | MB_ICONERROR);
exit(1);
}
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_port = htons(1234);
addr.sin_family = AF_INET;
do
{
cout << "Connect to Masterserver? [ENTER]" << endl;
getchar();
RetVal = connect(sConnect, (SOCKADDR*)&addr, sizeof(addr));
if (RetVal == 0)
break;
MessageBoxA(NULL, "Could not connect to server", "Error", MB_OK | MB_ICONERROR);
}
while (true);
char cID[64];
ZeroMemory(cID, 64);
if (!doRecv(sConnect, cID, 64))
exit(1);
int ID = atoi(cID);
cout << "Connected" << endl;
cout << "You are Client ID: " << ID << endl;
if (!CreateThread(NULL, NULL, &ClientThread, NULL, NULL, NULL))
exit(1);
for(;; Sleep(10))
{
string buffer;
getline(cin, buffer);
doSend(sConnect, buffer.c_str(), buffer.length());
}
return 0;
}
Update: Given your recent update, you still have problems with your client code. Did you even try the code I gave you above? Below is a fix for your recent code, though I still suggest you review the code above, which addresses a lot of other issues with your original code:
#pragma comment(lib, "Ws2_32.lib")
#include <WinSock2.h>
#include <Windows.h>
#include <iostream>
#include <string>
using namespace std;
SOCKADDR_IN addr;
SOCKET sConnect;
struct Buffer
{
int ID;
char Message[256];
};
int ClientThread()
{
Buffer sbuffer;
char buffer[sizeof(sbuffer)];
for(;; Sleep(10))
{
int numRead = recv(sConnect, &buffer, sizeof(buffer), NULL);
if (numRead < 1) break;
memcpy(&sbuffer, buffer, numRead);
cout << "<Client " << sbuffer.ID << ":> " << sbuffer.Message << endl;
}
return 0;
}
int main()
{
system("cls");
int RetVal = 0;
WSAData wsaData;
WORD DllVersion = MAKEWORD(2,1);
RetVal = WSAStartup(DllVersion, &wsaData);
if (RetVal != 0)
{
MessageBoxA(NULL, "Winsock startup failed", "Error", MB_OK | MB_ICONERROR);
exit(1);
}
sConnect = socket(AF_INET, SOCK_STREAM, NULL);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_port = htons(1234);
addr.sin_family = AF_INET;
do
{
cout << "Connect to Masterserver? [ENTER]" <<endl;
getchar();
RetVal = connect(sConnect, (SOCKADDR*)&addr, sizeof(addr));
if (RetVal == 0) break;
MessageBoxA(NULL, "Could not connect to server", "Error", MB_OK | MB_ICONERROR);
}
while (true);
char cID[64];
ZeroMemory(cID, 64);
recv(sConnect, cID, 64, NULL);
int ID = atoi(cID);
cout << "Connected" << endl;
cout << "You are Client No: " << ID << endl;
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE) ClientThread, NULL, NULL, NULL);
for(;; Sleep(10))
{
string buffer;
getline(cin, buffer);
if (send(sConnect, buffer.c_str(), buffer.length(), NULL) < 1)
exit(1);
}
return 0;
}
Related
I'm trying to make a simple heartbeat check from client to server and vice-versa, if connection on either is broken off unexpectedly it prints a message and calls closesocket.
I spent 8 hours on this and it still isn't acceptable to my mentor. Right now I got something that works, but if breakpoint is placed before while loop and connected client is forcefully closed, trying to go past breakpoint causes crash when it should break the loop and write out error.
Server side code:
int main(int argc, char *argv[])
{
SOCKET s, sa;
WSAData oWSAData;
WORD wVersion = 0x0001;
WSAStartup(wVersion, &oWSAData);
s = socket(AF_INET, SOCK_STREAM, 0);
sockaddr_in srv_address;
memset(&srv_address, 0, sizeof(srv_address));
srv_address.sin_family = AF_INET;
srv_address.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
srv_address.sin_port = htons(1099);
bind(s, (sockaddr*) &srv_address, sizeof(srv_address));
int l = listen(s, 10);
if (l < 0)
printf("Listen error\n");
else
{
printf("Listen OK. Listening on port %u\n",
htons(srv_address.sin_port));
sa = accept(s, NULL, NULL);
while (true)
{
char buffer[1000];
int nRecvLen = recv(sa, buffer, 999, 0);
buffer[nRecvLen] = '\0';
int r = recv(sa, NULL, 0, 0);
if (r == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET)
{
printf("Konekcija je naglo prekinuta!\n");
break;
}
else
{
if (nRecvLen > 0)
{
for (int i = 0; i < nRecvLen; i++)
{
cout << buffer[i];
}
}
}
}
closesocket(sa);
closesocket(s);
}
WSACleanup();
return 0;
}
and client side:
int main()
{
SOCKET s;
WSAData oWSAData;
WORD wVersion = 0x0001;
WSAStartup(wVersion, &oWSAData);
s = socket(AF_INET, SOCK_STREAM, 0);
sockaddr_in srv_address;
memset(&srv_address, 0, sizeof(srv_address));
srv_address.sin_family = AF_INET;
srv_address.sin_addr.S_un.S_un_b.s_b1 = xxx;
srv_address.sin_addr.S_un.S_un_b.s_b2 = xxx;
srv_address.sin_addr.S_un.S_un_b.s_b3 = x;
srv_address.sin_addr.S_un.S_un_b.s_b4 = xxx;
srv_address.sin_port = htons(1099);
int c = connect(s, (sockaddr*) &srv_address, sizeof(srv_address));
if (c < 0)
{
printf("Connection error\n");
cout << (WSAGetLastError());
}
else
{
string l = "Heartbeat\n";
int p = l.size();
char buff[1000];
strcpy_s(buff, l.c_str());
printf("Connected\n");
while (true)
{
if (send(s, buff, p, 0) > 0)
{
Sleep(1000);
}
else
{
printf("Konekcija je naglo prekinuta\n");
shutdown(s, SD_BOTH);
closesocket(s);
break;
}
}
WSACleanup();
return 0;
}
}
I'm trying to implement transmission of files through UDP protocol in C++.
What I've got is the server which can send a file requested by a client, but it only works for .txt files. When I try to do the same with image or executable, the transmission corrupts and the file is about 0 KB.
Server:
#include <winsock2.h>
#include <stdio.h>
#include <iostream>
#include <sstream>
#pragma comment(lib, "ws2_32.lib")
#define cipherKey 'S'
int const bufferSize = 512;
char buffer[bufferSize];
void clearBuf(char* b)
{
int i;
for (i = 0; i < bufferSize; i++)
b[i] = '\0';
}
char* notFound = "File not found.";
char Cipher(char ch)
{
return ch ^ cipherKey;
}
int sendFile(FILE* file, char* buffer, int s)
{
int i, len;
if (file == NULL)
{
strcpy(buffer, notFound);
len = strlen(notFound);
buffer[len] = EOF;
return 1;
}
char ch, ch2;
for (i = 0; i < s; i++)
{
ch = fgetc(file);
ch2 = Cipher(ch);
buffer[i] = ch2;
if (ch == EOF)
return 1;
}
return 0;
}
int main()
{
WSADATA wsaData;
int wynik_winsock = WSAStartup(MAKEWORD(2,2), &wsaData);
if(wynik_winsock != 0)
{
exit(1);
}
SOCKET socketServer;
socketServer = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(socketServer == INVALID_SOCKET)
{
WSACleanup();
exit(1);
}
char* ipAdd = "127.0.0.1";
int port = 1234;
SOCKADDR_IN server;
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = inet_addr(ipAdd);
if(bind(socketServer, (SOCKADDR *)&server, sizeof(server)) == SOCKET_ERROR)
{
closesocket(socketServer);
WSACleanup();
exit(1);
}
std::cout << "Waiting." << std::endl;
SOCKADDR_IN client;
int len_client = sizeof(client);
FILE* file;
if(recvfrom(socketServer, buffer, bufferSize, 0, (SOCKADDR *)&client, &len_client) == SOCKET_ERROR) //Odbiór danych od clienta wraz z kontrolą błędów.
{
closesocket(socketServer);
WSACleanup();
exit(1);
}
else
{
file = fopen(buffer, "rb");
std::cout << "Filename: " << buffer << std::endl;
if(file == NULL)
{
std::cout << "Couldnt open a file." << std::endl;
}
else
{
while (true)
{
if(sendFile(file, buffer, bufferSize))
{
sendto(socketServer, buffer, bufferSize, 0, (SOCKADDR *)&client, len_client);
break;
}
sendto(socketServer, buffer, bufferSize, 0, (SOCKADDR *)&client, len_client);
clearBuf(buffer);
}
fclose(file);
}
}
closesocket(socketServer);
WSACleanup();
system("pause");
return 0;
}
Client:
#include <winsock2.h>
#include <stdio.h>
#include <iostream>
#include <sstream>
#include <string.h>
#pragma comment(lib, "ws2_32.lib")
#define cipherKey 'S'
int const bufferSize = 512;
char buffer[bufferSize];
void clearBuf(char* b)
{
int i;
for (i = 0; i < bufferSize; i++)
b[i] = '\0';
}
char Cipher(char ch)
{
return ch ^ cipherKey;
}
int recvFile(char* buffer, int s, FILE* file)
{
int i;
char ch;
for (i = 0; i < s; i++)
{
ch = buffer[i];
ch = Cipher(ch);
if (ch == EOF)
{
return 1;
}
else
{
fprintf(file, "%c", ch);
}
}
return 0;
}
int main()
{
WSADATA wsaData;
int wynik_winsock = WSAStartup(MAKEWORD(2,2), &wsaData);
if(wynik_winsock != 0)
{
exit(1);
}
SOCKET socketClient;
socketClient = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(socketClient == INVALID_SOCKET)
{
WSACleanup();
exit(1);
}
char* ipAdd = "127.0.0.1";
int port = 1234;
SOCKADDR_IN server;
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = inet_addr(ipAdd);
int serverSizeOf = sizeof(server);
std::cout << "Name of file to send: ";
std::cin >> buffer;
if(sendto(socketClient, buffer, bufferSize, 0, (SOCKADDR *)&server, serverSizeOf) == SOCKET_ERROR)
{
closesocket(socketClient);
WSACleanup();
exit(1);
}
FILE* file;
file = fopen(buffer, "ab");
while (true)
{
clearBuf(buffer);
if(recvfrom(socketClient, buffer, bufferSize, 0, (SOCKADDR *)&server, &serverSizeOf) == SOCKET_ERROR)
{
closesocket(socketClient);
WSACleanup();
exit(1);
}
if (recvFile(buffer, bufferSize, file))
{
break;
}
fclose(file);
}
closesocket(socketClient);
WSACleanup();
system("pause");
return 0;
}
To do what I said above, I used the tutorial: C program for file Transfer using UDP (Linux).
How can I adapt the code to send other files than .txt only? Thank you in advance.
As said in the comments above you need a data type where EOF has a different value from all other character values, char is inadequate in this respect, especially when you are dealing with binary data.
The following change should improve things
int sendFile(FILE* file, char* buffer, int s)
{
...
for (i = 0; i < s; i++)
{
int ch = fgetc(file);
if (ch == EOF)
return 1;
buffer[i] = Cipher(ch);
}
...
I've decided to change nearly everything in the original solution I tried to implement. The most important changes are reading file using fread and writing it using fwrite.
The file is send in parts of 512 bytes (or less) and those parts are counted in the variable.
If the file requested by a client doesn't exist on the server, special information is sent and the file created to save data is deleted.
Now the program works as expected even for executables and SHA256 of both files, original and received, are the same.
Server:
//SERVER
#include <winsock2.h>
#include <stdio.h>
#include <iostream>
#include <sstream>
#include <algorithm>
#include <ctime>
#pragma comment(lib, "ws2_32.lib")
int main()
{
WSADATA wsaData;
int winsock_result = WSAStartup(MAKEWORD(2,2), &wsaData);
if(winsock_result != 0)
{
exit(1);
}
SOCKET server_socket;
server_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(server_socket == INVALID_SOCKET)
{
WSACleanup();
exit(1);
}
char* ip_address = "127.0.0.1";
int port = 6666;
SOCKADDR_IN server;
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = inet_addr(ip_address);
if(bind(server_socket,(SOCKADDR *)&server, sizeof(server)) == SOCKET_ERROR)
{
closesocket(server_socket);
WSACleanup();
exit(1);
}
std::cout << "Waiting for data." << std::endl;
SOCKADDR_IN client;
int client_sizeof = sizeof(client);
int const buffer_size = 512;
char buffer[buffer_size];
if(recvfrom(server_socket, buffer, buffer_size, 0,(SOCKADDR *)&client, &client_sizeof) == SOCKET_ERROR)
{
closesocket(server_socket);
WSACleanup();
exit(1);
}
else
{
FILE* file;
file = fopen(buffer, "rb");
std::cout << "Filename: " << buffer << std::endl;
if(file == NULL)
{
std::cout << "Couldn't open the file." << std::endl;
strcpy(buffer, "NOFILE");
if(sendto(server_socket, buffer, buffer_size, 0,(SOCKADDR *)&client, client_sizeof) == SOCKET_ERROR)
{
fclose(file);
closesocket(server_socket);
WSACleanup();
exit(1);
}
}
fseek(file, 0, SEEK_END);
int file_size = ftell(file);
size_t reading_size;
int part = 0;
const clock_t begin_time = clock();
while((part * buffer_size) < file_size)
{
fseek(file, (part * buffer_size), SEEK_SET);
reading_size = fread(buffer, 1, buffer_size, file);
if(sendto(server_socket, buffer, reading_size, 0,(SOCKADDR *)&client, client_sizeof) == SOCKET_ERROR)
{
fclose(file);
closesocket(server_socket);
WSACleanup();
exit(1);
}
part++;
}
std::cout << "Sent " << part << " parts of " << buffer_size << " bytes." << std::endl;
std::cout << "Time of sending file: " << float( clock () - begin_time ) / CLOCKS_PER_SEC << " seconds." << std::endl;
strcpy(buffer, "QUIT");
if(sendto(server_socket, buffer, buffer_size, 0,(SOCKADDR *)&client, client_sizeof) == SOCKET_ERROR)
{
fclose(file);
closesocket(server_socket);
WSACleanup();
exit(1);
}
fclose(file);
}
closesocket(server_socket);
WSACleanup();
system("pause");
return 0;
}
Client:
//CLIENT
#include <winsock2.h>
#include <stdio.h>
#include <iostream>
#include <sstream>
#include <string.h>
#pragma comment(lib, "ws2_32.lib")
int main()
{
WSADATA wsaData;
int winsock_result = WSAStartup(MAKEWORD(2,2), &wsaData);
if(winsock_result != 0)
{
exit(1);
}
SOCKET client_socket;
client_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(client_socket == INVALID_SOCKET)
{
WSACleanup();
exit(1);
}
char* ip_address = "127.0.0.1";
int port = 6666;
SOCKADDR_IN server;
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = inet_addr(ip_address);
int server_sizeof = sizeof(server);
int const buffer_size = 512;
char buffer[buffer_size];
std::cout << "Name of the requested file: ";
std::cin >> buffer;
char filename[buffer_size];
strcpy(filename, buffer);
if(sendto(client_socket, buffer, buffer_size, 0,(SOCKADDR *)&server, server_sizeof) == SOCKET_ERROR)
{
closesocket(client_socket);
WSACleanup();
exit(1);
}
FILE* file;
file = fopen(filename, "wb");
int received_size = 0;
while(true)
{
received_size = recvfrom(client_socket, buffer, buffer_size, 0,(SOCKADDR *)&server, &server_sizeof);
if(received_size == SOCKET_ERROR)
{
fclose(file);
closesocket(client_socket);
WSACleanup();
exit(1);
}
if(strcmp(buffer, "NOFILE") == 0)
{
std::cout << "The file does not exist on the server." << std::endl;
fclose(file);
remove(filename);
break;
}
else if(strcmp(buffer, "QUIT") == 0)
{
std::cout << "Transmission ended by the server." << std::endl;
break;
}
fwrite(buffer, sizeof(char), received_size, file);
}
fclose(file);
closesocket(client_socket);
WSACleanup();
system("pause");
return 0;
}
I am totally new to socket programming.I'm trying to understand the different practices when it comes to socket programming and handling multiple connections.
I have achieved the following functionality so for.
server connect to multiple clients. c1,c2,c3......cn.
server send a time-slot to client.
server send message to client for transmitting message.(to transmit
message in limited duration)
client send message within limited time period
once first connected client is time over server send message to next
client for transmitting message and so.
Here is my working code.
Server.cpp
#include "stdafx.h"
#pragma comment(lib,"ws2_32.lib")
#include <WinSock2.h>
#include <string>
#include <iostream>
#include<conio.h>
#define DEFAULT_NC_TIME 20
#define MAX_CLIENTS 2
SOCKET Connections[MAX_CLIENTS];
int TotalConnections = 0;
#include <vector>
#include<time.h>
void wait ( int seconds )
{
clock_t endwait;
endwait = clock () + seconds * CLOCKS_PER_SEC ;
while (clock() < endwait) {}
}
void ClientHandlerThread(int index) //index = the index in the SOCKET Connections array
{
int bufferlength; //Holds the length of the message a client sent
while (true)
{
recv(Connections[index], (char*)&bufferlength, sizeof(int), NULL); //get buffer length
char * buffer = new char[bufferlength+1]; //Allocate buffer
buffer[bufferlength] = '\0';
recv(Connections[index], buffer, bufferlength, NULL); //get buffer message from client
std::cout << buffer << std::endl;
delete[] buffer;
}
}
void emitMessageToClient(int indexOfClient, int ts){
std::string buftest="You are ready to transmit Message.\n";
int size = buftest.size(); //Get size of message in bytes and store it in int size
send(Connections[indexOfClient], (char*)&ts, sizeof(int), NULL); //send Timeslot Duration to the client i
send(Connections[indexOfClient], (char*)&size, sizeof(int), NULL); //send Size of message
send(Connections[indexOfClient], buftest.c_str(), buftest.size(), NULL); //send Message
}
int main()
{
//Winsock Startup
WSAData wsaData;
WORD DllVersion = MAKEWORD(2, 1);
if (WSAStartup(DllVersion, &wsaData) != 0)
{
MessageBoxA(NULL, "WinSock startup failed", "Error", MB_OK | MB_ICONERROR);
return 0;
}
SOCKADDR_IN addr;
int addrlen = sizeof(addr);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_port = htons(1111);
addr.sin_family = AF_INET;
SOCKET sListen = socket(AF_INET, SOCK_STREAM, NULL);
bind(sListen, (SOCKADDR*)&addr, sizeof(addr));
listen(sListen, SOMAXCONN);
SOCKET newConnection; //Socket to hold the client's connection
int ConnectionCounter = 0; //# of client connections
for (int i = 0; i < MAX_CLIENTS; i++)
{
newConnection = accept(sListen, (SOCKADDR*)&addr, &addrlen);
if (newConnection == 0)
{
std::cout << "Failed to accept the client's connection." << std::endl;
}
else
{
std::cout << "Client "<<i<<" Connected!" << std::endl;
Connections[i] = newConnection;
TotalConnections += 1; //Increment total # of clients that have connected
}
}
int ts = DEFAULT_NC_TIME / (TotalConnections + 1); // caculating time slot for each of connected node including server
std::cout<<"\nPress any key to start transmission: ";
getche();
for(int i= 0; i < TotalConnections; i++){
emitMessageToClient(i, ts);
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)ClientHandlerThread, (LPVOID)(i), NULL, NULL);
wait(ts);
if(i == (TotalConnections - 1)){
i = 0;
}
}
system("pause");
return 0;
}
Client.cpp
#include "stdafx.h"
#pragma comment(lib,"ws2_32.lib") //Required for WinSock
#include <WinSock2.h> //For win sockets
#include <string> //For std::string
#include <iostream> //For std::cout, std::endl, std::cin.getline
#include <time.h>
#include <vector>
#include <conio.h>
SOCKET Connection;//This client's connection to the server
void wait ( int seconds )
{
clock_t endwait;
endwait = clock () + seconds * CLOCKS_PER_SEC ;
while (clock() < endwait) {}
}
void ClientThread()
{
int bufferlength; //Holds the length of the message we are receiving
int ts; // holds timeslot duration for this client
while (true)
{
recv(Connection, (char*)&ts, sizeof(int), NULL); //receive timeslot
recv(Connection, (char*)&bufferlength, sizeof(int), NULL); //receive bufferlength
char * buffer = new char[bufferlength+1];
buffer[bufferlength] = '\0';
recv(Connection, buffer, bufferlength, NULL);
std::cout << buffer << std::endl; //print out buffer
std::string userinput; //holds the user's chat message
for (int n= ts; n>0; n--)
{
userinput = "First client sending message...";
int bufferlength = userinput.size(); //Find buffer length
send(Connection, (char*)&bufferlength, sizeof(int), NULL); //Send length of buffer
send(Connection, userinput.c_str(), bufferlength, NULL); //Send buffer
wait (1);
}
delete[] buffer; //Deallocate buffer
}
}
int main()
{
//Winsock Startup
WSAData wsaData;
WORD DllVersion = MAKEWORD(2, 1);
if (WSAStartup(DllVersion, &wsaData) != 0)
{
MessageBoxA(NULL, "Winsock startup failed", "Error", MB_OK | MB_ICONERROR);
return 0;
}
SOCKADDR_IN addr;
int sizeofaddr = sizeof(addr);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_port = htons(1111);
addr.sin_family = AF_INET;
Connection = socket(AF_INET, SOCK_STREAM, NULL);
if (connect(Connection, (SOCKADDR*)&addr, sizeofaddr) != 0)
{
MessageBoxA(NULL, "Failed to Connect", "Error", MB_OK | MB_ICONERROR);
return 0; //Failed to Connect
}
std::cout << "Connected!" << std::endl;
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)ClientThread, NULL, NULL, NULL);
getche();
return 0;
}
once server send message to all his connected clients.Server wants to send message again to his First client to transmit message again.(cyclically)
Problem
How can I achieve if server talk all his clients c1,c2,c3 to Cn and again talk to his first client C1.(cyclically) I have used this check
if(i == (TotalConnections - 1)){
i = 0;
}
but it crash the program.
By setting i back to 0 you create an infinite loop because i never gets large enough for the loop to end.
for(int i= 0; i < TotalConnections; i++) {
emitMessageToClient(i, ts);
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)ClientHandlerThread, (LPVOID)(i), NULL, NULL);
wait(ts);
// This will create an infinite loop
if(i == (TotalConnections - 1))
{
i = 0;
}
}
What you can do is wait until the loop has finished to re-visit the first connection:
for(int i= 0; i < TotalConnections; i++) {
emitMessageToClient(i, ts);
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)ClientHandlerThread, (LPVOID)(i), NULL, NULL);
wait(ts);
}
// now the loop is over, revisit the first connection
// if it exists
if(TotalConnections > 0) {
emitMessageToClient(0, ts);
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)ClientHandlerThread, (LPVOID)(0), NULL, NULL);
wait(ts);
}
I'm writing a server in C++ on windows. On connection, the server creates two threads to handle the request - one constantly reading the input from the socket and the second constantly writes output to it. I'm experiencing problems with it because when there's no input from the client but output to send to it thread #1 is blocking on recv() making thread two block on send() and not write it's data.
Is there a way to send() to a socket although it is already in blocking recv()?
Thanks
EDIT:
I took out the relevant code and reproduced the issue. if I comment out the AfxThreadCall to the readThread, the data is being sent. otherwise it isn't. the client is a simple client that prints what it recieves.
SOCKET m_sockIOSrv = NULL;
SOCKET m_sockIO = NULL;
UINT readThread(LPVOID args)
{
WSABUF DataBuf;
DWORD RecvBytes;
DWORD Flags = 0 ;
char buf[4096];
DataBuf.len = 4096;
DataBuf.buf = buf;
while (true)
{
int ret = WSARecv(m_sockIO, &DataBuf, 1, &RecvBytes, &Flags, NULL, NULL); // Read some data...
if (ret == SOCKET_ERROR)
{
printf("ERROR: WSARecv: %d", WSAGetLastError());
return 1;
}
printf("jobleader - got %d from recv\n", RecvBytes);
}
return 0;
}
UINT writeThread(LPVOID args)
{
WSABUF DataBuf;
DWORD SendBytes;
DWORD Flags;
char buf[] = { 'a', 'a', 'a' };
DataBuf.len = 3;
DataBuf.buf = buf;
while (true)
{
DWORD dwWritten;
WSASend(m_sockIO, &DataBuf, 1, &dwWritten, 0, NULL, NULL);
}
return 0;
}
SOCKET ServerSocket(unsigned int & port) {
// Our server socket
SOCKET s = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0);
if (s == INVALID_SOCKET)
return s;
SOCKADDR sa;
sa.sa_family = AF_INET;
memset(sa.sa_data, 0, sizeof(sa.sa_data));
if (bind(s, &sa, sizeof(sa))) {
closesocket(s);
return INVALID_SOCKET;
}
struct sockaddr_in sa_in;
int sz = sizeof(sa_in);
if (getsockname(s, (struct sockaddr *)&sa_in, &sz)) {
closesocket(s);
return INVALID_SOCKET;
}
if (listen(s, 50)) {
closesocket(s);
return INVALID_SOCKET;
}
port = ntohs(sa_in.sin_port);
return s;
}
int _tmain(int argc, _TCHAR* argv[])
{
unsigned int ioPort = 0;
WORD wVersionRequested = MAKEWORD(2, 2);
WSADATA wsaData;
if (WSAStartup(wVersionRequested, &wsaData))
{
if (!AfxSocketInit())
{
printf("ERROR: Unable to initialize WinSock DLL");
exit(2);
}
}
m_sockIOSrv = ServerSocket(ioPort);
if (m_sockIOSrv == INVALID_SOCKET) {
int lastError = GetLastError();
printf("ERROR: StartIOSockets Failed to start socket %d.\n",lastError);
return 1;
}
printf("Listening on %d\n", ioPort);
m_sockIO = accept(m_sockIOSrv, NULL, NULL);
AfxBeginThread(&readThread, (LPVOID)NULL);
AfxBeginThread(&writeThread, (LPVOID)NULL);
getchar();
}
I’ve recently started learning IOCP on Windows and been reading the following article:
http://www.codeproject.com/Tips/95363/Another-TCP-echo-server-using-IOCP
You can download the sample for the article from:
http://dl.dropbox.com/u/281215/documentation/iocp-1.00.html
The sample contains two simple applications – iocp_echo_server and TcpEchoClient.
I understand that IOCP is usually used on the server side of the client/server model but I’d like to create a client using IOCP.
I’ve so far tried modifying the client sample above so that whenever the server sends a response to the client, it gets picked up automatically, however it doesn’t work.
I’ve left iocp_echo_server.c as is. My modified version of TcpEchoClient.c looks like:
//TcpEchoClient.c - a minimalistic echo client
// -----------------------------------------------------------------------------
// C language includes
#include <stdio.h>
#include <winsock2.h>
#include "mswsock.h" // for AcceptEx
#include <stdlib.h> // exit
#include <string.h>
// Windows includes
#include <windows.h>
#pragma warning(disable: 4996) // sprintf
// -----------------------------------------------------------------------------
// configuration
enum
{
BUFLEN = 1000,
SERVICE_PORT = 4000,
SERVER_ADDRESS = INADDR_LOOPBACK
};
enum // socket operations
{
OP_NONE,
OP_ACCEPT,
OP_READ,
OP_WRITE
};
typedef struct _SocketState // socket state & control
{
char operation;
SOCKET socket;
DWORD length;
char buf[1024];
} SocketState;
// variables
static HANDLE cpl_port;
static SOCKET sock;
static SocketState sock_state;
static WSAOVERLAPPED sock_ovl;
static LPFN_ACCEPTEX pfAcceptEx;
static GUID GuidAcceptEx = WSAID_ACCEPTEX;
static int msgNumber;
static char msgBuf[BUFLEN];
static struct sockaddr_in sin;
// prototypes
static void createConnection(void);
static void createSocket(void);
static void init(void);
static void initWinsock(void);
static void prepareEndpoint(void);
static void recvBuffer(void);
static void run(void);
static void sendBuffer(void);
static SOCKET create_accepting_socket(void);
static void create_io_completion_port(void);
static BOOL get_completion_status(DWORD*, SocketState**,WSAOVERLAPPED**);
// -----------------------------------------------------------------------------
void main(void)
{
init();
run();
}
// -----------------------------------------------------------------------------
static void createConnection(void)
{
printf("* connecting\n");
if (WSAConnect(sock, (LPSOCKADDR)&sin, sizeof(sin), NULL, NULL, NULL, NULL) == SOCKET_ERROR)
{
int err = WSAGetLastError();
printf("* error %d in connect\n", err);
exit(1);
}
printf("* connected\n");
}
// -----------------------------------------------------------------------------
static void createSocket(void)
{
sock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
if (sock == INVALID_SOCKET)
{
int err = WSAGetLastError();
printf("* error %d creating socket\n", err);
exit(1);
}
// for use by AcceptEx
sock_state.socket = 0; // to be updated later
sock_state.operation = OP_ACCEPT;
if (CreateIoCompletionPort((HANDLE)sock, cpl_port, (ULONG_PTR)&sock_state, 0) != cpl_port)
{
int err = WSAGetLastError();
printf("* error %d in listener\n", err);
exit(1);
}
}
// -----------------------------------------------------------------------------
static void init(void)
{
initWinsock();
create_io_completion_port();
createSocket();
prepareEndpoint();
createConnection();
}
// -----------------------------------------------------------------------------
static void initWinsock(void)
{
WSADATA wsaData;
if (WSAStartup(0x202, &wsaData) == SOCKET_ERROR)
{
int err = WSAGetLastError();
printf("* error %d in WSAStartup\n", err);
exit(1);
}
}
// -----------------------------------------------------------------------------
static void prepareEndpoint(void)
{
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = htonl(SERVER_ADDRESS);
sin.sin_port = htons(SERVICE_PORT);
// bind_listening_socket()
{
//if (bind(sock, (SOCKADDR*)&sin, sizeof(sin)) == SOCKET_ERROR)
//{
// printf("* error in bind!\n");
// exit(1);
//}
}
// start_listening()
{
//if (listen(sock, 100) == SOCKET_ERROR)
//{
// printf("* error in listen!\n");
// exit(1);
//}
//printf("* started listening for connection requests...\n");
}
// load_accept_ex()
{
//DWORD dwBytes;
// black magic for me!!!
// You do not need to call in your code WSAIoctl. You can directly use AcceptEx and adds Mswsock.lib.
//WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &GuidAcceptEx, sizeof(GuidAcceptEx), &pfAcceptEx, sizeof(pfAcceptEx), &dwBytes, NULL, NULL);
}
// start_accepting()
{
//SOCKET acceptor = create_accepting_socket();
//DWORD expected = sizeof(struct sockaddr_in) + 16;
//printf("* started accepting connections...\n");
// uses listener's completion key and overlapped structure
//sock_state.socket = acceptor;
//memset(&sock_ovl, 0, sizeof(WSAOVERLAPPED));
// starts asynchronous accept
//if (!pfAcceptEx(sock, acceptor, sock_state.buf, 0 /* no recv */, expected, expected, NULL, &sock_ovl))
//{
// int err = WSAGetLastError();
// if (err != ERROR_IO_PENDING)
// {
// printf("* error %d in AcceptEx\n", err);
// exit(1);
// }
//}
}
}
// -----------------------------------------------------------------------------
static void recvBuffer(void)
{
char* buf = msgBuf;
int pendingLen = BUFLEN;
printf("* receiving reply\n");
while (pendingLen > 0)
{
int partialLen = recv(sock, buf, pendingLen, 0);
if (partialLen > 0)
{
pendingLen -= partialLen;
buf += partialLen;
continue;
}
// ------
if (partialLen == 0)
{
printf("* connection closed by the server\n");
}
else // partialLen < 0
{
int err = WSAGetLastError();
printf("* error %d in recv\n", err);
}
exit(1);
}
}
// -----------------------------------------------------------------------------
static void run(void)
{
DWORD length;
BOOL resultOk;
WSAOVERLAPPED* ovl_res;
SocketState* socketState;
for (;;)
{
sendBuffer();
resultOk = get_completion_status(&length, &socketState, &ovl_res);
recvBuffer();
}
}
// -----------------------------------------------------------------------------
static void sendBuffer(void)
{
char* buf = msgBuf;
int pendingLen = BUFLEN;
printf("* sending message\n");
sprintf(msgBuf, "%05 *****", msgNumber++);
while (pendingLen > 0)
{
int partialLen = send(sock, buf, pendingLen, 0);
if (partialLen > 0)
{
pendingLen -= partialLen;
buf += partialLen;
continue;
}
// -----------
if (partialLen == 0)
{
printf("* connection closed by the server\n");
}
else // partialLen < 0
{
int err = WSAGetLastError();
printf("* error %d in send\n", err);
}
exit(1);
}
}
// -----------------------------------------------------------------------------
static SOCKET create_accepting_socket(void)
{
SOCKET acceptor = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (acceptor == INVALID_SOCKET)
{
printf("* error creating accept socket!\n");
exit(1);
}
return acceptor;
}
// -----------------------------------------------------------------------------
static void create_io_completion_port(void)
{
cpl_port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
if (!cpl_port)
{
int err = WSAGetLastError();
printf("* error %d in line %d CreateIoCompletionPort\n", err, __LINE__);
exit(1);
}
}
// -----------------------------------------------------------------------------
static BOOL get_completion_status(DWORD* length, SocketState** socketState, WSAOVERLAPPED** ovl_res)
{
BOOL resultOk;
*ovl_res = NULL;
*socketState = NULL;
resultOk = GetQueuedCompletionStatus(cpl_port, length, (PULONG_PTR)socketState, ovl_res, INFINITE);
if (!resultOk)
{
DWORD err = GetLastError();
printf("* error %d getting completion port status!!!\n", err);
}
if (!*socketState || !*ovl_res)
{
printf("* don't know what to do, aborting!!!\n");
exit(1);
}
return resultOk;
}
// -----------------------------------------------------------------------------
// the end
When the server send a response by calling:
WSASend(socketState->socket, &wsabuf, 1, NULL, 0, ovl, NULL)
I’d expect it to be picked up by the client on this line:
resultOk = get_completion_status(&length, &socketState, &ovl_res);
But it doesn’t…
Would anybody be able to tell me what I’m doing wrong?
Edit:
I’ve taken the following points:
On the client side, you use WSAConnect() to create an outbound connection.
Call WSARecv() and WSASend() to start reading/writing operations when needed
you have to use WSASend/WSARecv if you want to use I/O completion ports.
and attempted to create a simple IOCP based client:
#include <iostream>
#include <winsock2.h>
#pragma comment(lib,"ws2_32.lib")
static DWORD WINAPI ClientWorkerThread(LPVOID lpParameter);
int main(void)
{
WSADATA WsaDat;
if (WSAStartup(MAKEWORD(2, 2), &WsaDat) != NO_ERROR)
return 0;
// Step 1 - Create an I/O completion port.
HANDLE hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
if (!hCompletionPort)
return 0;
// Step 2 - Find how many processors.
SYSTEM_INFO systemInfo;
GetSystemInfo(&systemInfo);
const int nNumberOfProcessors = systemInfo.dwNumberOfProcessors;
// Step 3 - Create worker threads.
for (int i = 0; i < nNumberOfProcessors; i++)
{
HANDLE hThread = CreateThread(NULL, 0, ClientWorkerThread, hCompletionPort, 0, NULL);
CloseHandle(hThread);
}
// Step 4 - Create a socket.
SOCKET Socket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
if (Socket == INVALID_SOCKET)
return 0;
struct hostent *host;
if ((host = gethostbyname("localhost")) == NULL)
return 0;
SOCKADDR_IN SockAddr;
SockAddr.sin_family = AF_INET;
SockAddr.sin_addr.s_addr = *((unsigned long*)host->h_addr);
SockAddr.sin_port = htons(8888);
// Step 5 - Associate the socket with the I/O completion port.
CreateIoCompletionPort((HANDLE)Socket, hCompletionPort, (ULONG_PTR)0, 0);
if (WSAConnect(Socket, (SOCKADDR*)(&SockAddr), sizeof(SockAddr), NULL, NULL, NULL, NULL) == SOCKET_ERROR)
return 0;
char buffer[1000];
memset(buffer, 0, 999);
WSABUF wsaBuf = {strlen(buffer), buffer};
DWORD dwSendBytes = 0;
DWORD dwReceivedBytes = 0;
DWORD dwFlags = 0;
WSAOVERLAPPED wsaOverlapped;
SecureZeroMemory((PVOID)&wsaOverlapped, sizeof(wsaOverlapped));
wsaOverlapped.hEvent = WSACreateEvent();
for(;;)
{
WSARecv(Socket, &wsaBuf, 1, &dwReceivedBytes, &dwFlags, &wsaOverlapped, NULL);
std::cout << wsaBuf.buf;
//WSASend(Socket, &wsaBuf, 1, &dwSendBytes, 0, &wsaOverlapped, NULL);
int nError = WSAGetLastError();
if(nError != WSAEWOULDBLOCK&&nError != 0)
{
std::cout << "Winsock error code: " << nError << "\r\n";
std::cout << "Server disconnected!\r\n";
shutdown(Socket, SD_SEND);
closesocket(Socket);
break;
}
Sleep(1000);
}
WSACleanup();
system("PAUSE");
return 0;
}
static DWORD WINAPI ClientWorkerThread(LPVOID lpParameter)
{
HANDLE hCompletionPort = (HANDLE)lpParameter;
DWORD dwBytesTransferred = 0;
while (TRUE)
{
BOOL bRet = GetQueuedCompletionStatus(hCompletionPort, &dwBytesTransferred, (LPDWORD)0, (LPOVERLAPPED*)0, INFINITE);
}
return 0;
}
I know there are several things I’m doing wrong but I don’t know what they are.
Could somebody take a look at my code and give me some hints please?
Many thanks
Edit 2:
Sorry this post is getting too long.
I've had another go trying to implement an IOCP based client after reading Remy's comments below but I'm still not sure if I'm on the right track.
I'd really appreciate it if somebody could take a look at my new code (compiles fine under VS2010 & error checking omitted) below and give me some feedback.
NonBlockingClient:
#include <iostream>
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
static DWORD WINAPI ClientWorkerThread(LPVOID lpParameter);
typedef struct _PER_HANDLE_DATA
{
SOCKET Socket;
} PER_HANDLE_DATA, * LPPER_HANDLE_DATA;
typedef struct
{
WSAOVERLAPPED wsaOverlapped;
WSABUF wsaBuf;
int OperationType;
} PER_IO_DATA, * LPPER_IO_DATA;
int main(void)
{
WSADATA WsaDat;
WSAStartup(MAKEWORD(2, 2), &WsaDat);
// Step 1 - Create an I/O completion port.
HANDLE hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
// Step 2 - Find how many processors.
SYSTEM_INFO systemInfo;
GetSystemInfo(&systemInfo);
// Step 3 - Create worker threads.
for (int i = 0; i < (int)systemInfo.dwNumberOfProcessors; i++)
{
HANDLE hThread = CreateThread(NULL, 0, ClientWorkerThread, hCompletionPort, 0, NULL);
CloseHandle(hThread);
}
// Step 4 - Create a socket.
SOCKET Socket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
PER_HANDLE_DATA *pPerHandleData = new PER_HANDLE_DATA;
pPerHandleData->Socket = Socket;
struct hostent *host;
host = gethostbyname("localhost");
SOCKADDR_IN SockAddr;
SockAddr.sin_family = AF_INET;
SockAddr.sin_addr.s_addr = *((unsigned long*)host->h_addr);
SockAddr.sin_port = htons(8888);
// Step 5 - Associate the socket with the I/O completion port.
CreateIoCompletionPort((HANDLE)Socket, hCompletionPort, (DWORD)pPerHandleData, 0);
WSAConnect(Socket, (SOCKADDR*)(&SockAddr), sizeof(SockAddr), NULL, NULL, NULL, NULL);
static char buffer[1000];
memset(buffer, 0, 999);
PER_IO_DATA *pPerIoData = new PER_IO_DATA;
pPerIoData->wsaBuf.buf = buffer;
pPerIoData->wsaBuf.len = sizeof(buffer);
DWORD dwSendBytes = 0;
DWORD dwReceivedBytes = 0;
DWORD dwFlags = 0;
SecureZeroMemory((PVOID)&pPerIoData->wsaOverlapped, sizeof(pPerIoData->wsaOverlapped));
pPerIoData->wsaOverlapped.hEvent = WSACreateEvent();
WSARecv(Socket, &pPerIoData->wsaBuf, 1, &dwReceivedBytes, &dwFlags, &pPerIoData->wsaOverlapped, NULL);
std::cout << pPerIoData->wsaBuf.buf;
for (;;)
{
int nError = WSAGetLastError();
if (nError != WSAEWOULDBLOCK&&nError != 0)
{
std::cout << "Winsock error code: " << nError << "\r\n";
std::cout << "Server disconnected!\r\n";
shutdown(Socket, SD_SEND);
closesocket(Socket);
break;
}
Sleep(1000);
}
delete pPerHandleData;
delete pPerIoData;
WSACleanup();
return 0;
}
static DWORD WINAPI ClientWorkerThread(LPVOID lpParameter)
{
HANDLE hCompletionPort = (HANDLE)lpParameter;
DWORD bytesCopied = 0;
OVERLAPPED *overlapped = 0;
LPPER_HANDLE_DATA PerHandleData;
LPPER_IO_DATA PerIoData;
DWORD SendBytes, RecvBytes;
DWORD Flags;
BOOL bRet;
while (TRUE)
{
bRet = GetQueuedCompletionStatus(hCompletionPort, &bytesCopied, (LPDWORD)&PerHandleData, (LPOVERLAPPED*)&PerIoData, INFINITE);
if (bytesCopied == 0)
{
break;
}
else
{
Flags = 0;
ZeroMemory(&(PerIoData->wsaOverlapped), sizeof(WSAOVERLAPPED));
PerIoData->wsaBuf.len = 1000;
WSARecv(PerHandleData->Socket, &(PerIoData->wsaBuf), 1, &RecvBytes, &Flags, &(PerIoData->wsaOverlapped), NULL);
}
}
return 0;
}
NonBlockingServer:
#include <iostream>
#include <winsock2.h>
#pragma comment(lib,"ws2_32.lib")
int main()
{
WSADATA WsaDat;
WSAStartup(MAKEWORD(2,2), &WsaDat);
SOCKET listenSocket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
SOCKADDR_IN server;
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(8888);
bind(listenSocket, (SOCKADDR*)(&server), sizeof(server));
listen(listenSocket, 1);
SOCKET acceptSocket = SOCKET_ERROR;
sockaddr_in saClient;
int nClientSize = sizeof(saClient);
while (acceptSocket == SOCKET_ERROR)
{
std::cout << "Waiting for incoming connections...\r\n";
acceptSocket = WSAAccept(listenSocket, (SOCKADDR*)&saClient, &nClientSize, NULL, NULL);
}
std::cout << "Client connected!\r\n\r\n";
char *szMessage = "Welcome to the server!\r\n";
WSAOVERLAPPED SendOverlapped;
DWORD SendBytes;
WSABUF DataBuf;
DataBuf.len = 1000;
DataBuf.buf = szMessage;
SecureZeroMemory((PVOID)&SendOverlapped, sizeof(WSAOVERLAPPED));
SendOverlapped.hEvent = WSACreateEvent();
for (;;)
{
WSASend(acceptSocket, &DataBuf, 1, &SendBytes, 0, &SendOverlapped, NULL);
int nError = WSAGetLastError();
if (nError != WSAEWOULDBLOCK && nError != 0)
{
std::cout << "Winsock error code: " << nError << "\r\n";
std::cout << "Client disconnected!\r\n";
shutdown(acceptSocket, SD_SEND);
closesocket(acceptSocket);
break;
}
Sleep(1000);
}
WSACleanup();
return 0;
}
Thanks again!
Try something like this:
Client:
#include <iostream>
#include <string>
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
typedef struct
{
WSAOVERLAPPED Overlapped;
SOCKET Socket;
WSABUF wsaBuf;
char Buffer[1024];
DWORD Flags;
} PER_IO_DATA, * LPPER_IO_DATA;
static DWORD WINAPI ClientWorkerThread(LPVOID lpParameter)
{
HANDLE hCompletionPort = (HANDLE)lpParameter;
DWORD NumBytesRecv = 0;
ULONG CompletionKey;
LPPER_IO_DATA PerIoData;
while (GetQueuedCompletionStatus(hCompletionPort, &NumBytesRecv, &CompletionKey, (LPOVERLAPPED*)&PerIoData, INFINITE))
{
if (!PerIoData)
continue;
if (NumBytesRecv == 0)
{
std::cout << "Server disconnected!\r\n\r\n";
}
else
{
// use PerIoData->Buffer as needed...
std::cout << std::string(PerIoData->Buffer, NumBytesRecv);
PerIoData->wsaBuf.len = sizeof(PerIoData->Buffer);
PerIoData->Flags = 0;
if (WSARecv(PerIoData->Socket, &(PerIoData->wsaBuf), 1, &NumBytesRecv, &(PerIoData->Flags), &(PerIoData->Overlapped), NULL) == 0)
continue;
if (WSAGetLastError() == WSA_IO_PENDING)
continue;
}
closesocket(PerIoData->Socket);
delete PerIoData;
}
return 0;
}
int main(void)
{
WSADATA WsaDat;
if (WSAStartup(MAKEWORD(2, 2), &WsaDat) != 0)
return 0;
HANDLE hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
if (!hCompletionPort)
return 0;
SYSTEM_INFO systemInfo;
GetSystemInfo(&systemInfo);
for (DWORD i = 0; i < systemInfo.dwNumberOfProcessors; ++i)
{
HANDLE hThread = CreateThread(NULL, 0, ClientWorkerThread, hCompletionPort, 0, NULL);
CloseHandle(hThread);
}
SOCKET Socket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
if (Socket == INVALID_SOCKET)
return 0;
SOCKADDR_IN SockAddr;
SockAddr.sin_family = AF_INET;
SockAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
SockAddr.sin_port = htons(8888);
CreateIoCompletionPort((HANDLE)Socket, hCompletionPort, 0, 0);
if (WSAConnect(Socket, (SOCKADDR*)(&SockAddr), sizeof(SockAddr), NULL, NULL, NULL, NULL) == SOCKET_ERROR)
return 0;
PER_IO_DATA *pPerIoData = new PER_IO_DATA;
ZeroMemory(pPerIoData, sizeof(PER_IO_DATA));
pPerIoData->Socket = Socket;
pPerIoData->Overlapped.hEvent = WSACreateEvent();
pPerIoData->wsaBuf.buf = pPerIoData->Buffer;
pPerIoData->wsaBuf.len = sizeof(pPerIoData->Buffer);
DWORD dwNumRecv;
if (WSARecv(Socket, &(pPerIoData->wsaBuf), 1, &dwNumRecv, &(pPerIoData->Flags), &(pPerIoData->Overlapped), NULL) == SOCKET_ERROR)
{
if (WSAGetLastError() != WSA_IO_PENDING)
{
delete pPerIoData;
return 0;
}
}
while (TRUE)
Sleep(1000);
shutdown(Socket, SD_BOTH);
closesocket(Socket);
WSACleanup();
return 0;
}
Server:
#include <iostream>
#include <winsock2.h>
#pragma comment(lib,"ws2_32.lib")
typedef struct
{
WSAOVERLAPPED Overlapped;
SOCKET Socket;
WSABUF wsaBuf;
char Buffer[1024];
DWORD BytesSent;
DWORD BytesToSend;
} PER_IO_DATA, * LPPER_IO_DATA;
static DWORD WINAPI ServerWorkerThread(LPVOID lpParameter)
{
HANDLE hCompletionPort = (HANDLE)lpParameter;
DWORD NumBytesSent = 0;
ULONG CompletionKey;
LPPER_IO_DATA PerIoData;
while (GetQueuedCompletionStatus(hCompletionPort, &NumBytesSent, &CompletionKey, (LPOVERLAPPED*)&PerIoData, INFINITE))
{
if (!PerIoData)
continue;
if (NumBytesSent == 0)
{
std::cout << "Client disconnected!\r\n\r\n";
}
else
{
PerIoData->BytesSent += NumBytesSent;
if (PerIoData->BytesSent < PerIoData->BytesToSend)
{
PerIoData->wsaBuf.buf = &(PerIoData->Buffer[PerIoData->BytesSent]);
PerIoData->wsaBuf.len = (PerIoData->BytesToSend - PerIoData->BytesSent);
}
else
{
PerIoData->wsaBuf.buf = PerIoData->Buffer;
PerIoData->wsaBuf.len = strlen(PerIoData->Buffer);
PerIoData->BytesSent = 0;
PerIoData->BytesToSend = PerIoData->wsaBuf.len;
}
if (WSASend(PerIoData->Socket, &(PerIoData->wsaBuf), 1, &NumBytesSent, 0, &(PerIoData->Overlapped), NULL) == 0)
continue;
if (WSAGetLastError() == WSA_IO_PENDING)
continue;
}
closesocket(PerIoData->Socket);
delete PerIoData;
}
return 0;
}
int main()
{
WSADATA WsaDat;
if (WSAStartup(MAKEWORD(2,2), &WsaDat) != 0)
return 0;
HANDLE hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
if (!hCompletionPort)
return 0;
SYSTEM_INFO systemInfo;
GetSystemInfo(&systemInfo);
for (DWORD i = 0; i < systemInfo.dwNumberOfProcessors; ++i)
{
HANDLE hThread = CreateThread(NULL, 0, ServerWorkerThread, hCompletionPort, 0, NULL);
CloseHandle(hThread);
}
SOCKET listenSocket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
if (listenSocket == INVALID_SOCKET)
return 0;
SOCKADDR_IN server;
ZeroMemory(&server, sizeof(server));
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(8888);
if (bind(listenSocket, (SOCKADDR*)(&server), sizeof(server)) != 0)
return 0;
if (listen(listenSocket, 1) != 0)
return 0;
std::cout << "Waiting for incoming connection...\r\n";
SOCKET acceptSocket;
do
{
sockaddr_in saClient;
int nClientSize = sizeof(saClient);
acceptSocket = WSAAccept(listenSocket, (SOCKADDR*)&saClient, &nClientSize, NULL, NULL);
}
while (acceptSocket == INVALID_SOCKET);
std::cout << "Client connected!\r\n\r\n";
CreateIoCompletionPort((HANDLE)acceptSocket, hCompletionPort, 0, 0);
LPPER_IO_DATA pPerIoData = new PER_IO_DATA;
ZeroMemory(pPerIoData, sizeof(PER_IO_DATA));
strcpy(pPerIoData->Buffer, "Welcome to the server!\r\n");
pPerIoData->Overlapped.hEvent = WSACreateEvent();
pPerIoData->Socket = acceptSocket;
pPerIoData->wsaBuf.buf = pPerIoData->Buffer;
pPerIoData->wsaBuf.len = strlen(pPerIoData->Buffer);
pPerIoData->BytesToSend = pPerIoData->wsaBuf.len;
DWORD dwNumSent;
if (WSASend(acceptSocket, &(pPerIoData->wsaBuf), 1, &dwNumSent, 0, &(pPerIoData->Overlapped), NULL) == SOCKET_ERROR)
{
if (WSAGetLastError() != WSA_IO_PENDING)
{
delete pPerIoData;
return 0;
}
}
while (TRUE)
Sleep(1000);
shutdown(acceptSocket, SD_BOTH);
closesocket(acceptSocket);
WSACleanup();
return 0;
}
Have you had a look at the example in the MSDN documentation for WSARecv?
Essentially, you have to start the asynchronous WSARecv operation first and then get notified via the completion port of its completion.
Or to put it another way: Windows I/O completion ports are using a proactor model (in contrast to the reactor model of Linux/FreeBSD/NetBSD).