I encountered some problems with MHD_suspend_connection() and MHD_resume_connection() in libmicrohttpd while using the external event loop. Afterwards I have wrote a small example (without error handling) below. My question is: What am I doing wrong? Or is it a bug in the library? It should work as far as I understand the manual. Using external select with suspend/resume is allowed explicitly.
The problem is that connections are not resumed correctly. Processing the connection does not continue right after calling MHD_resume_connection(). In some versions of my program, it did continue after another request was incomming. In other versions later requests was not handled at all (access_handler() was never called). In some of this versions I got a response for the first request while stopping libmicrohttpd. When I enable MHD_USE_SELECT_INTERNALLY and remove my external loop (let it sleep), everything works.
I tested it on Debian (libmicrohttpd 0.9.37) and Arch (libmicrohttpd 0.9.50). The problem exists on both systems but maybe the behavior was a little bit different.
#include <algorithm>
#include <csignal>
#include <cstring>
#include <iostream>
#include <vector>
#include <sys/select.h>
#include <microhttpd.h>
using std::cerr;
using std::cout;
using std::endl;
static volatile bool run_loop = true;
static MHD_Daemon *ctx = nullptr;
static MHD_Response *response = nullptr;
static std::vector<MHD_Connection*> susspended;
void sighandler(int)
{
run_loop = false;
}
int handle_access(void *cls, struct MHD_Connection *connection,
const char *url, const char *method, const char *version,
const char *upload_data, size_t *upload_data_size,
void **con_cls)
{
static int second_call_marker;
static int third_call_marker;
if (*con_cls == nullptr) {
cout << "New connection" << endl;
*con_cls = &second_call_marker;
return MHD_YES;
} else if (*con_cls == &second_call_marker) {
cout << "Suspending connection" << endl;
MHD_suspend_connection(connection);
susspended.push_back(connection);
*con_cls = &third_call_marker;
return MHD_YES;
} else {
cout << "Send response" << endl;
return MHD_queue_response(connection, 200, response);
}
}
void myapp()
{
std::signal(SIGINT, &sighandler);
std::signal(SIGINT, &sighandler);
ctx = MHD_start_daemon(MHD_USE_DUAL_STACK //| MHD_USE_EPOLL
| MHD_USE_SUSPEND_RESUME | MHD_USE_DEBUG,
8080, nullptr, nullptr,
&handle_access, nullptr,
MHD_OPTION_END);
response = MHD_create_response_from_buffer(4, const_cast<char*>("TEST"),
MHD_RESPMEM_PERSISTENT);
while (run_loop) {
int max;
fd_set rs, ws, es;
struct timeval tv;
struct timeval *tvp;
max = 0;
FD_ZERO(&rs);
FD_ZERO(&ws);
FD_ZERO(&es);
cout << "Wait for IO activity" << endl;
MHD_UNSIGNED_LONG_LONG mhd_timeout;
MHD_get_fdset(ctx, &rs, &ws, &es, &max);
if (MHD_get_timeout(ctx, &mhd_timeout) == MHD_YES) {
//tv.tv_sec = std::min(mhd_timeout / 1000, 1ull);
tv.tv_sec = mhd_timeout / 1000;
tv.tv_usec = (mhd_timeout % 1000) * 1000;
tvp = &tv;
} else {
//tv.tv_sec = 2;
//tv.tv_usec = 0;
//tvp = &tv;
tvp = nullptr;
}
if (select(max + 1, &rs, &ws, &es, tvp) < 0 && errno != EINTR)
throw "select() failed";
cout << "Handle IO activity" << endl;
if (MHD_run_from_select(ctx, &rs, &ws, &es) != MHD_YES)
throw "MHD_run_from_select() failed";
for (MHD_Connection *connection : susspended) {
cout << "Resume connection" << endl;
MHD_resume_connection(connection);
}
susspended.clear();
}
cout << "Stop server" << endl;
MHD_stop_daemon(ctx);
}
int main(int argc, char *argv[])
{
try {
myapp();
} catch (const char *str) {
cerr << "Error: " << str << endl;
cerr << "Errno: " << errno << " (" << strerror(errno) << ")" << endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
I've compiled and run your sample on Windows and am seeing the same behavior w/ 0.9.51.
It's not a bug in microhttpd. The problem is that you are resuming a connection before queuing a response on it. The only code you have that creates a response relies on more activity on the connection so it's a catch-22.
The point of MHD_suspend_connection/MHD_resume_connection is to not block new connections while long-running work is going on. Thus typically after suspending the connection you need to kick off that work on another thread to continue while the listening socket is maintained. When that thread has queued the response it can resume the connection and the event loop will know it is ready to send back to the client.
I'm not sure of your other design requirements but you may not need to be implementing external select. That is to say, suspend/resume does not require it (I've used suspend/resume just fine with MHD_USE_SELECT_INTERNALLY, e.g.).
I dont know if it's mentioned. But you have a multi-threading bug, and perhaps, "intent bug". As the lib, may or may not use threads, depending on other factors. You can see if you are using threads, by printing the thread id, from the functions. But, your answerToConnection function, sets your vector (without mutex protection), and then you are immediately looking at it, and retrying potentially from another thread. this goes against the intent/purpose of suspend/retry, since suspend is really for something taking "a long time". The gotcha, is that you dont own the calling code, so, you dont know when it's totally done. however, you can age your retry, with a timeval, so, you dont retry too soon. at least a value of tv_usec +1. you need to note, that you are using the vector from two or more threads, without mutex protection.
Related
At this question, I asked how to unblock a grpc::CompletionQueue::Next() that is waiting on a grpc::Channel::NotifyOnStateChange(..., gpr_inf_future(GPR_CLOCK_MONOTONIC), ...).
That question, specifically, is still unanswered, but I am trying a workaround, where the CompletionQueue is instead waiting on a grpc::Channel::NotifyOnStateChange() with a non-infinite deadline:
// main.cpp
#include <chrono>
#include <iostream>
#include <memory>
#include <thread>
#include <grpcpp/grpcpp.h>
#include <unistd.h>
using namespace std;
using namespace grpc;
void threadFunc(shared_ptr<Channel> ch, CompletionQueue* cq) {
void* tag = NULL;
bool ok = false;
int i = 1;
grpc_connectivity_state state = ch->GetState(false);
std::chrono::time_point<std::chrono::system_clock> now =
std::chrono::system_clock::now();
std::chrono::time_point<std::chrono::system_clock> deadline =
now + std::chrono::seconds(2);
cout << "state " << i++ << " = " << (int)state << endl;
ch->NotifyOnStateChange(state,
//gpr_inf_future(GPR_CLOCK_MONOTONIC),
deadline,
cq,
(void*)1);
while (cq->Next(&tag, &ok)) {
state = ch->GetState(false);
cout << "state " << i++ << " = " << (int)state << endl;
now = std::chrono::system_clock::now();
deadline = now + std::chrono::seconds(2);
ch->NotifyOnStateChange(state,
//gpr_inf_future(GPR_CLOCK_MONOTONIC),
deadline,
cq,
(void*)1);
}
cout << "thread end" << endl;
}
int main(int argc, char* argv[]) {
ChannelArguments channel_args;
CompletionQueue cq;
channel_args.SetInt(GRPC_ARG_HTTP2_MAX_PINGS_WITHOUT_DATA, 0);
channel_args.SetInt(GRPC_ARG_MIN_RECONNECT_BACKOFF_MS, 2000);
channel_args.SetInt(GRPC_ARG_MAX_RECONNECT_BACKOFF_MS, 2000);
channel_args.SetInt(GRPC_ARG_HTTP2_BDP_PROBE, 0);
channel_args.SetInt(GRPC_ARG_KEEPALIVE_TIME_MS, 60000);
channel_args.SetInt(GRPC_ARG_KEEPALIVE_TIMEOUT_MS, 30000);
channel_args.SetInt(GRPC_ARG_HTTP2_MIN_SENT_PING_INTERVAL_WITHOUT_DATA_MS,
60000);
{
shared_ptr<Channel> ch(CreateCustomChannel("my_grpc_server:50051",
InsecureChannelCredentials(),
channel_args));
std::thread my_thread(&threadFunc, ch, &cq);
cout << "sleeping" << endl;
sleep(5);
cout << "slept" << endl;
cq.Shutdown();
cout << "shut down cq" << endl;
my_thread.join();
}
}
Output of the running executable:
$ ./a.out
sleeping
state 1 = 0
state 2 = 0
state 3 = 0
slept
shut down cq
state 4 = 0
E1012 15:29:07.677225824 54 channel_connectivity.cc:234] assertion failed: grpc_cq_begin_op(cq, tag)
Aborted (core dumped)
This version periodically unblocks, as expected, but why does it assert?
My question is ultimately: how do you cleanly exit from a loop/thread that is waiting on a grpc::CompletionQueue that is waiting on a grpc::Channel::NotifyOnStateChange() ?
My experience has been that with an infinite deadline, it's impossible to unblock grpc::CompletionQueue::Next(), and with a non-infinite deadline, shutting down the grpc::CompletionQueue results in an assert, which is presumably a non-clean exit.
The documentation for CompletionQueue::Shutdown()`](https://grpc.github.io/grpc/cpp/classgrpc_1_1_completion_queue.html#a40efddadd9073386fbcb4f46e8325670) says:
Also note that applications must ensure that no work is enqueued on this completion queue after this method is called.
In other words, once you shut down the CQ, it is illegal to call NotifyOnStateChange() again, because that is enqueing new work.
In this case, what you should expect to see after you call CompletionQueue::Shutdown() is that the already-invoked call to CompletionQueue::Next() will return the already-requested NotifyOnStateChange() completion, and the next call to CompletionQueue::Next() will return false, thus indicating that the CQ is shut down. However, your code is not making a call to Next() to see if the CQ is shut down before it calls NotifyOnStateChange() again to request another state change notification, so that is happening after the CQ is shut down, which is why you're seeing this assertion.
In general, the right way to use a CQ is to have a separate, dedicated set of threads that always call Next() in a loop but do not themselves start any new work on the CQs. Starting new work on the CQs should be done in separate thread(s) and should not be done after the CQ is shut down.
I hope this information is helpful.
I have a multi threaded program on Raspberry in which I want to handle SIGTERM and shut everything down gracefully. The issue is that I have a background thread that has called recvfrom() on a blocking socket. As per my understanding from the man pages, if I exit my handler all the system calls should be woken up and return with -1 and errno set to EINTR. However in my case the recvfrom call keeps hanging.
1) In general am I understanding this right, that all threads that have blocking system calls that are able to be woken up by a signal should wake up in this scenario?
2) Could it be that the operating system is setting some special signal mask on my thead?
The interresting part is that I am using the VideoCore primitives, not pthread, maybe that could be the cause? Here is a small test example:
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <signal.h>
#include "interface/vcos/vcos.h"
void SignalHandler(int nSignalNumber)
{
std::cout << "received signal " << nSignalNumber << std::endl;
}
void* ThreadMain(void* pArgument)
{
int nSocket = socket(AF_INET, SOCK_DGRAM, 0);
if (nSocket >= 0)
{
sockaddr_in LocalAddress;
memset(&LocalAddress, 0, sizeof(LocalAddress));
LocalAddress.sin_family = AF_INET;
LocalAddress.sin_addr.s_addr = INADDR_ANY;
LocalAddress.sin_port = htons(1234);
if (bind(nSocket, reinterpret_cast<sockaddr *>(&LocalAddress), sizeof(LocalAddress)) == 0)
{
sockaddr_in SenderAddress;
socklen_t nSenderAddressSize = sizeof(SenderAddress);
unsigned char pBuffer[512];
std::cout << "calling recvfrom()" << std::endl;
int nBytesReceived = recvfrom(nSocket, pBuffer, sizeof(pBuffer), 0, reinterpret_cast<struct sockaddr *>(&SenderAddress), &nSenderAddressSize);
if (nBytesReceived == -1)
{
if (errno == EINTR)
{
std::cout << "recvfrom() was interrupred by a signal" << std::endl;
}
else
{
std::cout << "recvfrom() failed with " << errno << std::endl;
}
}
}
else
{
std::cout << "bind() failed with " << errno << std::endl;
}
close(nSocket);
}
else
{
std::cout << "socket() failed with " << errno << std::endl;
}
return NULL;
}
int main(int argc, char** argv)
{
struct sigaction SignalAction;
memset(&SignalAction, 0, sizeof(SignalAction));
SignalAction.sa_handler = SignalHandler;
sigaction(SIGTERM, &SignalAction, NULL);
VCOS_THREAD_T Thread;
VCOS_STATUS_T nVcosStatus = vcos_thread_create(&Thread, "", NULL, ThreadMain, NULL);
if (nVcosStatus == VCOS_SUCCESS)
{
void* pData = NULL;
vcos_thread_join(&Thread, &pData);
}
else
{
std::cout << "vcos_thread_create() failed with " << nVcosStatus << std::endl;
}
return EXIT_SUCCESS;
}
It can be compiled like this:
g++ test.cpp -I/opt/vc/include -L/opt/vc/lib -lvcos -o test
When I run it and then call kill on the running instance the output is:
calling recvfrom()
received signal 15
and the process hangs. I'll try if a pthread behaves differently.
UPDATE
Ok I updated the sample to spawn a pthread thread as well and that one is not quitting as well. So I assume the signals are not populated to all threads?
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>
#include "interface/vcos/vcos.h"
void SignalHandler(int nSignalNumber)
{
std::cout << "received signal " << nSignalNumber << std::endl;
}
void* ThreadMain(void* pArgument)
{
const char* pThreadType = reinterpret_cast<const char*>(pArgument);
int nSocket = socket(AF_INET, SOCK_DGRAM, 0);
if (nSocket >= 0)
{
sockaddr_in LocalAddress;
memset(&LocalAddress, 0, sizeof(LocalAddress));
LocalAddress.sin_family = AF_INET;
LocalAddress.sin_addr.s_addr = INADDR_ANY;
LocalAddress.sin_port = htons(pThreadType[0] * 100);
if (bind(nSocket, reinterpret_cast<sockaddr *>(&LocalAddress), sizeof(LocalAddress)) == 0)
{
sockaddr_in SenderAddress;
socklen_t nSenderAddressSize = sizeof(SenderAddress);
unsigned char pBuffer[512];
std::cout << "calling recvfrom()" << std::endl;
int nBytesReceived = recvfrom(nSocket, pBuffer, sizeof(pBuffer), 0, reinterpret_cast<struct sockaddr *>(&SenderAddress), &nSenderAddressSize);
if (nBytesReceived == -1)
{
if (errno == EINTR)
{
std::cout << "recvfrom() was interrupred by a signal" << std::endl;
}
else
{
std::cout << "recvfrom() failed with " << errno << std::endl;
}
}
}
else
{
std::cout << "bind() failed with " << errno << std::endl;
}
close(nSocket);
}
else
{
std::cout << "socket() failed with " << errno << std::endl;
}
std::cout << pThreadType << " thread is exiting" << std::endl;
return NULL;
}
int main(int argc, char** argv)
{
struct sigaction SignalAction;
memset(&SignalAction, 0, sizeof(SignalAction));
SignalAction.sa_handler = SignalHandler;
sigaction(SIGTERM, &SignalAction, NULL);
VCOS_THREAD_T VcosThread;
VCOS_STATUS_T nVcosStatus = vcos_thread_create(&VcosThread, "", NULL, ThreadMain, const_cast<char*>("vcos"));
bool bJoinVcosThread = false;
if (nVcosStatus == VCOS_SUCCESS)
{
bJoinVcosThread = true;
}
else
{
std::cout << "vcos_thread_create() failed with " << nVcosStatus << std::endl;
}
pthread_t PthreadThread;
int nPthreadStatus = pthread_create(&PthreadThread, NULL, ThreadMain, const_cast<char*>("pthread"));
bool bJoinPthreadThread = false;
if (nPthreadStatus == 0)
{
bJoinPthreadThread = true;
}
else
{
std::cout << "pthread_create() failed with " << nPthreadStatus << std::endl;
}
if (bJoinVcosThread)
{
void* pData = NULL;
vcos_thread_join(&VcosThread, &pData);
}
if (bJoinPthreadThread)
{
void* pData = NULL;
pthread_join(PthreadThread, &pData);
}
return EXIT_SUCCESS;
}
A signal such as SIGTERM is submitted to one thread in the process only. The only precondition is that the chosen thread must either have not masked the signal, or must wait for it using sigwait. The other threads will not be directly notified that the signal has been delivered.
A common approach to combine signals with threads is to have a separate thread which handles signals only and notifies the other threads using thread synchronization mechanisms such as condition variables.
For interrupting file I/O, this may not be sufficient because there is a race condition between checking for a termination request and making the system call to perform the I/O operation. Some language run-time libraries use non-blocking I/O with poll or epoll with a special file descriptor which becomes ready on signal delivery (either using the previously-mentioned thread-based approach, or something Linux-specific like signalfd). Others try to avoid this overhead by using the read and write system calls directly with a complicated dance which uses dup2 to replace the file descriptor with one that always causes I/O to fail, thereby avoiding the race condition (but the bookkeeping needed for that is fairly complicated).
The manpage for signal reads:
If a signal handler is invoked while a system call or library function call is blocked, then either:
the call is automatically restarted after the signal handler returns; or
the call fails with the error EINTR.
Which of these two behaviors occurs depends on the interface and whether or not the signal handler was established using the SA_RESTART flag (see sigaction(2)). The details vary across UNIX systems<...>
A few lines below, recvfrom is listed among the functions that use SA_RESTART behavior by default. (Note: this behavior is disabled if there's a timeout on the socket, though.)
Thus, you should fill the sa_flags field of the sigaction structure to carefully avoid setting the SA_RESTART flag.
A good way to deal with blocking sockets -see socket(7)- (and even non blocking ones) is to use a multiplexing syscall like poll(2) (or the obsolete select(2)....)
Regarding signals, be sure to read signal(7) and signal-safety(7).
A common way to handle signals with some event loop (using poll(2)) is to have a signal handler which simply write(2)-s a byte on a pipe(7) to self (you'll setup the pipe at initialization, and you'll poll it in your event loop). The Qt documentation explains how and why. You might also use the Linux specific signalfd(2).
I managed to get the wpa_supplicant C API to work. But it behaves completly different each time I restart my Program.
The Connection succeeds every time. But then the troubles begin:
Sometimes SCAN replies an empty String but returns 0 (Ok).
In another run it replies "OK\n" and returns 0. When I loop and wait for an return of 0 and a "OK\n"-reply it runs forever with an empty reply and a 0 return.
In rare cases when SCAN returns 0 and replies "OK\n" I move on and wait for SCAN_RESULTS to return 0. At this point it behaves completely random. Sometimes it replies the whole Scan-Results. Sometimes it replies nothing but return 0 and the Scan-results are in my Event-Pipeline.
Or like in most cases: It returns 0 but does nothing. No reply, no Events. Nothing.
For debugging I reduced my Code to this snippet and try to figure out whats wrong. Im done, tried everything and I am somewhat frustrated with the Documentation of the ctrl-interface which doesn't define any workflow or tips. Im sick of reverse engineering the wpa_cli.c to figure out their flow.
I have to attach that mostly the first PING works well. Every other PING results in empty Strings.
/* some includes */
wpa_ctrl* _wpac;
static void callback(char* rply, size_t rplylen){
std::cout << std::string(rply,rplylen) << std::endl;
}
bool ScanResults() {
if(_wpac)
{
char rply[4096]; //same as in wpa_cli.c
size_t rplylen;
int retval = wpa_ctrl_request(_wpac,"SCAN_RESULTS",12,rply,&rplylen,callback);
if(retval == 0) {
std::string rplystring = std::string(rply,rplylen);
std::string message = std::string("wpa_ctrl(SCAN_RESULTS) replied: '").append(rplystring).append("' (").append(std::to_string(retval)).append(")");
std::cout << message << std::cout;
std::cout << std::string("wpa_ctrl(SCAN_RESULTS): Available (").append(std::to_string(retval)).append(")") << std::endl;
return true;
}
else
std::cout << std::string("wpa_ctrl(SCAN_RESULTS): Unavailable (").append(std::to_string(retval)).append(")") << std::endl;
return false;
}
return false;
}
bool InitScan() {
if(_wpac)
{
char rply[4096]; //same as in wpa_cli.c
size_t rplylen;
int retval = wpa_ctrl_request(_wpac,"SCAN",4,rply,&rplylen,callback);
if(retval == 0) {
std::string rplystring = std::string(rply,rplylen);
std::string message = std::string("wpa_ctrl(SCAN) replied: '").append(rplystring).append("' (").append(std::to_string(retval)).append(")");
std::cout << message << std::endl;
if(rplystring == "OK\n") {
std::string message = std::string("wpa_ctrl(SCAN): Scan initiated (").append(std::to_string(retval)).append(")");
std::cout << message << std::endl;
return true;
}
}
std::string message = std::string("wpa_ctrl(SCAN) failed: (").append(std::to_string(retval)).append(")");
std::cout << message << std::endl;
}
return false;
}
int main(){
std::string connection_string = std::string("/var/run/wpa_supplicant/").append(_interface);
wpa_ctrl* _wpac = wpa_ctrl_open(connection_string.c_str());
if(!_wpac)
return 1;
/* Well Working Attach to as Eventlistener omitted */
while(!InitScan())
sleep(1);
while(!ScanResults())
sleep(1)
return 0;
}
Try doing something like this in the appropriate places in your code
char rply[4096];
size_t rplylen = sizeof(rply);
static char cmd[] = "SCAN"; //maybe a bit easier to deal with since you need a command length
int retval = wpa_ctrl_request(_wpac, cmd, sizeof(cmd)-1, rply, &rplylen, NULL);
NULL, because I suspect you really don't need a callback routine. But put one in if you want to.
I am trying to write a threaded function that sends system information via Tcp/ip over the local network to another computer. I have been using sockets to achieve this and this has worked out quite allright thus far. But I am now at a point where this usually works but around 30% of the time I get error messages telling me that the socket can not be opened. I use the activeSocket library for the sockets.
#include "tbb/tick_count.h"
#include "ActiveSocket.h"
using namespace std;
CActiveSocket socket;
extern int hardwareStatus;
int establishTCP() {
char time[11];
int communicationFailed = 0;
memset(&time, 0, 11);
socket.Initialize();
socket.SetConnectTimeout(0, 20);
socket.SetSendTimeout(0, 20);
return communicationFailed;
}
int monitor() {
cout << "Monitor: init continious monitoring" << endl;
int communicationFailed;
tbb::tick_count monitorCounter = tbb::tick_count::now();
while (!closeProgram) {
tbb::tick_count currentTick = tbb::tick_count::now();
tbb::tick_count::interval_t interval;
interval = currentTick - monitorCounter;
if (interval.seconds() > 2) {
monitorCounter = tbb::tick_count::now();
communicationFailed = 1;
char buffer[256];
sprintf(buffer, "%d;", hardwareStatus);
establishTCP();
char *charip = new char[monitoringIP.size() + 1];
charip[monitoringIP.size()] = 0;
memcpy(charip, monitoringIP.c_str(), monitoringIP.size());
const uint8* realip = (const uint8 *) charip;
int monitorCount = 0;
cout << "Monitor: " << buffer << endl;
while (communicationFailed == 1 && monitorCount < 2) {
monitorCount++;
if (socket.Open(realip, 2417)) {
if (socket.Send((const uint8 *) buffer, strlen(buffer))) {
cout << "Monitor: Succeeded sending data" << endl;
communicationFailed = 0;
socket.Close();
} else {
socket.Close();
communicationFailed = 1;
cout << "Monitor: FAILED TO SEND DATA" << endl;
}
} else {
socket.Close();
communicationFailed = 1;
cout << "Monitor: FAILED TO OPEN SOCKET FOR DATA" << endl;
}
}
if (monitorCount == 2) cout << "Monitor: UNABLE TO SEND DATA" << endl;
}
}
return communicationFailed;
}
I think I am doing something wrong with these functions and that the problem is not on the other side of the line where this data is received. Can anyone see any obvious mistakes in this code that could cause the failure? I keep getting my own cout message "Monitor: FAILED TO OPEN SOCKET FOR DATA"
EDIT: With telnet everything works fine, 100% of the time
You can use netstat to check that the server is listening on the port and connections are being established. Snoop is another good application in your Armour for finding out what is going wrong. Another possibility is to use telnet to see if the client can connect to that IP address and port. As to the code I will take a look at it later to see if something has gone awry.
socket is a global variable. It might be re-used concurrently between two threads or sequentially inside one thread. In fact, the while(~closeProgram) loop indicates that you intend to use it sequentially.
Some documentation for CActiveSocket::Open reads: "Connection-based protocol sockets (CSocket::SocketTypeTcp) may successfully call Open() only once..."
Perhaps your program fails when you call .Open() twice on the same object.
I eventually found out the problem with my code. As the connection was unstable and working for 70% of the time it seemed to be a timeout issue. I removed the two timeout settings
socket.SetConnectTimeout(0, 20);
socket.SetSendTimeout(0, 20);
Now it works perfectly fine, thanks for the troubleshooting tips though!
I begin to develop my tool, which works with net at the TCP level, which will present simple functions of web-server.
In testing my program I have got very bad mistakes:
Memory leaks
Creating thousands of threads immediately
In taskmgr.exe you may see about ~1,5 of threads and about ~50kb of allocated memory.
Also, I compiled program as 32 bit, but in vmmap utility you may see a lot of 64 bit stacks. My OS is 64 bit, but in taskmgr.exe you may see *32 , I don’t know how 32 bit program uses 64 bit stack, maybe it’s normal for launching 32 bit program in 64 bit OS, but I have no knowledge about this design of OS, so I shall be very pleased , if you give me a piece of advice on this question.
So, why did my program creates immediately a lot of threads? ( I guess , cause of while(true) block ).
But , I want the next:
Create each thread for each new request
When request has been handled, then terminate the thread and free the memory
How should I remake my code?
Thanks!
Here is my code ( MS VC ++ 9 ):
#include <iostream>
#include <Windows.h>
#pragma comment(lib, "Ws2_32.lib")
typedef struct Header
{
friend struct Net;
private:
WORD wsa_version;
WSAData wsa_data;
SOCKET sock;
SOCKADDR_IN service;
char *ip;
unsigned short port;
public:
Header(void)
{
wsa_version = 0x202;
ip = "0x7f.0.0.1";
port = 0x51;
service.sin_family = AF_INET;
service.sin_addr.s_addr = inet_addr(ip);
service.sin_port = htons(port);
}
} Header;
typedef struct Net
{
private:
int result;
HANDLE thrd;
DWORD exit_code;
void WSAInit(WSAData *data, WORD *wsa_version)
{
result = WSAStartup(*wsa_version, &(*data));
if(result != NO_ERROR)
{
std::cout << "WSAStartup() failed with the error: " << result << std::endl;
}
else
{
std::cout << (*data).szDescription << " " << (*data).szSystemStatus << std::endl;
}
}
void SocketInit(SOCKET *my_socket)
{
(*my_socket) = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if((*my_socket) == INVALID_SOCKET)
{
std::cout << "Socket initialization failed with the error: " << WSAGetLastError() << std::endl;
WSACleanup();
}
else
{
std::cout << "Socket initialization successful!" << std::endl;
}
}
void SocketBind(SOCKET *my_socket, SOCKADDR_IN *service)
{
result = bind((*my_socket), (SOCKADDR*)&(*service), sizeof(*service));
if(result == SOCKET_ERROR)
{
std::cout << "Socket binding failed with the error: " << WSAGetLastError() << std::endl;
closesocket((*my_socket));
WSACleanup();
}
else
{
std::cout << "Socket binding successful!" << std::endl;
}
result = listen(*my_socket, SOMAXCONN);
if(result == SOCKET_ERROR)
{
std::cout << "Socket listening failed with the error: " << WSAGetLastError() << std::endl;
}
else
{
std::cout << "Listening to the socket..." << std::endl;
}
}
static void SocketAccept(SOCKET *my_socket)
{
SOCKET sock_accept = accept((*my_socket), 0, 0);
if(sock_accept == INVALID_SOCKET)
{
std::cout << "Accept failed with the error: " << WSAGetLastError() << std::endl;
closesocket(*my_socket);
WSACleanup();
}
else
{
std::cout << "Client socket connected!" << std::endl;
}
char data[0x400];
int result = recv(sock_accept, data, sizeof(data), 0);
HandleRequest(data, result);
char *response = "HTTP/1.1 200 OK\r\nServer: Amegas.sys-IS/1.0\r\nContent-type: text/html\r\nSet-Cookie: ASD643DUQE7423HFDG; path=/\r\nCache-control: private\r\n\r\n<h1>Hello World!</h1>\r\n\r\n";
result = send(sock_accept, response, (int)strlen(response), 0);
if(result == SOCKET_ERROR)
{
std::cout << "Sending data via socket failed with the error: " << WSAGetLastError() << std::endl;
closesocket(sock_accept);
WSACleanup();
}
else
{
result = shutdown(sock_accept, 2);
}
}
static void HandleRequest(char response[], int length)
{
std::cout << std::endl;
for(int i = 0; i < length; i++)
{
std::cout << response[i];
}
std::cout << std::endl;
}
static DWORD WINAPI Threading(LPVOID lpParam)
{
SOCKET *my_socket = (SOCKET*)lpParam;
SocketAccept(my_socket);
return 0;
}
public:
Net(void)
{
Header *obj_h = new Header();
WSAInit(&obj_h->wsa_data, &obj_h->wsa_version);
SocketInit(&obj_h->sock);
SocketBind(&obj_h->sock, &obj_h->service);
while(true)
{
thrd = CreateThread(NULL, 0, &Net::Threading, &obj_h->sock, 0, NULL);
//if(GetExitCodeThread(thrd, &exit_code) != 0)
//{
// ExitThread(exit_code);
//}
}
delete &obj_h;
}
} Net;
int main(void)
{
Net *obj_net = new Net();
delete &obj_net;
return 0;
}
You should create the thread AFTER you accept a connection, not before.
What you are doing is creating a ton of threads, and then having each of them wait for a connection. Many of them have nothing to do. I don't even know if Windows' accept call is thread-safe - you might end up with multiple threads handling the same connection.
What you need to do instead is, in your main loop (Net's constructor while(true)), you need to call accept(). Since accept() blocks until it has a connection, this will cause the main thread to wait until somebody tries to connect. Then, when they do, you create another thread (or process - more likely on UNIX) to handle that connection. So, your loop now looks like this:
SOCKET sock_accept = accept((*my_socket), 0, 0);
if(sock_accept == INVALID_SOCKET)
{
std::cout << "Accept failed with the error: " << WSAGetLastError() << std::endl;
closesocket(*my_socket);
WSACleanup();
}
else
{
std::cout << "Client socket connected!" << std::endl;
}
thrd = CreateThread(NULL, 0, &Net::Threading, &obj_h->sock, 0, NULL);
//push back thrd into a std::vector<HANDLE> or something like that
//if you want to keep track of it for later: there's more than one thread
Then, delete that code you moved from SocketAccept into this loop. And then, for cosmetic purposes, I would change the name of SocketAccept to SocketHandleConnection.
Now, when your thread starts, it already has a connection, and all you need to do is handle the data (e.g. what you do starting at char data[0x400]).
If you want to handle cleanup for connections, there are a few ways to do this. One, since you are threaded, you can have the thread do its own cleanup. It shares memory with the main process, so you can do this. But in this example, I don't see anything you need to clean up.
Lastly, I think you don't understand what ExitThread does. According to MSDN:
ExitThread is the preferred method of exiting a thread in C code. However, in C++ code,
the thread is exited before any destructors can be called or any other automatic cleanup
can be performed. Therefore, in C++ code, you should return from your thread function.
So it appears that you don't need to call ExitThread- you just return from your function and the thread exits automatically. You don't need to call it from the main thread.
Finally, you should really (if you can) use the new standard C++ threads in c++11, and then if you put in a little bit of effort to port your code over to boost::asio, you'll have a completely cross platform application, with no need for windows API C ugliness :D
DISCLAIMER: I only have a passing understanding of Windows as most of my experience is related to UNIX. I have attempted to be as accurate as I can but if I have any misconceptions about how this knowledge converts over to Windows, well, I warned you.
Why are you creating threads in an infinite loop? This will, of course, create tons of threads. I am referring to this piece of code:
while(true)
{
thrd = CreateThread(NULL, 0, &Net::Threading, &obj_h->sock, 0, NULL);
}