All :)
I have some piece of code with correctly works on Linux and WinXP
int FlowTestIP::do_recvfrom()
{
int ret;
struct timeval timeout;// = {2, 0};
timeout.tv_sec = 2;
timeout.tv_usec = 0;
fd_set rfds;
while(running) {
FD_ZERO(&rfds);
FD_SET(m_socket, &rfds);
ret = select(m_socket + 1, &rfds, 0, 0, &timeout);
cout << "// select ret = " << ret << " (errno = " << errno << ")" << endl;
if (ret == -1 || ret == 0) {
if (!(ret == 0 && errno == 0))
cout << "select ret = " << ret << " (errno = " << errno << ")" << endl;
return ret;
}
if (FD_ISSET(m_socket, &rfds)) {
cout << WSAGetLastError() << endl;
break;
}
}
return recvfrom(m_socket, in_buf, mtu, 0, (struct sockaddr *)&si_other, (socklen_t *)&otherAddrSize);
}
But in Windows 7 in continues to loop even when client apptication is exited.
Bad output is
...
// select ret = 1 (errno = 0)
0
// select ret = 1 (errno = 0)
0
// select ret = 1 (errno = 0)
0
FTControlServerThread::run
FTControlServerThread::recieved
pkt->ft_tos = UCHAR_MAX
QFuture::waitForFinished()
client alive true
// select ret = 1 (errno = 0)
0
// select ret = 1 (errno = 0)
0
// select ret = 1 (errno = 0)
0
normaly (on WinXP and Linux) output will be
...
// select ret = 1 (errno = 0)
0
// select ret = 1 (errno = 0)
0
// select ret = 1 (errno = 0)
0
FTControlServerThread::run
FTControlServerThread::recieved
pkt->ft_tos = UCHAR_MAX
QFuture::waitForFinished()
client alive true
QFuture::finished
And that is what I am waiting from it.
Has anyone encountered with such a misbehaviour?
Thanks in advance!
P.S. As #JoachimPileborg asked to show how I call this function:
FlowTest::recv(char *payload, size_t size)
{
...
while (running) {
len = do_recvfrom(); // it virtual method of class FlowTest
if (len == -1 || len == 0) {
return len;
}
if (running && !transport_recv_helper()) {
continue;
} else {
break;
}
}
...
}
and recv is called from server GUI
void FlowTestServer::runTest(FlowTestServerHelper handler)
{
...
if ((ret = server->recv(rcvdData, (size_t *)&size)) == -1 || ret == 0 || size == 0) {
if (server_busy == 0)
break;
cout << "can't receive data (size == " << size << "; ret = " << ret << ")" << endl;
continue;
}
...
}
Firstly: Using cout is allowed to reset errno. It doesn't usually, but it can, especially on first use when it has to set up the locale. You need to save the value of errno in a temp variable, or use fprintf and friends - as a C lib function POSIX controls its behavior.
Secondly: In Windows, you need to use WSAGetLastError() not errno.
I suggest you make a macro sock_errno which will be (errno) on Linux/unix, and (WSAGetLastError()) on Windows. That will enable to you to code in a platform independent manner.
See here for more: http://msdn.microsoft.com/en-us/library/windows/desktop/ms737828(v=vs.85).aspx
Related
I am using windows sockets with c++. In the following call I am trying to reply a message to the socket that just connected.
I tried connecting using a dummy client in c++. It would connect but the recv() would not receive anything.
Then I tried using telnet, it worked instantly, just as i wanted.
SOCKET s = accept(ls, (sockaddr*)&clientSin, &s_len);
if (s == INVALID_SOCKET) {
cerr << "Error in accept call: " << WSAGetLastError();
}
else {
cout << "Connection accepted at , socket no. :" << s << endl;
//adding to list of incoming sockets
inactiveList.push_back(s);
//send message to client requesting credentials
char buff[10];
// the character 'x' is a code to the client to provide the server with the credentials
buff[0] = 'x';
buff[1] = '\0';
//send(s, buff, 2, 0);
if (send(s, "From Vic: ", 10, 0) == INVALID_SOCKET)
{
int errorcode = WSAGetLastError();
cerr << "send to client failed: " << errorcode << endl;
closesocket(s);
continue;
}
Sleep(1000);
if (send(s, "From Vic: ", 10, 0) == INVALID_SOCKET)
{
int errorcode = WSAGetLastError();
cerr << "send to client failed: " << errorcode << endl;
closesocket(s);
continue;
}
}
the recv code is:
tnb = 0;
while ((nb = recv(s, &buff[tnb], LINESZ - tnb, 0)) > 0)
{
tnb += nb;
}
/* If there was an error on the read, report it. */
if (nb < 0)
{
printf("recv failed: %d\n", WSAGetLastError());
return 1;
}
if (tnb == 0)
{
printf("Disconnect on recv");
}
/* Make the response NULL terminated and display it. Using C output */
printf("tnb = %d\n", tnb);
buff[tnb] = '\0';
puts(buff);
Taking all my comments and turning it into an answer.
I suspect your recv loop is continuing forever because you haven't sent enough data to make it break out of the loop.
Change this:
while ((nb = recv(s, &buff[tnb], LINESZ - tnb, 0)) > 0)
{
tnb += nb;
}
To this: (notice that I'm allocating +1 for the array buff)
char buff[LINESZ+1]; // +1 for null terminator
buff[0] = '\0';
tnb = 0;
while (tnb < LINESZ)
{
nb = recv(s, &buff[tnb], LINESZ-tnb, 0);
if (nb < 0)
{
printf("Error on socket: %d\n", (int)WSAGetLastError());
break;
}
else if (nb == 0)
{
printf("Remote socket closed\n");
break;
}
printf("Received: %d bytes\n", (int)nb);
tnb += nb;
buff[tnb] = '\0'; // null terminate the end of the buffer so it will print reliably
}
I continue learn network programming using c/c++, and after that I have created multi process tcp server, I want to create simple http server, which return static resources, I use epoll so let me show my code
first of all I use fd passing for handle request in workers
so, my main function and head process
struct Descriptors{
int sv[2];
};
class Parent{
public:
static Parent& getInstance(){
static Parent instance;
return instance;
}
Parent(Parent const&) = delete;
void operator=(Parent const&) = delete;
void addFd(int fd){
m_fd.push_back(fd);
};
void run() {
startServer();
size_t index = 0;
while(true){
struct epoll_event Events[MAX_EVENTS];
int N = epoll_wait(m_epoll, Events, MAX_EVENTS, -1);
for (size_t i =0; i < N; ++i){
if (Events[i].events & EPOLLHUP){
epoll_ctl(m_epoll, EPOLL_CTL_DEL, Events[i].data.fd, &(Events[i]));
shutdown(Events[i].data.fd,SHUT_RDWR);
close(Events[i].data.fd);
continue;
}else {
if (Events[i].data.fd == m_masterSocket) {
handleConnection();
}else {
char * arg = "1";
ssize_t size = sock_fd_write(m_fd[index], arg, 1,Events[i].data.fd);
index = (1+index) % m_fd.size();
}
}
}
}
}
private:
Parent(){
m_numCpu = sysconf(_SC_NPROCESSORS_ONLN);
}
void startServer(){
m_masterSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
struct sockaddr_in SockAddr;
SockAddr.sin_family = AF_INET;
SockAddr.sin_port = htons(11141);
SockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(m_masterSocket, (struct sockaddr *)(&SockAddr), sizeof(SockAddr));
set_nonblock(m_masterSocket);
listen(m_masterSocket, SOMAXCONN);
m_epoll = epoll_create1(0);
struct epoll_event Event;
Event.data.fd = m_masterSocket;
Event.events = EPOLLIN | EPOLLRDHUP;
epoll_ctl(m_epoll, EPOLL_CTL_ADD, m_masterSocket, &Event);
}
void handleConnection(){
int SlaveSocket = accept(m_masterSocket, 0, 0);
set_nonblock(SlaveSocket);
struct epoll_event Event;
Event.data.fd = SlaveSocket;
Event.events = EPOLLIN | EPOLLRDHUP;
epoll_ctl(m_epoll, EPOLL_CTL_ADD, SlaveSocket, &Event);
}
int m_epoll;
int m_masterSocket;
int m_numCpu;
std::vector<int> m_fd;
};
void parent(int sock){
Parent::getInstance().addFd(sock);
}
int main(int argc, char **argv){
int numCpu = sysconf(_SC_NPROCESSORS_ONLN);
std::vector<Descriptors> desc;
desc.resize(numCpu);
bool isParent = true;
for (int i = 0; i < numCpu && isParent; ++i){
std::cout << "pid my is = " << getpid() <<std::endl;
int sv[2];
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) < 0) {
perror("socketpair");
exit(1);
}
pid_t forkId = fork();
switch (forkId){
case 0:{
isParent = false;
close(sv[0]);
child(sv[1]);
break;
}
case -1:
perror("fork");
exit(1);
default:
close(sv[1]);
parent(sv[0]);
break;
}
}
if (isParent){
Parent::getInstance().run();
int status;
waitpid(-1, &status, 0);
}
}
And my worker process is
void respond(int fd)
{
char mesg[99999], *reqline[3], data_to_send[BYTES], path[99999];
int rcvd, fileDesc, bytes_read;
memset( (void*)mesg, (int)'\0', 99999 );
const char *ROOT = "/home/web_server/";
int RecvResult = recv(fd,mesg, 99999, MSG_NOSIGNAL);
if (RecvResult == 0 && errno != EAGAIN){
shutdown(fd,SHUT_RDWR);
close(fd);
}else if (RecvResult >0){
printf("%s", mesg);
reqline[0] = strtok (mesg, " \t\n"); // split on lexemes
if ( strncmp(reqline[0], "GET\0", 4)==0 ) // if first 4 character equal
{
reqline[1] = strtok (NULL, " \t");
reqline[2] = strtok (NULL, " \t\n");
std::cout << "reqline 1 " << reqline[1] << std::endl;
std::cout << "reqline 2 " << reqline[2] << std::endl;
if ( strncmp( reqline[2], "HTTP/1.0", 8)!=0
&& strncmp(reqline[2], "HTTP/1.1", 8 ) !=0 )
{
write(fd, "HTTP/1.0 400 Bad Request\n", 25);
}
else
{
if ( strncmp(reqline[1], "/\0", 2)==0 )
reqline[1] = "/index.html";
strcpy(path, ROOT);
strcpy(&path[strlen(ROOT)], reqline[1]);
printf("file: %s\n", path);
if ( (fileDesc=open(path, O_RDONLY))!=-1 )
{
send(fd, "HTTP/1.0 200 OK\n\n", 17, 0);
while ( (bytes_read=read(fileDesc, data_to_send, BYTES))>0 )
write (fd, data_to_send, bytes_read);
}
else write(fd, "HTTP/1.0 404 Not Found\n", 23);
}
}
}
shutdown(fd,SHUT_RDWR);
close(fd);
}
void child(int sock)
{
int fd;
char buf[16];
ssize_t size;
sleep(1);
for (;;) {
size = sock_fd_read(sock, buf, sizeof(buf), &fd);
if (size <= 0)
break;
if (fd != -1) {
respond(fd);
}
}
printf("child processes is end\n");
}
And when I go in browser http://127.0.0.1:11141/ it is ok, and I get index.html, but when I run in apache benchmark, as
ab -n 10 -c 10 http://127.0.0.1:11141/
I get answer as
This is ApacheBench, Version 2.3
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking 127.0.0.1 (be patient)...apr_socket_recv: Connection reset by peer (104)
Total of 2 requests completed
I don't understand where is my error, because I I think that my server in theory(because using epoll ) have to resolved C10K problem. but on the practice, my server can not resolved 10 connection. Could you help me please?
Thank you for useful links and any advices!
UPDATE
When I run as
strace -f ./server 2> error.txt
in end of error.txt
[pid 6552] write(6, 0x7ffdbff00390, 757) = -1 EPIPE (Broken pipe)
[pid 6552] --- SIGPIPE {si_signo=SIGPIPE, si_code=SI_USER, si_pid=6552, si_uid=1000} ---
[pid 6552] +++ killed by SIGPIPE +++
write(1, 0x7fc5ffbe3000, 83) = 83
write(1, 0x7fc5ffbe3000, 12) = 12
write(1, 0x7fc5ffbe3000, 20) = 20
write(1, 0x7fc5ffbe3000, 41) = 41
open(0x7ffdbff18e30, O_RDONLY) = 11
sendto(10, 0x403df9, 17, 0, NULL, 0) = 17
read(11, 0x7ffdbff00390, 1024) = 757
write(10, 0x7ffdbff00390, 757) = -1 EPIPE (Broken pipe)
--- SIGPIPE {si_signo=SIGPIPE, si_code=SI_USER, si_pid=6554, si_uid=1000} ---
+++ killed by SIGPIPE +++
So I think that problem in EPipe error, But I don't understand why...
Update
So I think that problem in close descriptor, but I don't understand how to fix it. Thank you for useful advices.
UPDATE
I Get error on function send in worker process
Seems I found my error, right version function void :
void respond(int fd)
{
char mesg[99999], *reqline[3], data_to_send[BYTES], path[99999];
int rcvd, fileDesc, bytes_read;
memset( (void*)mesg, (int)'\0', 99999 );
const char *ROOT = "/home/web_server/";
int RecvResult = recv(fd,mesg, 99999, MSG_NOSIGNAL);
//EAGAIN - "there is no data available right now, try again later
if (RecvResult == 0 && errno != EAGAIN){
shutdown(fd,SHUT_RDWR);
close(fd);
std::cout << "error recv" << std::endl;
return;
}else if (RecvResult >0){
printf("%s", mesg);
reqline[0] = strtok (mesg, " \t\n"); // split on lexemes
if ( strncmp(reqline[0], "GET\0", 4)==0 ) // if first 4 character equal
{
reqline[1] = strtok (NULL, " \t");
reqline[2] = strtok (NULL, " \t\n");
std::cout << "reqline 1 " << reqline[1] << std::endl;
std::cout << "reqline 2 " << reqline[2] << std::endl;
if ( strncmp( reqline[2], "HTTP/1.0", 8)!=0
&& strncmp(reqline[2], "HTTP/1.1", 8 ) !=0 )
{
send(fd, "HTTP/1.0 400 Bad Request\n", 25 , MSG_NOSIGNAL);
}
else
{
if ( strncmp(reqline[1], "/\0", 2)==0 )
reqline[1] = "/index.html";
strcpy(path, ROOT);
strcpy(&path[strlen(ROOT)], reqline[1]);
printf("file: %s\n", path);
if ( (fileDesc=open(path, O_RDONLY))!=-1 )
{
send(fd, "HTTP/1.0 200 OK\n\n", 17, MSG_NOSIGNAL);
while ( (bytes_read=read(fileDesc, data_to_send, BYTES))>0 )
{
if (bytes_read != -1)
send (fd, data_to_send, bytes_read, MSG_NOSIGNAL);
}
}
else send(fd, "HTTP/1.0 404 Not Found\n", 23, MSG_NOSIGNAL);
}
shutdown(fd,SHUT_RDWR);
close(fd);
}
}else {
std::cout << "Client disconnected unexpect" << std::endl;
}
}
Problem was that , that I close socket , and after that I try to read from this socket.
I'm having an issue with winsock on windows 8.1 where recv keeps returning 0 randomly. I'm running both client and server on the same machine (thus all traffic is pointed at the loopback address) and i have breakpoints on any statement on both client and server which would shut down the sockets. But when the issue occurs the server is still operating normally, and hasn't shutdown anything, while the client has hit a breakpoint that only triggers on recv returning 0 or less.
The client keeps returning 0 from recv randomly (although always at the same point in my code) when the server VS 2013 project is set to compile as a windows program (rather than a console, in order to make it produce no window, it's supposed to be silent running). The bug doesn't seem to occur when i compile the server as a console application, as I've been debugging the program in that mode and only come across this issue when i switched compilation settings.
Is there any way to launch a console window when compiling as a windows application so i can see any debug statements?
Does winsock behave differently when compiling for the console vs compiling for a windows application?
Why would the client's recv return 0, when I've not sent a shutdown signal from the server?
My code, ask if there's any more you need:
Client
void recvLoop()
{
int recievedBytes = 1;
while (running && recievedBytes > 0)
{
WORD dataSize;
WORD dataType;
int recievedBytesA = ConnectSock.Recieve(&dataSize, sizeof(WORD));
if (recievedBytesA <= 0)
{
closing = true; //breakpoint set here
attemptKillThreads();
continue;
}
int recievedBytesB = ConnectSock.Recieve(&dataType, sizeof(WORD));
if (recievedBytesB <= 0)
{
closing = true; //breakpoint set here
attemptKillThreads();
continue;
}
unique_ptr<char[]> data(new char[dataSize]);
int recievedBytesC = ConnectSock.Recieve(data.get(), dataSize);
if (recievedBytesC <= 0)
{
closing = true; //breakpoint set here - Always triggers here
attemptKillThreads();
continue;
}
//use the received data.....
}
}
When this breaks recievedBytesA = 2, recievedBytesB = 2, recievedBytesC = 0, dataType = 0, dataSize = 0
ConnectSock is a global of type ConnectSocket. here is its Recieve()
int ConnectSocket::Recieve(void *recvbuf, int recvbuflen)
{
if (sock != INVALID_SOCKET)
{
int i = recv(sock, (char *)recvbuf, recvbuflen, 0);
if ((i == SOCKET_ERROR))
{
int err = 0;
err = WSAGetLastError();
if (err != WSAEINTR)
{
//ignore WSAEINTR as that's likely to be because of a shutdown complating a bit awkwardly
cout << "error: " << err << endl;
}
}
return i;
}
return -2;
}
Server:
void sendLoop()
{
int bytessent = 0;
QueuePack tosend;
while (running)
{
tosend = sendQueue.Dequeue();
if (tosend.packType == QueuePack::EXIT || tosend.packType == 0 || tosend.dSize == 0)
{
attemptKillThreads();
continue;
}
bytessent = Connection->SendData(&tosend.dSize, sizeof(WORD));
//cout used to see what exactly is being sent, even if it is garbage when converted to text
cout << tosend.dSize << endl;
cout << bytessent << endl;
if (bytessent <= 0)
{
attemptKillThreads();
continue;
}
bytessent = Connection->SendData(&tosend.packType, sizeof(WORD));
cout << tosend.dSize << endl;
cout << bytessent << endl;
if (bytessent <= 0)
{
attemptKillThreads();
continue;
}
bytessent = Connection->SendData(tosend.bufferPtr(), tosend.dSize);
cout << tosend.bufferPtr() << endl;
cout << bytessent << endl;
if (bytessent <= 0)
{
attemptKillThreads();
}
}
if (Connection->shutdownSock(SD_SEND) == SOCKET_ERROR)
{
Connection->closeSock();
}
}
SendData is literally a wrapper for send that uses a reinterpret_cast
int SendData(void * writeBuffer, int bufferSize)
{
return send(SocketManager.clientSocket, reinterpret_cast<char *>(writeBuffer), bufferSize, 0);
}
sendQueue is a Bounded blocking queue that holds QueuePacks
QueuePacks are used to transfer data, it's size and what kind of data it is between threads. both Client and server use this as it allows me to make sure data gets to the right thread on the client
Queuepack has 2 public variables packType and dSize of type WORD.
QueuePack::QueuePack() : packType(UND), dSize(0)
{
int h = 0; //debug statement to break on - never gets hit after initial collection construction occurs
}
QueuePack::QueuePack(const WORD type, WORD size, char * data) : packType(type), dSize(size)
{
//debug test and statement to break on
if (size == 0 || type == 0)
{
int h = 0; //breakpoint - never gets hit
}
dSize = (dSize < 1 ? 1 : dSize);
_buffer = make_unique<char[]>(dSize);
memcpy(_buffer.get(), data, dSize);
}
QueuePack::QueuePack(QueuePack &other) : packType(other.packType), dSize(other.dSize)
{
//debug test and statement to break on
if (other.dSize == 0 || other.packType == 0)
{
int h = 0; //breakpoint - never gets hit
}
if (&other == this)
{
return;
}
_buffer = make_unique<char[]>(dSize);
other.buffer(_buffer.get());
}
QueuePack QueuePack::operator= (QueuePack &other)
{
// check for self-assignment
if (&other == this)
{
return *this;
}
// reuse storage when possible
if (dSize != other.dSize)
{
_buffer.reset(new char[other.dSize]);
dSize = other.dSize;
}
packType = other.packType;
other.buffer(_buffer.get());
return *this;
}
QueuePack::~QueuePack()
{
}
HRESULT QueuePack::buffer(void* container)
{
try
{
memcpy(container, _buffer.get(), dSize);
}
catch (...)
{
return E_FAIL;
}
return S_OK;
}
char * QueuePack::bufferPtr()
{
return _buffer.get();
}
When this breaks recievedBytesA = 2, recievedBytesB = 2, recievedBytesC = 0, dataType = 0, dataSize = 0
You are calling ConnectSock.Recieve() when dataSize is 0. There is nothing to read, so Receive() reports that 0 bytes were read.
You need to add a check for that condition:
unique_ptr<char[]> data(new char[dataSize]);
if (dataSize != 0) // <-- add this
{
int recievedBytesC = ConnectSock.Recieve(data.get(), dataSize);
if (recievedBytesC <= 0)
{
closing = true;
attemptKillThreads();
continue;
}
}
Your code is also assuming that Recieve() reads all bytes that are requested, it is not handling the possibility that recv() may return fewer bytes. So you need to make Recieve() call recv() in a loop to guarantee that everything requested is actually read:
int ConnectSocket::Recieve(void *recvbuf, int recvbuflen)
{
if (sock == INVALID_SOCKET)
return -2;
char *buf = static_cast<char *>(recvbuf);
int total = 0;
while (recvbuflen > 0)
{
int i = recv(sock, buf, recvbuflen, 0);
if (i == SOCKET_ERROR)
{
int err = WSAGetLastError();
if (err != WSAEINTR)
{
//ignore WSAEINTR as that's likely to be because of a shutdown complating a bit awkwardly
cout << "error: " << err << endl;
}
return -1;
}
if (i == 0)
{
cout << "disconnected" << endl;
return 0;
}
buf += i;
recvbuflen -= i;
total += i;
}
return total;
}
Same with SendData(), as send() may return fewer bytes than requested:
int SendData(void * writeBuffer, int bufferSize)
{
if (SocketManager.clientSocket == INVALID_SOCKET)
return -2;
char *buf = static_cast<char *>(writeBuffer);
int total = 0;
while (bufferSize > 0)
{
int i = send(SocketManager.clientSocket, buf, bufferSize, 0);
if (i == SOCKET_ERROR)
{
int err = WSAGetLastError();
if (err != WSAEINTR)
{
//ignore WSAEINTR as that's likely to be because of a shutdown complating a bit awkwardly
cout << "error: " << err << endl;
}
return -1;
}
buf += i;
bufferSize -= i;
total += i;
}
return total;
}
The following program is written to receive clients' messages and read user inputs from keyboard:
FD_ZERO(&masterfds);
FD_SET(udp_con, &masterfds);
FD_SET(0, &masterfds);
maxfds = udp_con;
while(exit == false)
{
FD_ZERO(&readfds);
readfds = masterfds;
selectFunc = select(maxfds+1, &readfds, NULL, NULL, &tv);
if(selectFunc < 0) {
message("error in select");
exit = true;
} else {
// The server has received something from a client
for(i = 0; i <= maxfds; i++) {
if(FD_ISSET(i, &readfds)) {
if(FD_ISSET(0, &readfds)) {
fgets(userInput, sizeof(userInput), stdin);
int len = strlen(userInput) - 1;
if (userInput[len] == '\n') {
userInput[len] = '\0';
}
cout<<"The user said: "<<userInput<<endl;
} else if(i == udp_con) {
cout<<"Datagram received"<<endl;
// After reading the user input, it never reaches here
}
}
}
}
}
The problem is that when I press the 'enter' key on the keyboard and the '0' file descriptor activates, the program will never activate any other file descriptor and it locks on the '0' file descriptor. How can I fix this bug?
You need to FD_CLR(0, readfds) after you test it with FD_ISSET(0, &readfds), or else it will always take that branch.
But you can simplify the algorithm by rewriting it. It's a good idea to get into the habit of formatting your code to make it easily readable.
FD_ZERO(&masterfds);
FD_SET(udp_con, &masterfds);
FD_SET(0, &masterfds);
maxfds = udp_con;
while (true) {
readfds = masterfds;
selectFunc = select(maxfds + 1, &readfds, NULL, NULL, &tv);
if (selectFunc < 0) {
message("error in select");
break;
}
// Check for input on stdin (fd 0).
if (FD_ISSET(0, &readfds)) {
fgets(userInput, sizeof(userInput), stdin);
int len = strlen(userInput) - 1;
if (userInput[len] == '\n') {
userInput[len] = '\0';
}
cout << "The user said: '" << userInput << "'" << endl;
}
// Check for input on the udp_con fd.
if (FD_ISSET(udp_con, &readfds)) {
cout << "Datagram received" << endl;
}
}
EDIT: I am guessing the problem is I have to associate the OVERLAPPED or WSAOVERLAPPED in the container with my completion port. Is that correct?
I can get IO completions when someone connects to my server. I then use CreateIoCompletionPort on the new socket, with the completionport that original was used. But when they send me data, it does not get set off. Although, it still gets set off if someone else connects. My question is, why would this happen? I also make sure CreateIoCompletionPort returns the same handle as was the original. What gives?
EDIT:
DWORD WINAPI worker_thread(LPVOID lpParam) {
client_information_class *cicc = NULL;
HANDLE CompletionPort = (HANDLE)lpParam;
ULONG_PTR Key;
DWORD BytesTransfered;
OVERLAPPED *lpOverlapped = NULL;
DWORD error = NULL;
while(1) {
error = GetQueuedCompletionStatus(CompletionPort, &BytesTransfered, (PULONG_PTR)&Key, &lpOverlapped, 0);
cicc = CONTAINING_RECORD ( lpOverlapped, client_information_class, ol );
if ( error == TRUE ) {
cout << endl << "IO TRIGGERED" << endl;
switch ( cicc->operation ) {
/*#define OP_ACCEPT 0
#define OP_READ 1
#define OP_WRITE 2*/
case 0:{
if ( check_auth_progress ( cicc->client_socket , cicc->client_buff , BytesTransfered ) ) {
cout << "Client " << cicc->client_socket << " connected." << endl;
client_information_class *k = NULL;
SOCKADDR_STORAGE *LocalSockaddr=NULL, *RemoteSockaddr=NULL;
int LocalSockaddrLen,RemoteSockaddrLen;
k = (client_information_class *)Key;
k->lpfnGetAcceptExSockaddrs(
cicc->client_buff,
cicc->client_len - ((sizeof(SOCKADDR_STORAGE) + 16) * 2),
sizeof(SOCKADDR_STORAGE) + 16,
sizeof(SOCKADDR_STORAGE) + 16,
(SOCKADDR **)&cicc->LocalSockaddr,
&cicc->LocalSockaddrLen,
(SOCKADDR **)&cicc->RemoteSockaddr,
&cicc->RemoteSockaddrLen
);
client_information_class *cicc2 = NULL;
cicc2 = ( client_information_class *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(client_information_class) + (sizeof(BYTE) * 4096));
if (cicc2 == NULL) {
fprintf(stderr, "Out of memory!\n");
}
cicc2->client_socket = cicc->client_socket;
cicc2->client_socketaddr_in = cicc->client_socketaddr_in;
cicc2->LocalSockaddr = cicc->LocalSockaddr;
cicc2->LocalSockaddrLen = cicc->LocalSockaddrLen;
cicc2->RemoteSockaddr = cicc->RemoteSockaddr;
cicc2->RemoteSockaddrLen = cicc->RemoteSockaddrLen;
HANDLE hrc = CreateIoCompletionPort( (HANDLE)cicc2->client_socket, CompletionPort, (ULONG_PTR)cic, 0 );
if (hrc == NULL) {
fprintf(stderr, "CompletionThread: CreateIoCompletionPort failed: %d\n", GetLastError());
return 0;
} else {
fprintf(stderr, "CompletionThread: CreateIoCompletionPort: %d\n", hrc);
}
cic->deleteNode ( cicc->client_socket , cic );
cic->addNode ( cicc2 );
} else {
cout << endl << "Something Happened ... " << endl;
}
}break;
case 1:{
if ( ParsePacket ( cicc->client_socket , data ) ) {
cout << "Client " << cicc->client_socket << " connected." << endl;
} else {
cout << endl << "Something Happened ... " << endl;
}
}break;
default:{
cout << endl << "Didnt catch that operation ... " << cicc->operation << endl;
}break;
}
} else if ( error == FALSE && &lpOverlapped == NULL ) {
// no packet was dequed...
fprintf(stderr, "[error == FALSE && &lpOverlapped == NULL] CompletionThread: GetQueuedCompletionStatus failed: %d [0x%x]\n", GetLastError(), &lpOverlapped->Internal);
} else if ( error == FALSE && &lpOverlapped != NULL ) {
if((DWORD)&lpOverlapped->Internal == 0x0) { // a timeout...
} else {
fprintf(stderr, "[error == FALSE && &lpOverlapped != NULL] CompletionThread: GetQueuedCompletionStatus failed: %d [0x%x]\n", GetLastError(), &lpOverlapped->Internal);
}
}
}
ExitThread(0);
return 0;
}
Id hate to do this again, but I was correct, you have to place the socket into a new mode (much like acceptex) using WSARECV: I did not know this, and its not very clear on the MSDN, and one of the sources I was looking at to learn IOCP, doesn't talk about it. Hopefully this helps someone :/
WSABUF wbuf;
DWORD bytes, flags;
wbuf.buf = cicc2->client_buff;
wbuf.len = cicc2->client_len;
flags = 0;
int rr = WSARecv ( cicc2->client_socket , &wbuf , 1 , &bytes , &flags , &cicc2->ol , NULL );
if (rr == FALSE) {
if (WSAGetLastError() != WSA_IO_PENDING) {
printf("PostRecv: WSARecv* failed: %d\n", WSAGetLastError());
closesocket(cicc2->client_socket);
cic->deleteNode ( cicc2->client_socket , cic );
}
fprintf(stderr, "PostRecv: WSARecv* failed: %d\n", GetLastError());
}