I have a program that I want to automatically terminate on a WM_POWERBROADCAST message. For some reason, however, it is not terminating when I cause the computer to sleep. The program should have more than enough time to respond to this call, but I don't think the program is processing the message at all. I believe this mainly because the program doesn't terminate when the computer resumes, either, and the message should at least be in the window's queue.
What am I doing wrong that is causing my program not to be able to process this message?
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_POWERBROADCAST:
DestroyWindow(hWnd);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_CLOSE:
return 0;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
//Set title of program
//SetConsoleTitleA("Star");
//FreeConsole();
//Change the current directory of the program back to the appropriate folder
wchar_t* UserProf;
SHGetKnownFolderPath(FOLDERID_Profile, 0, NULL, &UserProf);
const wchar_t* EndProf = L"\\AppData\\UserUpdates";
wcsncat(UserProf, EndProf, 23);
wstring ws(UserProf);
string wstr(ws.begin(), ws.end());
//cout << wstr << endl;
SetCurrentDirectoryA(wstr.c_str());
WNDCLASSW WindowClass{CS_NOCLOSE, WindowProc, 0, 0, hInstance, NULL, LoadCursor(nullptr, IDC_ARROW), NULL, NULL, L"chakra"};
RegisterClass(&WindowClass);
HWND hWnd = CreateWindow(L"chakra", L"star", WS_POPUP, 0, 0, 10, 10, NULL, NULL, hInstance, NULL);
ShowWindow(hWnd, SW_HIDE);
string ipAddress = "10.0.0.201"; //IP address of my computer on local network
int port = 13777;
Hunter:
WSAData data;
WORD ver = MAKEWORD(2, 2);
int wsResult = WSAStartup(ver, &data);
if (wsResult != 0) {
//cerr << "Can't start Winsock, Err#" << wsResult << endl;
return 0;
}
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == INVALID_SOCKET) {
//cerr << "Can't create socket" << endl;
return 0;
}
sockaddr_in hint;
hint.sin_family = AF_INET;
hint.sin_port = htons(port);
inet_pton(AF_INET, ipAddress.c_str(), &hint.sin_addr);
int connCounter = 0;
//Constantly attempts to connect to server
do {
int connResult = connect(sock, (sockaddr*)&hint, sizeof(hint));
if (connResult == SOCKET_ERROR) {
connCounter = 0;
closesocket(sock);
WSACleanup();
goto Hunter;
}
else {
connCounter = 1;
}
} while (connCounter == 0);
char buf[1024]; //Where message from server will be stored
char* pbuf{ buf };
//Things to compare
const char* CreateAccount = "create"; //Server tells client to make IG account
const char* CheckStatus = "check"; //Client tells server if account is running or not
const char* Info = "info"; //Client sends Username and Password of account to server
const char* Run = "run"; //Tells client to start running the account
const char* Kill = "kill"; //Kills program on client for around a month
const char* Settings = "settings"; //Server sets settings for account to run on
string TryAgain = "#13 Not a valid input, either type [check] to check if the account is running, type [create] to create new account, or type [info] for account information\n";
string accInfoSuccess = "#777 You have successfully entered the information for the account (^.^)\n";
string settingsInfoSuccess = "#777 You have successfully set the settings for this account (^.^)\n";
string accInfoProblem = "#13 There was a problem in either saving the information to this account or the account creation program. Type [create] and try again (T.T)\n";
string settingsInfoProblem = "#13 There was a problem in saving the account settings. Type [settings] and try again (T.T)\n";
string accRun = "#777 The account is currently running! ~(^.^)~\n";
string accNoRun = "#13 Sorry the account isn't running, type [run] to run the account (O.o)\n";
string accNoInfo = "#13 There is no info for an account that can be run. Type [create] to make information (\".\")\n";
string accRunErr = "#13 There is a problem with opening the main bot program, try again. If this problem persists, there is an issue clientside V(T.T)V\n";
int loopCounter = 0;
//int on = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)TRUE, sizeof(TRUE));
MSG Msg = { 0 };
do {
ZeroMemory(buf, 1024);
u_long block = 0;
ioctlsocket(sock, FIONBIO, &block);
DWORD timeout = 500;
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));
//setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(timeout));
int bytesReceived = recv(sock, buf, 1024, 0);
if (bytesReceived > 0) {
//Check to see if it equals one of the strings above
if (strstr(pbuf, CreateAccount)) {
//Run program to create account
int accountSuccess;
accountSuccess = accountCreation(sock);
if (accountSuccess == 0) {
int sendResult = send(sock, accInfoSuccess.c_str(), accInfoSuccess.size() + 1, 0);
}
else {
int sendResult = send(sock, accInfoProblem.c_str(), accInfoProblem.size() + 1, 0);
}
}
else if (strstr(pbuf, Settings)) {
int settingsSuccess;
settingsSuccess = settingsCreation(sock);
if (settingsSuccess == 0) {
int sendResult = send(sock, settingsInfoSuccess.c_str(), settingsInfoSuccess.size() + 1, 0);
}
else {
int sendResult = send(sock, settingsInfoProblem.c_str(), settingsInfoProblem.size() + 1, 0);
}
}
else if (strstr(pbuf, CheckStatus)) {
//Check to see if program that runs account is running
int accountSuccess = isRunning();
if (accountSuccess == 0) {
int sendResult = send(sock, accRun.c_str(), accRun.size() + 1, 0);
}
else if (accountSuccess == 1){
int sendResult = send(sock, accNoRun.c_str(), accNoRun.size() + 1, 0);
}
else {
int sendResult = send(sock, accNoInfo.c_str(), accNoInfo.size() + 1, 0);
}
}
else if (strstr(pbuf, Info)) {
//Read text file containing account info and send to server
int infoChecker = checkInfo(sock);
if (infoChecker != 0) {
int sendResult = send(sock, accNoInfo.c_str(), accNoInfo.size() + 1, 0);
}
}
else if (strstr(pbuf, Run)) {
//Runs the account running program
int running = runProg();
if (running == 0) {
int sendResult = send(sock, accRun.c_str(), accRun.size() + 1, 0);
}
else {
int sendResult = send(sock, accRunErr.c_str(), accRunErr.size() + 1, 0);
}
}
else if (strstr(pbuf, Kill)) {
//Kills this program
WSACleanup();
return 0;
}
else {
//Send back to server that the wrong thing was inputted
int sendResult = send(sock, TryAgain.c_str(), TryAgain.size() + 1, 0);
ZeroMemory(buf, 1024);
loopCounter = 0;
}
}
else {
//Check to make sure bot is running
int vroom = isRunning();
if (vroom == 1) {
//runProg();
loopCounter = 0;
}
else {
loopCounter = 0;
}
}
if (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE) != 0) {
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
} while (loopCounter == 0);
WSACleanup();
return 0;
}
There are a TON of problems with the code shown, but lets start with the most important one that is affecting your message issue - all of that thread-blocking socket code DOES NOT belong inside your WinMain() at all, let alone inside your message loop itself. That is why your handling of window messages, like WM_POWERBROADCAST, is running so slow.
You need to restructure your code. Either:
move the socket code to a worker thread, and let it block that thread all it wants.
use asynchronous socket I/O (via WSAAsyncSelect(), etc) that you can process inside of your WindowProc. Or use overlapped I/O. Either way, no threading or blocking operations are needed.
Whatever you do, DO NOT block WinMain()'s message loop.
That being said, other problems with your code include:
SHGetKnownFolderPath(..., &UserProf); wcsncat(UserProf, EndProf, 23); is undefined behavior. SHGetKnownFolderPath() does not allocate enough memory for you to append anything onto. You need to allocate another buffer large enough to copy UserProf into and append EndProf into. Or, simply convert UserProf to a std::wstring first and then append EndProf onto the end of that (either way, don't forget to free UserProf when you are done with it - you are currently leaking it).
you should not be adding your custom UserUpdates subfolder directly to the user's AppData folder itself. Use FOLDERID_LocalAppData/Low, FOLDERID_RoamingAppData, or FOLDERID_ProgramData instead to get a more appropriate folder to add your things to.
string wstr(ws.begin(), ws.end()); is not the correct way to convert a std::wstring to a std::string. Use std::wstring_convert, WideCharToMultiByte(), or other similar conversion instead. Or, simply do not convert at all, use std::wcout and SetCurrentDirectoryW() instead.
you are misusing SO_REUSEADDR. It is useless after bind()/connect(), and you are not even enabling it properly anyway.
recv() does not return null-terminated data, like you are expecting. You are likely to experience undesired side effects, if not alright crashes, in your code that tries to match up received strings. TCP is stream oriented, not message oriented, like you think it is. There are numerous posts on StackOverflow that demonstrate the proper way to handle I/O over TCP.
With all of that said, try something more like this:
HWND hMyWnd = NULL;
HANDLE hThread = NULL;
bool stopThread = false;
int readString(SOCKET sock, char *buf, int buflen, string &str)
{
str.clear();
char ch, *pbuf = buf;
int len = 0, bytesReceived;
do {
if (stopThread)
return -2;
bytesReceived = recv(sock, &ch, 1, 0);
if (bytesReceived == -1) {
if ((WSAGetLastError() != WSAETIMEDOUT) || !str.empty()) {
//cerr << "Can't read from socket, Err#" << WSAGetLastError() << endl;
return -1;
}
return 1;
}
if (bytesReceived == 0) {
//cerr << "Socket disconnected by server" << endl;
return 0;
}
if (ch == '\0')
break;
*pbuf++ = ch;
len += bytesReceived;
if (len == buflen) {
str.append(buf, len);
pbuf = buf;
len = 0;
}
}
while (true);
if (len > 0)
str.append(buf, len);
return 1;
}
int sendString(SOCKET sock, const string &str)
{
const char *pbuf = str.c_str();
int len = str.length() + 1, bytesSent;
do {
if (stopThread)
return -2;
bytesSent = send(sock, pbuf, len, 0);
if (bytesSent == -1) {
//cerr << "Can't send to socket, Err#" << WSAGetLastError() << endl;
return -1;
}
pbuf += bytesSent;
len -= bytesSent;
}
while (len > 0);
return 1;
}
//Things to compare
const char* CreateAccount = "create"; //Server tells client to make IG account
const char* CheckStatus = "check"; //Client tells server if account is running or not
const char* Info = "info"; //Client sends Username and Password of account to server
const char* Run = "run"; //Tells client to start running the account
const char* Kill = "kill"; //Kills program on client for around a month
const char* Settings = "settings"; //Server sets settings for account to run on
//Things to send
const string TryAgain = "#13 Not a valid input, either type [check] to check if the account is running, type [create] to create new account, or type [info] for account information\n";
const string accInfoSuccess = "#777 You have successfully entered the information for the account (^.^)\n";
const string settingsInfoSuccess = "#777 You have successfully set the settings for this account (^.^)\n";
const string accInfoProblem = "#13 There was a problem in either saving the information to this account or the account creation program. Type [create] and try again (T.T)\n";
const string settingsInfoProblem = "#13 There was a problem in saving the account settings. Type [settings] and try again (T.T)\n";
const string accRun = "#777 The account is currently running! ~(^.^)~\n";
const string accNoRun = "#13 Sorry the account isn't running, type [run] to run the account (O.o)\n";
const string accNoInfo = "#13 There is no info for an account that can be run. Type [create] to make information (\".\")\n";
const string accRunErr = "#13 There is a problem with opening the main bot program, try again. If this problem persists, there is an issue clientside V(T.T)V\n";
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
string ipAddress = "10.0.0.201"; //IP address of my computer on local network
int port = 13777;
WSAData data;
WORD ver = MAKEWORD(2, 2);
int wsResult = WSAStartup(ver, &data);
if (wsResult != 0) {
//cerr << "Can't start Winsock, Err#" << wsResult << endl;
return 0;
}
sockaddr_in hint = {};
hint.sin_family = AF_INET;
hint.sin_port = htons(port);
inet_pton(AF_INET, ipAddress.c_str(), &hint.sin_addr);
SOCKET sock = INVALID_SOCKET;
char buf[1024]; //Where message from server will be stored
string str;
while (!stopThread) {
//attempt to connect to server
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == INVALID_SOCKET) {
//cerr << "Can't create socket, Err#" << WSAGetLastError() << endl;
break;
}
//BOOL on = TRUE;
//setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on));
//u_long block = 0;
//ioctlsocket(sock, FIONBIO, &block);
DWORD timeout = 500;
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));
//setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(timeout));
wsResult = connect(sock, (sockaddr*)&hint, sizeof(hint));
if (wsResult != SOCKET_ERROR) {
do {
wsResult = readString(sock, buf, sizeof(buf), str);
if (wsResult <= 0)
break;
if (!str.empty()) {
//Check to see if it equals one of the strings above
if (str == CreateAccount) {
//Run program to create account
if (accountCreation(sock) == 0) {
wsResult = sendString(sock, accInfoSuccess);
}
else {
wsResult = sendString(sock, accInfoProblem);
}
}
else if (str == Settings) {
if (settingsCreation(sock) == 0) {
wsResult = sendString(sock, settingsInfoSuccess);
}
else {
wsResult = sendString(sock, settingsInfoProblem);
}
}
else if (str == CheckStatus) {
//Check to see if program that runs account is running
int accountSuccess = isRunning();
if (accountSuccess == 0) {
wsResult = sendString(sock, accRun);
}
else if (accountSuccess == 1){
wsResult = sendString(sock, accNoRun);
}
else {
wsResult = sendString(sock, accNoInfo);
}
}
else if (str == Info) {
//Read text file containing account info and send to server
if (checkInfo(sock) != 0) {
wsResult = sendString(sock, accNoInfo);
}
}
else if (str == Run) {
//Runs the account running program
if (runProg() == 0) {
wsResult = sendString(sock, accRun);
}
else {
wsResult = sendString(sock, accRunErr);
}
}
else if (str == Kill) {
//Kills this program
PostMessage(hMyWnd, WM_CLOSE, 0, 0);
stopThread = true;
break;
}
else {
//Send back to server that the wrong thing was inputted
wsResult = sendString(sock, TryAgain);
}
}
else {
//Check to make sure bot is running
if (isRunning() == 1) {
//runProg();
}
}
}
while (!stopThread);
}
closesocket(sock);
sock = INVALID_SOCKET;
if (!stopThread)
Sleep(5000);
}
WSACleanup();
return 0;
}
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_CREATE: {
hThread = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
return 0;
case WM_POWERBROADCAST:
DestroyWindow(hWnd);
return 0;
case WM_DESTROY:
if (hThread) {
stopThread = true;
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
hThread = NULL;
}
PostQuitMessage(0);
return 0;
case WM_CLOSE:
DestroyWindow(hWnd);
return 0;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
//Change the current directory of the program back to the appropriate folder
wchar_t* UserProf;
if (SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, NULL, &UserProf) == S_OK) {
wstring wstr = wstring(UserProf) + L"\\UserUpdates";
//wcout << wstr << endl;
SetCurrentDirectoryW(wstr.c_str());
CoTaskMemFree(UserProf);
}
WNDCLASSW WindowClass{CS_NOCLOSE, WindowProc, 0, 0, hInstance, NULL, LoadCursor(nullptr, IDC_ARROW), NULL, NULL, L"chakra"};
if (!RegisterClassW(&WindowClass)) {
//cerr << "Can't register window class, Err#" << GetLastError() << endl;
return 0;
}
hMyWnd = CreateWindowW(L"chakra", L"star", WS_POPUP, 0, 0, 10, 10, NULL, NULL, hInstance, NULL);
if (!hMyWnd) {
//cerr << "Can't create window, Err#" << GetLastError() << endl;
return 0;
}
ShowWindow(hMyWnd, SW_HIDE);
MSG Msg;
while (GetMessage(&Msg, NULL, 0, 0)) {
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return 0;
}
Related
My program normally detects what Serial Ports are available from the OS at start up. It is a simple method of polling if a port can be accessed by name.
project defines for serial port
std::string COMPortNumber[MAXPORTS] {"\\\\.\\COM1", "\\\\.\\COM2", "\\\\.\\COM3", "\\\\.\\COM4", "\\\\.\\COM5",
"\\\\.\\COM6", "\\\\.\\COM7", "\\\\.\\COM8", "\\\\.\\COM9", "\\\\.\\COM10",
"\\\\.\\COM11", "\\\\.\\COM12", "\\\\.\\COM13", "\\\\.\\COM14", "\\\\.\\COM15",
"\\\\.\\COM16", "\\\\.\\COM17", "\\\\.\\COM18", "\\\\.\\COM19", "\\\\.\\COM20"};
std::string COMPortName[MAXPORTS] = {"com1", "com2", "com3", "com4", "com5", "com6", "com7", "com8", "com9", "com10",
"com11", "com12", "com13", "com14", "com15", "com16", "com17", "com18", "com19", "com20"};
polling function:
void updateSerialList(){
ComboBox_ResetContent(SerialPortDropDown); //clears all content from drop down box
//int iresult = ComboBox_AddString(SerialPortDropDown, "Update Port List\0");
for(int n=0; n<MAXPORTS; n++)
{
COMPortAvailable[n] = serial.getComPortList( COMPortNumber[n] );
if(COMPortAvailable[n] == true)
{
char* tempBuf = new char[COMPortName[n].length() + 1];
for(unsigned int t=0; t<COMPortName[n].length(); t++)
{
tempBuf[t] = COMPortName[n][t];
}
tempBuf[COMPortName[n].length()] = '\0';
int iResult = ComboBox_AddString(SerialPortDropDown, tempBuf);
{
if(iResult == CB_ERR){std::cout << "error adding string" << std::endl;}
else if(iResult == CB_ERRSPACE){std::cout << "error no room" << std::endl;}
}
delete[] tempBuf;
}
}
//place baud rates in select box
for(int n=NUMBERBAUDRATES-1; n>-1; n--)
{
char* tempBuf = new char[BaudRateName[n].length() + 1];
for(unsigned int t=0; t<BaudRateName[n].length(); t++)
{
tempBuf[t] = BaudRateName[n][t];
}
tempBuf[BaudRateName[n].length()] = '\0';
int iResult = ComboBox_AddString(BaudRateDropDown, tempBuf);
{
if(iResult == CB_ERR){std::cout << "error adding string" << std::endl;}
else if(iResult == CB_ERRSPACE){std::cout << "error no room" << std::endl;}
}
delete[] tempBuf;
}
This compiles a list in a dropdown box for the user to select. It uses a function in a class for a serial instance. This is the function call inside the class.
bool getComPortList(std::string portName)
{
bool test;
HANDLE testSerial;
testSerial = CreateFile( (portName.c_str()) , GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, NULL, NULL);
if(testSerial == INVALID_HANDLE_VALUE)
{
test = false;
}
else
{
test = true;
cout << "port number " << portName << " is available" << endl;
}
CloseHandle(testSerial);
return test;
}
This method has worked fine until I tried running the program on Windows 10. It previously was tested and used on Vista, Win7, Win 8.1 however even if the Windows10 device manager says comm ports are available on the system, my program cannot get a list of them.
What is different about Win10 serial port access?
your main logic error, that you assume - if CreateFile for some name return INVALID_HANDLE_VALUE - this mean that this name not exist. but this is of course false, because CreateFile can fail by different reasons. you need call GetLastError after fail. only if it return ERROR_FILE_NOT_FOUND the name really not exist (ERROR_PATH_NOT_FOUND can not be for "\\\\.\\COMX" because path here always exist and correct). for com devices very common error was STATUS_ACCESS_DENIED - because it have DO_EXCLUSIVE flag. with this flag only one file on device can be open at a time.
however for enumerate com devices - you need enumerate interfaces for GUID_DEVINTERFACE_COMPORT via CM_Get_Device_Interface_ListW
enumInterfaces(const_cast<PGUID>(&GUID_DEVINTERFACE_COMPORT));
static volatile UCHAR guz;
void enumInterfaces(PGUID InterfaceClassGuid)
{
CONFIGRET status;
ULONG len = 0, cb = 0, rcb;
PVOID stack = alloca(guz);
PWSTR buf = 0;
do
{
if (status = CM_Get_Device_Interface_List_SizeW(&len, InterfaceClassGuid, 0, CM_GET_DEVICE_INTERFACE_LIST_PRESENT))
{
break;
}
if (cb < (rcb = len * sizeof(WCHAR)))
{
len = (cb = RtlPointerToOffset(buf = (PWSTR)alloca(rcb - cb), stack)) / sizeof(WCHAR);
}
status = CM_Get_Device_Interface_ListW(InterfaceClassGuid, 0, buf, len, CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
if (status == CR_SUCCESS)
{
while (*buf)
{
DbgPrint("use this name in CreateFile = %S\n", buf);
PrintFriendlyNameByInterface(buf);
buf += 1 + wcslen(buf);
}
}
} while (status == CR_BUFFER_SMALL);
}
CONFIGRET PrintFriendlyNameByInterface(PCWSTR pszDeviceInterface)
{
ULONG cb = 0, rcb = 64;
PVOID stack = alloca(guz);
DEVPROPTYPE PropertyType;
CONFIGRET status;
union {
PVOID pv;
PWSTR DeviceID;
PBYTE pb;
};
do
{
if (cb < rcb)
{
rcb = cb = RtlPointerToOffset(pv = alloca(rcb - cb), stack);
}
status = CM_Get_Device_Interface_PropertyW(pszDeviceInterface, &DEVPKEY_Device_InstanceId, &PropertyType, pb, &rcb, 0);
if (status == CR_SUCCESS)
{
if (PropertyType == DEVPROP_TYPE_STRING)
{
DbgPrint("DeviceID = %S\n", DeviceID);
status = PrintFriendlyNameByDeviceID(DeviceID);
}
else
{
status = CR_WRONG_TYPE;
}
break;
}
} while (status == CR_BUFFER_SMALL);
return status;
}
CONFIGRET PrintFriendlyNameByDeviceID(PWSTR DeviceID)
{
DEVINST dnDevInst;
CONFIGRET status = CM_Locate_DevNodeW(&dnDevInst, DeviceID, CM_LOCATE_DEVNODE_NORMAL);
if (status == CR_SUCCESS)
{
ULONG cb = 0, rcb = 256;
PVOID stack = alloca(guz);
DEVPROPTYPE PropertyType;
union {
PVOID pv;
PWSTR sz;
PBYTE pb;
};
do
{
if (cb < rcb)
{
rcb = cb = RtlPointerToOffset(pv = alloca(rcb - cb), stack);
}
status = CM_Get_DevNode_PropertyW(dnDevInst, &DEVPKEY_NAME, &PropertyType, pb, &rcb, 0);
if (status == CR_SUCCESS)
{
if (PropertyType == DEVPROP_TYPE_STRING)
{
DbgPrint("show this name for user = %S\n", sz);
}
else
{
status = CR_WRONG_TYPE;
}
}
} while (status == CR_BUFFER_SMALL);
}
return status;
}
and demo output:
use this name in CreateFile = \\?\ACPI#PNP0501#0#{86e0d1e0-8089-11d0-9ce4-08003e301f73}
DeviceID = ACPI\PNP0501\0
show this name for user = Communications Port (COM1)
in my system \\?\ACPI#PNP0501#0#{86e0d1e0-8089-11d0-9ce4-08003e301f73} is symbolic link to PDO device \Device\00000034 (created by aspi.sys) and it have not DO_EXCLUSIVE flag. despite this on second call of CreateFile i got access denied error. to this device FDO - \Device\Serial0 (\\?\COM1 symbolic link to it) attached. it already have DO_EXCLUSIVE flag. anyway SerialCreateOpen (IRP_MJ_CREATE procedure serial.sys) denied access create more than one file - at very begin in increment some counter in device extension, and if it != 1 - return STATUS_ACCESS_DENIED
so even if we try open PDO (\\?\ACPI#PNP0501#0#{86e0d1e0-8089-11d0-9ce4-08003e301f73}) which not exclusive device (setting the exclusive flag for the FDO has no effect here) - the create request begin execute on stack top from \Device\Serial0 and serial.sys enforce exclusivity themselves within their SerialCreateOpen routine.
I am working on a application which works by received commands (API calls) over a socket connection. The application has two socket server with two different port numbers. As said the application is acting on the received commands (string). What is the best way to setup the socket server(s)? The socket server object is been build in a class file and contains two threads (listen for connections, handling the new connected client). The question is, what will be the best way to use the received commands/data so that the application will respond to it. Should I buildin a buffer (vector of string) to store the received data, read this buffer in the main loop of the application and after the reading clear the vector? Or is there a better/ other way?
The code of the socket server class is:
void SocketServer::Startup()
{
WSADATA wsa;
WSAStartup(0x0202, &wsa);
this->WorkerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
this->EndPnt.sin_addr.s_addr = INADDR_ANY;
this->EndPnt.sin_family = AF_INET;
this->EndPnt.sin_port = htons(this->m_port);
this->Enabled = true;
this->ID = 0;
bind(this->WorkerSocket, (SOCKADDR*)&this->EndPnt, sizeof(this->EndPnt));
printf("[SocketServer]Bound on %d..\n", this->m_port);
listen(this->WorkerSocket, this->Backlog);
CreateThread(0, 0, (LPTHREAD_START_ROUTINE)ServerMainThread, this, NULL, NULL);
}
void SocketServer::WaitForConnections(SocketServer * Ser)
{
while(Ser->Enabled)
{
SOCKET accptsock = accept(Ser->WorkerSocket, NULL, NULL);
printf("[SocketServer]Client connected.\n");
CreateThread(0, 0, (LPTHREAD_START_ROUTINE)ServerDataThread, (LPVOID)accptsock, NULL, NULL);
}
}
void SocketServer::WaitForData(LPVOID lpParam)
{
SOCKET sock = (SOCKET)lpParam;
char *message = "ECHO Daemon v1.0 \r\n";
send(sock, message, strlen(message), 0);
int res;
char buffer[255];
while(true)
{
res = recv(sock, buffer, sizeof(buffer), 0);
if( res == SOCKET_ERROR)
{
int error_code = WSAGetLastError();
if(error_code == WSAECONNRESET)
{
//Somebody disconnected , get his details and print
cout << "[SocketServer]Client disconnected" << endl;
//Close the socket and mark as 0 in list for reuse
closesocket(sock);
ExitThread(0);
}
else
{
closesocket(sock);
ExitThread(0);
}
}
if(res == 0)
{
closesocket(sock);
ExitThread(0);
}
else
{
cout << buffer << endl;
// insert data to vector here
send(sock , buffer , sizeof(buffer) , 0 );
}
}
}
DWORD WINAPI SocketServer::ServerMainThread(LPVOID lParam)
{
((SocketServer*)lParam)->WaitForConnections((SocketServer*)lParam);
return 0;
}
DWORD WINAPI SocketServer::ServerDataThread(LPVOID lParam)
{
((SocketServer*)lParam)->WaitForData((LPVOID)lParam);
return 0;
}
The vector it needs to push the data to is called "Buffer", "Buffer.push_back(buffer)";
I have some problem.
I write next code.
z=recv(conn,buff,512,0);//"Hi VahagnAAAAAAA" - but encrypted for example "zЖWЙЇ%ЂАЊ"S]яАAЧ0АбЯ.Щk5S¤Oц", length 32
BYTE messageLen = (BYTE)strlen(buff);// messageLen = 32
BYTE encryptedMessage[32];
memcpy(encryptedMessage, buff, messageLen);//!!!!!!!!!!!
DWORD encryptedMessageLen = messageLen;
CryptDecrypt(hSessionKeyRSA_2,NULL,TRUE,0,encryptedMessage, &encryptedMessageLen);
cout<<encryptedMessage<<endl;
I recv to buffer char array 32 length.
Where I copy encrypted text
"zЖWЙЇ%ЂАЊ"S]яАAЧ0АбЯ.Щk5S¤Oц"
to byte array, on the encryptedMessage have next value
"zЖWЙЇ%ЂАЊ"S]яАAЧ0АбЯ.Щk5S¤OцMMMMMMMMMMMMMMMMMMM"
where I decrypted I don't get start text, I get
"Ik VqagnеAAcS]‰МММММММММММ ММММММММ"
How I can fix it? please help me.
UPDATE
Client main()
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
const char* servername="127.0.0.1";
Sleep(2000);
setlocale(LC_ALL, "Russian");
WSADATA wsaData;
struct hostent *hp;
unsigned int addr;
struct sockaddr_in server;
int wsaret=WSAStartup(0x101,&wsaData);
if(wsaret)
return 0;
SOCKET conn;
conn=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(conn==INVALID_SOCKET)
return 0;
if(inet_addr(servername)==INADDR_NONE)
{
hp=gethostbyname(servername);
}
else
{
addr=inet_addr(servername);
hp=gethostbyaddr((char*)&addr,sizeof(addr),AF_INET);
}
if(hp==NULL)
{
closesocket(conn);
return 0;
}
server.sin_addr.s_addr=*((unsigned long*)hp->h_addr);
server.sin_family=AF_INET;
server.sin_port=htons(20248);
if(connect(conn,(struct sockaddr*)&server,sizeof(server)))
{
closesocket(conn);
return 0;
}
std::cout<<"Connected to server";
char buff[512];
memset(buff,'\0',512);
int z;
z=recv(conn,(char*)exportRSAKey,140,0);//Import RSA key
z=recv(conn,(char*)exportAESKey,140,0);//Import AES key
z=recv(conn,buff,512,0);//Get encryption text
importKey();//import key to client
BYTE messageLen = (BYTE)strlen(buff);
BYTE encryptedMessage[33];
memcpy(encryptedMessage, buff, messageLen);
DWORD encryptedMessageLen = messageLen;
CryptDecrypt(hSessionKeyRSA_2,NULL,FALSE,0,encryptedMessage, &encryptedMessageLen);
cout<<encryptedMessage<<endl;
// buff[z]=0;
}
Import key to client
if (CryptAcquireContext(&hCryptProv_RSA_2, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, 0))
{
printf("A cryptographic provider has been acquired.\r\n");
}
else
{
DWORD d = GetLastError();
return -1;
}
int iii = CryptImportKey(hCryptProv_RSA_2,(BYTE *)&exportAESKey,140,NULL,NULL,&hSessionKeyRSA_2);
if(CryptSetKeyParam(hSessionKeyRSA_2, KP_IV, exportRSAKey, 0))
{
cout<<"ok";
}
Server main()
std::cout<<"Client connected... "<<pParam<<std::endl;
char buff[512];
CString cmd;
CString params;
int n;
int x;
BOOL auth=false;
SOCKET client=(SOCKET)pParam;
strcpy(buff,"#Server Ready.\r\n");
char keybuff[1024];
createRSAPublicKey();//create enc_dec key
//keybuff = exportRSAKey;
//memset(rec,'\0',512);
const char *p = reinterpret_cast<const char*>(exportRSAKey);
send(client,p,140,0);//send RSA
const char *pp = reinterpret_cast<const char*>(exportAESKey);
send(client,pp,140,0);//Send AES
const char *ppp = reinterpret_cast<const char*>(encryptedMessage);
send(client,ppp,512,0);//Send encrypt text
createRSAPublicKey()
BOOL createRSAPublicKey()
{
if (CryptAcquireContext(&hCryptProv_AES, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, 0))
{
printf("A cryptographic provider has been acquired.\r\n");
}
else
{
DWORD d = GetLastError();
return -1;
}
HCRYPTKEY hSessionKey_AES;
if (!CryptGenKey(hCryptProv_AES, CALG_AES_256, CRYPT_EXPORTABLE, &hSessionKey_AES))
{
DWORD d = GetLastError();
return -1;
}
// Create RSA key to encrypt AES one
HCRYPTKEY hSessionKey;
if (!CryptGenKey(hCryptProv_AES, AT_KEYEXCHANGE, 1024 << 16, &hSessionKey))
{
DWORD d = GetLastError();
return -1;
}
// Export key
DWORD keylen;
BOOL ok = CryptExportKey(hSessionKey_AES, hSessionKey, SIMPLEBLOB, 0, exportRSAKey, &keylen);
if (ok == FALSE)
{
DWORD d = GetLastError();
return -1;
}
BYTE *encKey = (BYTE *)malloc(keylen);
ok = CryptExportKey(hSessionKey_AES, hSessionKey, SIMPLEBLOB, 0, exportAESKey, &keylen);
if (ok == FALSE)
{
DWORD d = GetLastError();
return -1;
}
else
printf("A cryptographic key export succeeded.\r\n");
BYTE messageLen = (BYTE)strlen(mess);
memcpy(encryptedMessage, mess, messageLen);
DWORD encryptedMessageLen = messageLen;
CryptEncrypt(hSessionKey_AES, NULL, TRUE, 0, encryptedMessage, &encryptedMessageLen, sizeof(encryptedMessage));
}
You are using strlen() to get the length of buff, but recv() does not null-terminate the buffer unless a null terminator was actually transmitted and read. You should instead be using the return value of recv(), which is the number of bytes actually read:
z=recv(conn,buff,512,0);
messageLen = z;//(BYTE)strlen(buff);
That being said, TCP is a byte stream, it has no concept of message boundaries. There is no 1-to-1 relationship between send() and recv() in TCP, like there is in UDP, so recv() above could read as little as 1 byte or as many as 512 bytes, and buff could contain a full message, a partial message, pieces of multiple messages, etc. You can't just blindly read and expect to receive everything in one go. You need to take all of that into account.
Design your TCP protocol to delimit messages, either with a preceding header that specifies the message length, or a trailing delimiter that never appears in the message body. Call recv() as many times as it takes, buffering any received data, and only process/decrypt complete messages that are in your buffer, leaving partial message data in the buffer to be completed by later reads.
Try something more like this:
Client main()
int readBuffer(SOCKET s, void *buffer, int buflen)
{
unsigned char *pbuf = (unsigned char*) buffer;
int total = 0;
while (total < buflen)
{
int num = recv(s, pbuf+total, buflen-total, 0);
if (num < 0)
return SOCKET_ERROR;
if (num == 0)
return 0;
total += num;
}
return total;
}
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
const char* servername="127.0.0.1";
setlocale(LC_ALL, "Russian");
WSADATA wsaData;
memset(&wsaData, 0, sizeof(wsaData));
int wsaret = WSAStartup(0x101, &wsaData);
if (wsaret != 0)
return 0;
struct sockaddr_in server;
memset(&server, 0, sizeof(server));
server.sin_addr.s_addr = inet_addr(servername);
if (server.sin_addr.s_addr == INADDR_NONE)
{
struct hostent *hp = gethostbyname(servername);
if (hp == NULL)
return 0;
server.sin_addr = *((in_addr*)hp->h_addr);
}
server.sin_family = AF_INET;
server.sin_port = htons(20248);
SOCKET conn = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (conn == INVALID_SOCKET)
return 0;
if (connect(conn, (struct sockaddr*)&server, sizeof(server)) != 0)
{
closesocket(conn);
return 0;
}
std::cout << "Connected to server";
if (readBuffer(conn, exportRSAKey, 140) <= 0) //Import RSA key
{
closesocket(conn);
return 0;
}
if (readBuffer(conn, exportAESKey, 140) <= 0) //Import AES key
{
closesocket(conn);
return 0;
}
importKey();//import key to client
DWORD messageLen;
if (readBuffer(conn, &messageLen, sizeof(messageLen)) <= 0) //Get encryption text length
{
closesocket(conn);
return 0;
}
messageLen = ntohl(messageLen);
std::vector<BYTE> buff(messageLen);
if (messageLen > 0)
{
if (readBuffer(conn, &buff[0], messageLen) <= 0) //Get encryption text
{
closesocket(conn);
return 0;
}
if (!CryptDecrypt(hSessionKeyRSA_2, NULL, FALSE, 0, &buff[0], &messageLen))
{
closesocket(conn);
return 0;
}
}
std::cout << std::string((char*)buff.data(), messageLen) << std::endl;
}
Server main()
int sendBuffer(SOCKET s, void *buffer, int buflen)
{
unsigned char *pbuf = (unsigned char*) buffer;
int total = 0;
while (total < buflen)
{
int num = send(s, pbuf+total, buflen-total, 0);
if (num < 0)
return SOCKET_ERROR;
if (num == 0)
return 0;
total += num;
}
return total;
}
...
SOCKET client = (SOCKET)pParam;
std::cout << "Client connected... " << pParam << std::endl;
...
createRSAPublicKey();//create enc_dec key
...
if (sendBuffer(client, exportRSAKey, 140) <= 0) //send RSA
{
closesocket(client);
return;
}
if (sendBuffer(client, exportAESKey, 140) <= 0) //Send AES
{
closesocket(client);
return;
}
...
DWORD tmpMessageLen = htonl(messageLen);
if (sendBuffer(client, &tmpMessageLen, sizeof(tmpMessageLen)); //Send encrypt text length
{
closesocket(client);
return;
}
if (sendBuffer(client, encryptedMessage, messageLen) <= 0) //Send encrypt text
{
closesocket(client);
return;
}
...
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).
I am having a lot of trouble tracking down where I am getting a BufferOverrun in the code below when I send it socket data from my flash/as3 telnet client. I am mostly looking to find more memory leaks and such, so in addition to this problem, is there an easier way to check for buffer overflow/overrun than manual debugging?
/*
MTSocketServer
--------------
The purpose of this application is to have a client application that can connect to this server.
This server should be able to parse messages from connected users and to send them to other users.
*/
// Headers and Namespace
#include "stdafx.h"
#include "MTSocketServer.h"
using namespace std;
// Constants
#define BUFFERSIZE 1000
#define PORT 20248
// Global Variables
SOCKET server;
struct Client_Data
{
SOCKET client_socket;
string user_id;
};
list<Client_Data> clients;
// Method Prototypes
UINT Server_Thread(LPVOID pParams);
UINT Client_Thread(LPVOID pParams);
string Recv_String(SOCKET listener);
void Send_String(const char* message, SOCKET sender);
bool Identity_Exists(string id);
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
AfxBeginThread(Server_Thread, NULL);
while(_getch() != 27);
closesocket(server);
WSACleanup();
return 0;
}
UINT Server_Thread(LPVOID pParams)
{
// Spawn Server Thread
cout << "Main thread spawned, press ESC at any time to exit.\r\n";
WSADATA wsa_data;
SOCKADDR_IN server_location;
if (int wsa_return_val = WSAStartup(0x101, &wsa_data) != 0)
{
cout << "Incorrect version of 'Ws2_32.dll'.\r\n";
cout << "Client thread de-initialized.\r\n";
AfxEndThread(0, true);
return 1;
}
server_location.sin_family = AF_INET;
server_location.sin_addr.s_addr = INADDR_ANY;
server_location.sin_port = htons(20248);
server = socket(AF_INET, SOCK_STREAM, NULL);
if (server == INVALID_SOCKET)
{
cout << "Socket creation error.\r\n";
cout << "Client thread de-initialized.\r\n";
AfxEndThread(0, true);
return 1;
}
if (bind(server, (SOCKADDR*)&server_location, sizeof(server_location)) != 0)
{
cout << "Socket binding error.\r\n";
cout << "Client thread de-initialized.\r\n";
AfxEndThread(0, true);
return 1;
}
if (listen(server, 10) != 0)
{
cout << "Socket listening error.\r\n";
cout << "Client thread de-initialized.\r\n";
AfxEndThread(0, true);
return 1;
}
SOCKET client;
SOCKADDR_IN client_location;
int len_of_client = sizeof(client_location);
bool abort = false;
while(abort == false)
{
client = accept(server, (SOCKADDR*)&client_location, &len_of_client);
// Spawn Client Thread
cout << "Connection from " << inet_ntoa(client_location.sin_addr) << ". Client thread spawned.\r\n";
AfxBeginThread(Client_Thread, (LPVOID)client);
}
// De-Initialize Server Thread
cout << "Server thread de-initialized.\r\n";
AfxEndThread(0, true);
return 0;
}
UINT Client_Thread(LPVOID pParams)
{
SOCKET client = (SOCKET)pParams;
string client_id = "";
// Check if a client with the same ID is on already
send(client, "ID_ME", 6, 0);
client_id = Recv_String(client);
if (Identity_Exists(client_id))
{
Send_String("That ID is already taken. Please try another.", client);
} else
{
cout << "Created ID " << client_id << " successfully\r\n";
Send_String("Type 'send' to send a message, 'quit' to exit.", client);
Client_Data this_client;
this_client.user_id = client_id;
this_client.client_socket = client;
clients.push_back(this_client);
bool is_con = true;
while(is_con)
{
string response = Recv_String(client);
if (response == "send")
{
Send_String("What username to send to?", client);
string to_id = Recv_String(client);
if (Identity_Exists(to_id))
{
Send_String("Type your message below: ", client);
string temp = Recv_String(client) + "\n\0";
const char* message = temp.c_str();
for (list<Client_Data>::iterator iterator = clients.begin(), end = clients.end(); iterator != end; ++iterator)
{
if (to_id == iterator->user_id)
{
SOCKET temp = iterator->client_socket;
cout << temp;
Send_String(message, temp);
}
}
} else
{
Send_String("Invalid username specified", client);
}
}
else if (response == "quit")
{
Send_String("Quit Command Issued", client);
break;
}
else
{
Send_String("Invalid Command Issued", client);
}
}
}
// De-Initialize Client Thread
closesocket(client);
cout << "Client thread de-initialized.\r\n";
AfxEndThread(0, true);
return 0;
}
// Function to parse full string values since they are often recieved one at a time
string Recv_String(SOCKET listener)
{
char buffer[BUFFERSIZE];
string temp;
int numofbytes = 0;
while (true)
{
int num = recv(listener, buffer, BUFFERSIZE, 0);
numofbytes++;
buffer[num] = '\0';
if (buffer[num-1] == '\n')
break;
if(numofbytes >= BUFFERSIZE-1)
break;
temp += buffer;
strcpy(buffer, "");
}
return temp;
}
void Send_String(const char* message, SOCKET sender)
{
char buffer[BUFFERSIZE];
strcpy(buffer, message);
size_t size = strlen(message);
int sendtest = send(sender, buffer, size, 0);
strcpy(buffer, "");
}
bool Identity_Exists(string id)
{
for (list<Client_Data>::iterator iterator = clients.begin(), end = clients.end(); iterator != end; ++iterator)
{
if (id == iterator->user_id)
{
return true;
}
}
return false;
}
buffer[num] = '\0'; writes one beyond the buffer if num happens to be equal to BUFFERSIZE. It writes one below the buffer if num happens to be -1.
if (buffer[num-1] == '\n') though not a buffer overrun, is also wrong. The \n can be anywhere (from 0 to n-1) in the buffer.