I have basic example of using poll() method:
struct pollfds fds[5];
int nfds = 1;
fds[0].fd = listen_sd;
fds[0].events = POLLIN;
while(1) {
int ret = poll(fds, nfds, 0);
if (ret < 0) {
exit(1);
}
int size = nfds;
for (int i = 0; i < size; i++) {
if (fds[i].revents == 0) {
continue;
}
if (fds[i].revents != POLLIN) {
exit(1);
}
if (fds[i].fd == listen_sd) {
// Something happened on the server
// If there is a client which wasn't accepted yet, it returns its fd
int new = accept(listen_sd, null, null);
// If there is a client which was already accepted,
// it doesn't return anything, just loop in accept() method
} else {
// Something happened on a different socket than the server
}
}
}
All messages from clients will flow through server socket.
It means there is always something happened on the server socket, isn't it?
But how can I do something like this (in true condition):
Try to accept a socket, some function should return: the socket is already
accepted, the socket hasn't been accepted yet.
When the socket is already accepted - receive the data.
When the socket hasn't been accepted yet - accept the socket and
receive the data.
On the server side, a TCP socket listening for connections will enter a readable state when it has a client connection waiting to be accepted. Call accept() to actually accept the connection, and then you can start monitoring for read/write/close states on the new TCP socket returned by accept(). This is a completely separate socket than the one that is listening for connections. Add it to your pollfds array so you can poll both it and the listening socket at the same time.
Try something like this:
#include <vector>
std::vector<pollfds> fds;
{
pollfds listen_pf;
listen_pf.fd = listen_sd;
listen_pf.events = POLLIN;
listen_pf.push_back(listen_pf);
}
while (true) {
int ret = poll(fds.data(), fds.size(), 0);
if (ret < 0) {
exit(1);
}
size_t i = 0, size = fds.size();
while (i < size) {
if (fds[i].revents & POLLIN) {
if (fds[i].fd == listen_sd) {
// accept a pending client connection...
int client_sd = accept(listen_sd, NULL, NULL);
if (client_sd != -1) {
pollfds client_pf;
client_pf.fd = client_sd;
client_pf.events = POLLIN | POLLOUT | POLLRDHUP;
fds.push_back(client_pf);
}
}
else {
// read data from fds[i].fd as needed...
if (read fails) {
fds.erase(fds.begin()+i);
--size;
continue;
}
}
}
if (fds[i].revents & POLLOUT) {
// write pending data to fds[i].fd as needed ...
if (write fails) {
fds.erase(fds.begin()+i);
--size;
continue;
}
}
if (fds[i].revents & (POLLERR | POLLHUP | POLLNVAL)) {
if (fds[i].fd == listen_fd) {
exit(1);
}
else {
fds.erase(fds.begin()+i);
--size;
continue;
}
}
++i;
}
}
On the client side, a TCP socket connecting to a server will enter a writable state when connect() is successful and the connection has been fully established with the server and is ready for I/O.
Related
So I'm writing a very basic TCP server which just echoes back the messages sent from the client back to the client. I have a setup where the server is running in a while loop and waiting for either a new client to connect or for a message from one of the existing clients using the select() method.
My question is:
How do i, from the server side, close the master socket and basically shutting down the server. Im not asking for exact code but more for standard practice.
For some context:
In the long run I am imagining multiple clients connected to my server and I need to shutdown the server gracefully from the server side.
Even more context: The server code.
#define TRUE 1
#define FALSE 0
#define PORT 55555
int main(int argc, char* argv[])
{
int opt = TRUE;
int masterSocket, addrlen, newSocket, maxClients = 10, clientSockets[maxClients],
activity, valread, sd, maxSd;
struct sockaddr_in address;
char buffer[1025];
fd_set readfds;
const char *message = "ECHO DAMON v1.0 \r\n";
/* Initialize array with 0 so it's not read */
for(int i = 0; i < maxClients ; i++)
{
clientSockets[i] = 0;
}
/* Create master socket */
if((masterSocket = socket(AF_INET, SOCK_STREAM,0)) == 0)
{
perror("Error when trying to create master socket.\n");
exit(EXIT_FAILURE);
}
/* Set master socket to allow multiple connections */
if( setsockopt(masterSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt)) < 0)
{
perror("Could not set sockopt");
exit(EXIT_FAILURE);
}
/* Socket type */
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);
if( bind(masterSocket, (struct sockaddr*)&address, sizeof(address)) < 0)
{
perror("Error, could not bind master socket. \n");
exit(EXIT_FAILURE);
}
printf("Listening on %d. \n", PORT);
if( listen(masterSocket, 3) < 0)
{
perror("Error, could not listen.\n");
exit(EXIT_FAILURE);
}
addrlen = sizeof(address);
puts("Waiting for connections...\n"); //just a printf variant
/* END INIT */
while(TRUE)
{
/* Clear socket set */
FD_ZERO(&readfds);
/* Add master socket to set */
FD_SET(masterSocket, &readfds);
/* Add child sockets to set, will be 0 first iteration */
for(int i = 0; i < maxClients ; i++)
{
sd = clientSockets[i]; // sd = socket descriptor
/* If valid socket descriptor */
if(sd > 0)
{
FD_SET(sd, &readfds);
}
/* Get highest fd number, needed for the select function (later) */
if(sd > maxSd)
{
maxSd = sd;
}
}//end for-loop
/* Wait for activity on any socket */
activity = select(maxSd +1, &readfds, NULL, NULL, NULL);
if((activity < 0) && (errno != EINTR))
{
printf("Error on select.\n"); //no need for exit.
}
/* If the bit for the file descriptor fd is set in the
file descriptor set pointed to by fdset */
/* If something happend in the master socket, its a new connection */
if(FD_ISSET(masterSocket, &readfds))
{
if((newSocket = accept(masterSocket, (struct sockaddr*)&address, (socklen_t*)&addrlen)) < 0)
{
perror("Could not accept new socket.\n");
exit(EXIT_FAILURE);
}
/* Print info about connector */
printf("New connection, socket fd is %d, ip is: %s, port: %d\n", newSocket, inet_ntoa(address.sin_addr), ntohs(address.sin_port));
if( send(newSocket, message, strlen(message), 0) != strlen(message))
{
perror("Could not sent welcome message to new socket.\n");
}
puts("Welcome message sen successfully.\n");
/* Add new socket to array of clients */
for(int i = 0; i < maxClients; i++)
{
if(clientSockets[i] == 0)
{
clientSockets[i] = newSocket;
printf("Adding socket to list of client at index %d\n", i);
break;
}
}
}//end masterSocket if
/* Else something happend at client side */
for(int i = 0; i < maxClients; i++)
{
sd = clientSockets[i];
if(FD_ISSET(sd, &readfds))
{ /* Read socket, if it was closing, else read value */
if((valread = read( sd, buffer, 1024)) == 0)
{
getpeername( sd, (struct sockaddr*)&address, (socklen_t*)&addrlen);
printf("Host disconnected, ip %s, port %d.\n", inet_ntoa(address.sin_addr), ntohs(address.sin_port));
close(sd);
clientSockets[i] = 0;
}
}
else
{
buffer[valread] = '\0';
send(sd, buffer, strlen(buffer), 0);
}
}
}
return 0;
}
If you are planning to exit your application gracefully by adding a breakstatement in your app by something similar to this:
if (exit_condition)
{
break;
}
You can place this loop at the end of your main function:
/* close connections gracefully */
closesocket(masterSocket);
masterSocket = 0; /* optional, see comment below */
for(int i = 0; i < maxClients; i++)
{
if (clientSockets[i] != 0)
{
shutdown(clientSockets[i]);
closesocket(clientSockets[i]);
clientSockets[i] = 0; /* optional, except if you also have a SIGINT handler */
}
}
If you want to do the same to handle an exit with Ctrl-C, you will find details on how to setup a SIGINT handler to handle Ctr-C here:
Catch Ctrl-C in C. Place the above loop in your handler, then. Your sockets-related variables will have to be declared at global scope, since your sockets are only visible from main() in your current code.
I have a problem where the returned file descriptor would gradually increase to be a number larger than FD_SETSIZE.
My tcp server is continually shutdown which requires my client to close the socket and reconnect. The client will then attempt to reconnect to the server by calling socket to obtain a new file descriptor before calling connect.
However it appears that everytime I call socket the file descriptor returned is incremented and after a certain amount of time it becomes larger than FD_SETSIZE, which is a problem where I use select to monitor the socket.
Is it ok to reuse the first file descriptor returned from socket for the connect call even though the the socket was closed? Or is there other workarounds?
Reconnect code (looping until connected):
int s = getaddrinfo(hostname, port, &hints, &result);
if (s != 0) { ... HANDLE ERROR ...}
...
struct addrinfo *rp;
int sfd;
for (rp = result; rp != NULL; rp -> ai_protocol)
{
sfd = socket( rp->ai_family, rp->ai_sockettype, rp->ai_addrlen);
if (sfd >= 0)
{
int res = connect(sfd, rp->ai_addr, rp->ai_addrlen);
if (res != -1)
{
_sockFd = sfd;
_connected = true;
break;
}
else
{
close (sfd);
break;
}
}
}
if (result != NULL)
{
free(result);
}
Read Message code:
if (_connected)
{
...
retval = select(n, &rec, NULL, NULL, &timeout);
if (retval == -1)
{
...
_connected = false;
close(_sockFd);
}
else if (retval)
{
if (FD_ISSET(_sockFD, &rec) == 0)
{
....
return;
}
int count = read(...)
if (count)
{
....
return;
}
else
{
....
_connected = false;
close(_sockFd);
}
}
}
You're not closing the socket if the connect fails. So it remains open, occupying an FD, so next time you call socket() you get a new FD. You're also not breaking out of your loop when connect() succeeds, which is another leak. You're also not checking the result of read() for -1.
I have an SSL server (code listed below) connecting to multiple SSL client. I am using a single context and the initialization is
SSL_CTX *ctx;
SSL *ssl[MAXEVENTS];
SSL_library_init();
ctx = InitServerCTX(); // initialize SSL
...
...
Then I have the following piece of code
ssl[i] = SSL_new(ctx); // get new SSL state with context
SSL_set_fd(ssl[i], infd); // set connection socket to SSL state
Then I perform SSL_accept(ssl[i]).
All this is being performed using epoll (edge trigger mode). I have modified the example in https://banu.com/blog/2/how-to-use-epoll-a-complete-example-in-c/ to use SSL by referring to https://www.cs.utah.edu/~swalton/listings/articles/ssl_server.c as a reference
The logic for this is
events = new epoll_event[MAXEVENTS * sizeof event];
// The event loop
while (true)
{
int n, i;
n = epoll_wait (efd, events, MAXEVENTS, -1);
for (i = 0; i < n; i++)
{
if ((events[i].events & EPOLLERR) ||
(events[i].events & EPOLLHUP) ||
(!(events[i].events & EPOLLIN)))
{
// An error has occured on this fd, or the socket is not
// ready for reading (why were we notified then?)
fprintf (stderr, "epoll error\n");
close (events[i].data.fd);
continue;
} else if (sfd == events[i].data.fd) {
// We have a notification on the listening socket, which
// means one or more incoming connections.
while (1)
{
struct sockaddr in_addr;
socklen_t in_len;
int infd;
char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
in_len = sizeof in_addr;
infd = accept (sfd, &in_addr, &in_len);
if (infd == -1)
{
if ((errno == EAGAIN) ||(errno == EWOULDBLOCK)) {
// We have processed all incoming
// connections.
break;
} else {
perror ("accept");
break;
}
}
s = getnameinfo (&in_addr, in_len,
hbuf, sizeof hbuf,
sbuf, sizeof sbuf,
NI_NUMERICHOST | NI_NUMERICSERV);
if (s == 0) {
printf("Accepted connection on descriptor %d "
"(host=%s, port=%s)\n", infd, hbuf, sbuf);
}
ssl[i] = SSL_new(ctx); // get new SSL state with context
SSL_set_fd(ssl[i], infd); // set connection socket to SSL state
int ret;
if ( (ret=SSL_accept(ssl[i])) == FAIL ) { // do SSL-protocol accept
ERR_print_errors_fp(stderr);
printf("Performing exchange Error 1.\n");
int error = SSL_get_error(ssl[i], 0);
//TODO A retry timer or retry counter. Cannot keep retrying perpetually.
if (ret <=0 && (error == SSL_ERROR_WANT_READ)) {
//Need to wait until socket is readable. Take action?
//LOG the reason here
perror ("Need to wait until socket is readable.");
} else if (ret <=0 && (error == SSL_ERROR_WANT_WRITE)) {
//Need to wait until socket is writable. Take action?
//LOG the reason here
perror ("Need to wait until socket is writable.");
} else {
//LOG the reason here
perror ("Need to wait until socket is ready.");
}
shutdown (infd, 2);
SSL_free (ssl[i]);
continue;
}
// Make the incoming socket non-blocking and add it to the
// list of fds to monitor.
s = SocketNonBlocking (infd);
if (s == -1) {
abort ();
}
event.data.fd = infd;
event.events = EPOLLIN | EPOLLET | EPOLLHUP;
s = epoll_ctl (efd, EPOLL_CTL_ADD, infd, &event);
if (s == -1) {
perror ("epoll_ctl");
abort ();
}
}
continue;
Now,
while (1)
{
ssize_t count;
char buf[1024];
char reply[1024];
printf("Performing exchange.\n");
const char* HTMLecho="<html><body><pre>%s</pre></body></html>\n\n";
ShowCerts(ssl[i]); // get any certificates
count = SSL_read(ssl[i], buf, sizeof(buf)); // get request
int32_t ssl_error = SSL_get_error (ssl[i], count);
switch (ssl_error) {
case SSL_ERROR_NONE:
printf("SSL_ERROR_NONE\n");
break;
case SSL_ERROR_WANT_READ:
printf("SSL_ERROR_WANT_READ\n");
break;
case SSL_ERROR_WANT_WRITE:
printf("SSL_ERROR_WANT_WRITE\n");
break;
case SSL_ERROR_ZERO_RETURN:
printf("SSL_ERROR_ZERO_RETURN\n");
break;
default:
break;
}
if (( count > 0 ) )
{
buf[count] = 0;
printf("count > 0 Client msg: \"%s\"\n", buf);
sprintf(reply, HTMLecho, buf); // construct reply
SSL_write(ssl[i], reply, strlen(reply)); // send reply
} else if ((count < 0) ){
printf("count < 0 \n");
if (errno != EAGAIN)
{
printf("count < 0 errno != EAGAIN \n");
perror ("read");
done = 1;
}
break;
} else if (count==0){
ERR_print_errors_fp(stderr);
epoll_ctl(efd, EPOLL_CTL_DEL, events[i].data.fd, NULL);
printf("count == 0 Client Disconnected.\n");
done = 1;
break;
}
}
if (done)
{
printf("Freeing data.\n");
int sd = SSL_get_fd(ssl[i]);
SSL_free(ssl[i]); // release SSL state
close(sd); // close connection
//close (events[i].data.fd);
}
}
This works fine for one server - one client. But the moment I try to connect two clients, the client that connected last is the only one that receives the data. The client that was connected before just keeps hanging without any activity.
UPDATE
I found that there is some indexing issue going on here. The value of the variable i from the epoll example is not corresponding to what I think it should be corresponding. I tried connecting two clients and I had thought initially the i should have incremented for second client but it is not the case. It still stays 0.
Ok I solved the issue. My problem stemmed from incorrect indexing. I was relying on the variable i that did not behave the way I expected. (see update in my question)
First I declare std::map<int,SSL*> sslPairMap;
Then I insert the successful fd and SSL accept into a std::map in C++. In C, one can use struct based pairing. There is one example here https://github.com/dCache/dcap/blob/b432bd322f0c1cf3e5c6a561845899eec3acad1e/plugins/ssl/sslTunnel.c
//(c) 2014 enthusiasticgeek for stack overflow
sslPairMap.insert(std::pair<int,SSL*>(infd, ssl));
// Make the incoming socket non-blocking and add it to the
// list of fds to monitor.
s = AibSocketNonBlocking (infd);
if (s == -1) {
abort ();
}
aibevent.data.fd = infd;
aibevent.events = EPOLLIN | EPOLLET | EPOLLHUP;
s = epoll_ctl (efd, EPOLL_CTL_ADD, infd, &aibevent);
if (s == -1) {
perror ("epoll_ctl");
abort ();
}
After this I simply retrieve the SSL* from the map which ensures I do not change index inadvertently. std::map saves the day
//(c) 2014 enthusiasticgeek for stack overflow
while (1)
{
ssize_t count;
char buf[1024];
char reply[1024];
printf("Performing exchange where i = %d.\n",i);
const char* HTMLecho="<html><body><pre>%s</pre></body></html>\n\n";
ShowCerts(sslPairMap[aibevents[i].data.fd]); // get any certificate
count = SSL_read(sslPairMap[aibevents[i].data.fd], buf, sizeof(buf)); // get request
ssl_error = SSL_get_error (sslPairMap[aibevents[i].data.fd], count);
switch (ssl_error) {
case SSL_ERROR_NONE:
printf("SSL_ERROR_NONE\n");
break;
case SSL_ERROR_WANT_READ:
printf("SSL_ERROR_WANT_READ\n");
break;
case SSL_ERROR_WANT_WRITE:
printf("SSL_ERROR_WANT_WRITE\n");
break;
case SSL_ERROR_ZERO_RETURN:
printf("SSL_ERROR_ZERO_RETURN\n");
break;
default:
break;
}
if (( count > 0 ) )
{
buf[count] = 0;
printf("count > 0 Client msg: \"%s\"\n", buf);
sprintf(reply, HTMLecho, buf); // construct reply
SSL_write(sslPairMap[aibevents[i].data.fd], reply, strlen(reply)); // send reply
break;
} else if ((count < 0) ){
printf("count < 0 \n");
if (errno != EAGAIN)
{
printf("count < 0 errno != EAGAIN \n");
perror ("read");
done = 1;
}
break;
} else if (count==0){
ERR_print_errors_fp(stderr);
epoll_ctl(efd, EPOLL_CTL_DEL, aibevents[i].data.fd, NULL);
printf("count == 0 Client Disconnected.\n");
done = 1;
break;
}
}
if (done)
{
printf("Freeing data.\n");
int sd = SSL_get_fd(sslPairMap[aibevents[i].data.fd]);
if(ssl_error == SSL_ERROR_NONE){
SSL_shutdown(sslPairMap[aibevents[i].data.fd]);
}
SSL_free(sslPairMap[aibevents[i].data.fd]); // release SSL state
close(sd); // close connection
//close (aibevents[i].data.fd);
erase_from_map(sslPairMap, aibevents[i].data.fd);
}
}
If anyone comes across this, another way to go about it is to store the SSL* pointer in the event data itself:
events[i].data.u64 = (long long)ssl;
And when you need to read/write from it:
auto ssl = (SSL*)events[i].data.u64;
SSL_read(ssl, someBuffer, sizeof(someBuffer));
For example.
I am writing a single chat program with GUI. I wanted to write a server that would accept many clients. Every client can connect successfuly. But there is a strange problem with sending and receiving data. I use select() and a thread to handle many sockets at the same time. If a client sends some data to server, it will receive it and send it back to that client (the client is especially written without "prediction"). But the server won't send it further to another clients (like every client had its own private conversation with the server). Here's my code:
// this is rewritten from the Beej's tutorial with a little and insignificant changes
/* in the thread */
fd_set mainfd;
fd_set readfd;
// sin-size, newfd, maxfd - int
while(TRUE)
{
readfd = mainfd;
if(select(maxfd+1, &readfd, NULL, NULL, NULL) == -1)
{
MessageBoxA(NULL, "Error while trying to accept incoming connections (select)", "Error", 16);
itoa(GetLastError(), buf, 10);
MessageBoxA(NULL, buf, buf, 0);
break;
}
for(int i = 0; i <= maxfd; i++)
{
char* psr;
char srMsg[256];
if(FD_ISSET(i, &readfd))
{
if(i == mainSocket)
{
sin_size = sizeof(their_addr);
newfd = accept(mainSocket, (struct sockaddr*)&their_addr, &sin_size);
if(newfd == SOCKET_ERROR)
{
AddTextToEdit(hStaticChat, "* Error: couldn't accept incoming connection.", TRUE);
}
else
{
FD_SET(newfd, &mainfd);
if(newfd > maxfd)
{
maxfd = newfd;
}
}
}
else
{
len = recv(i, srMsg, 256, 0);
if(len == 0 || len == SOCKET_ERROR)
{
AddTextToEdit(hStaticChat, "* Client has disconnected", TRUE);
close(i);
FD_CLR(i, &mainfd);
}
else
{
AddTextToEdit(hStaticChat, srMsg, TRUE);
for(int j = 0; j <= maxfd; j++)
{
if(FD_ISSET(j, &readfd))
{
send(j, srMsg, len, 0);
}
}
}
}
}
}
}
You are only sending data to the clients whos fd is in readfd, that is, only to that one which just communicated to you. Try to test FD_ISSET(j, mainfd) instead.
This code is not valid under WinSock. Windows does not deal with sockets using integer file descriptors like other platforms do. Sockets are represented using actual kernel objects instead, so you can't use loop counters as socket handles and such. There is also API differences (closesocket() instead of close(), maxfd is ignored by select(), FD_XXX() expect SOCKET handles instead of int, etc).
On Windows, you need to use something more like this instead:
fd_set mainfd;
SOCKET newfd;
int sin_size;
...
while(TRUE)
{
fd_set readfd = mainfd;
if (select(0, &readfd, NULL, NULL, NULL) == SOCKET_ERROR)
{
itoa(WSAGetLastError(), buf, 10);
MessageBoxA(NULL, "Error while trying to accept incoming connections (select)", "Error", 16);
MessageBoxA(NULL, buf, buf, 0);
break;
}
for(int i = 0; i < readfd.fd_count; i++)
{
if (readfd.fd_array[i] == mainSocket)
{
sin_size = sizeof(their_addr);
newfd = accept(mainSocket, (struct sockaddr*)&their_addr, &sin_size);
if (newfd == INVALID_SOCKET)
{
AddTextToEdit(hStaticChat, "* Error: couldn't accept incoming connection.", TRUE);
}
else
{
// Note that fd_set can only hold FD_SETSIZE (64) sockets as a time!
FD_SET(newfd, &mainfd);
}
}
else
{
char srMsg[257];
len = recv(readfd.fd_array[i], srMsg, 256, 0);
if (len < 1)
{
if (len == 0)
AddTextToEdit(hStaticChat, "* Client has disconnected", TRUE);
else
AddTextToEdit(hStaticChat, "* Error: couldn't read from a client connection.", TRUE);
closesocket(readfd.fd_array[i]);
FD_CLR(readfd.fd_array[i], &mainfd);
}
else
{
srMsg[len] = 0;
AddTextToEdit(hStaticChat, srMsg, TRUE);
for (int j = 0; j < mainfd.fd_count; j++)
{
if (mainfd.fd_array[i] != mainSocket)
send(mainfd.fd_array[j], srMsg, len, 0);
}
}
}
}
}
I'm having some problems with my Winsock application.
As it currently stands, when my client first connects to the server, the welcome message is sent fine but thats when it screws up. After that initial message its like the program goes into turbo mode.
Each client has a vector that stores messages that need to be sent, to debug I have it output how many messages are left to send and heres what the latest one says:
Successfully sent message. 1 messages left.
Successfully sent message. 1574803 messages left
................... (Counts down)
Successfully sent message. 1574647 messages left
Client connection closed or broken
EDIT: Managed to place some output code in the right place, for some reason when it starts sending update messages to clients, it starts sending the same message over and over.
Heres some code, am only posting the cpp's, 'Network::Update' is run by a thread in 'Game'
-- Server --
Main.cpp
while(true)
{
m_Game -> Update();
Sleep(500);
}
Network.cpp
#include "Network.h"
Network::Network()
{
}
Network::~Network()
{
closesocket(m_Socket);
}
Network::Network(char* ServerIP, int ServerPort)
{
Initialise(ServerIP, ServerPort);
}
void Network::Initialise(char* ServerIP, int ServerPort)
{
//
// Initialise the winsock library to 2.2
//
WSADATA w;
int error = WSAStartup(0x0202, &w);
if ((error != 0) || (w.wVersion != 0x0202))
{
MessageBox(NULL, L"Winsock error. Shutting down.", L"Notification", MB_OK);
exit(1);
}
//
// Create the TCP socket
//
m_Socket = socket(AF_INET, SOCK_STREAM, 0);
if (m_Socket == SOCKET_ERROR)
{
MessageBox(NULL, L"Failed to create socket.", L"Notification", MB_OK);
exit(1);
}
//
// Fill out the address structure
//
m_Address.sin_family = AF_INET;
m_Address.sin_addr.s_addr = inet_addr(ServerIP);
m_Address.sin_port = htons(ServerPort);
//
// Bind the server socket to that address.
//
if (bind(m_Socket, (const sockaddr *) &m_Address, sizeof(m_Address)) != 0)
{
MessageBox(NULL, L"Failed to bind the socket to the address.", L"Notification", MB_OK);
exit(1);
}
//
// Make the socket listen for connections.
//
if (listen(m_Socket, 1) != 0)
{
MessageBox(NULL, L"Failed to listen on the socket.", L"Notification", MB_OK);
exit(1);
}
m_ID = 1;
}
void Network::Update()
{
//
// Structures for the sockets
//
fd_set readable, writeable;
FD_ZERO(&readable);
FD_ZERO(&writeable);
//
// Let the socket accept connections
//
FD_SET(m_Socket, &readable);
//
// Cycle through clients allowing them to read and write
//
for (std::list<Client *>::iterator it = m_Clients.begin(); it != m_Clients.end(); ++it)
{
Client *client = *it;
if (client->wantRead())
{
FD_SET(client->sock(), &readable);
}
if (client->wantWrite())
{
FD_SET(client->sock(), &writeable);
}
}
//
// Structure defining the connection time out
//
timeval timeout;
timeout.tv_sec = 2;
timeout.tv_usec = 500000;
//
// Check if a socket is readable
//
int count = select(0, &readable, &writeable, NULL, &timeout);
if (count == SOCKET_ERROR)
{
ExitProgram(L"Unable to check if the socket can be read.\nREASON: Select failed");
}
//
// Check if a theres an incoming connection
//
if (FD_ISSET(m_Socket, &readable))
{
//
// Accept the incoming connection
//
sockaddr_in clientAddr;
int addrSize = sizeof(clientAddr);
SOCKET clientSocket = accept(m_Socket, (sockaddr *) &clientAddr, &addrSize);
if (clientSocket > 0)
{
// Create a new Client object, and add it to the collection.
Client *client = new Client(clientSocket);
m_Clients.push_back(client);
// Send the new client a welcome message.
NetworkMessage message;
message.m_Type = MT_Welcome;
client->sendMessage(message);
m_ID++;
}
}
//
// Loop through clients
//
for (std::list<Client *>::iterator it = m_Clients.begin(); it != m_Clients.end(); ) // note no ++it here
{
Client *client = *it;
bool dead = false;
//
// Read data
//
if (FD_ISSET(client->sock(), &readable))
{
dead = client->doRead();
}
//
// Write data
//
if (FD_ISSET(client->sock(), &writeable))
{
dead = client->doWrite();
}
//
// Check if the client is dead (Was unable to write/read packets)
//
if (dead)
{
delete client;
it = m_Clients.erase(it);
}
else
{
++it;
}
}
}
void Network::sendMessage(NetworkMessage message)
{
//Loop through clients and send them the message
for (std::list<Client *>::iterator it = m_Clients.begin(); it != m_Clients.end(); ) // note no ++it here
{
Client *client = *it;
client->sendMessage(message);
}
}
Client.cpp
#include "Client.h"
Client::Client(SOCKET sock)
{
m_Socket = sock;
send_count_ = 0;
}
// Destructor.
Client::~Client()
{
closesocket(m_Socket);
}
// Process an incoming message.
void Client::processMessage(NetworkMessage message)
{
// PROCESSING NEEDS TO GO HERE
}
// Return the client's socket.
SOCKET Client::sock()
{
return m_Socket;
}
// Return whether this connection is in a state where we want to try
// reading from the socket.
bool Client::wantRead()
{
// At present, we always do.
return true;
}
// Return whether this connection is in a state where we want to try-
// writing to the socket.
bool Client::wantWrite()
{
// Only if we've got data to send.
//return send_count_ > 0;
return Messages.size() > 0;
}
// Call this when the socket is ready to read.
// Returns true if the socket should be closed.
bool Client::doRead()
{
return false;
}
// Call this when the socket is ready to write.
// Returns true if the socket should be closed.
bool Client::doWrite()
{
/*int count = send(m_Socket, send_buf_, send_count_, 0);
if (count <= 0)
{
printf("Client connection closed or broken\n");
return true;
}
send_count_ -= count;
// Remove the sent data from the start of the buffer.
memmove(send_buf_, &send_buf_[count], send_count_);
return false;*/
int count = send(m_Socket, (char*)&Messages[0], sizeof(NetworkMessage), 0);
if( count <= 0 )
{
printf("Client connection closed or broken\n");
return true;
}
Messages.pop_back();
printf(" Successfully sent message. %d messages left.\n", Messages.size() );
return false;
}
void Client::sendMessage(NetworkMessage message)
{
/*if (send_count_ + sizeof(NetworkMessage) > sizeof(send_buf_))
{
ExitProgram(L"Unable to send message.\nREASON: send_buf_ full");
}
else
{
memcpy(&send_buf_[send_count_], message, sizeof(NetworkMessage));
send_count_ += sizeof(NetworkMessage);
printf(" Size of send_count_ : %d \n", send_count_);
}*/
Messages.push_back(message);
}
Game.cpp
void Game::Update()
{
tempPlayer -> ChangePosition(1, 1); //Increase X and Y pos by 1
Dispatch();
printf("Update\n");
}
void Game::Dispatch()
{
NetworkMessage temp;
temp.m_Type = MT_Update;
temp.m_ID = tempPlayer->getID();
temp.m_positionX = tempPlayer->getX();
temp.m_positionY = tempPlayer->getY();
m_Network->sendMessage(temp);
}
There are a few problems with your code.
The main problem is that Network::sendMessage() runs an infinite loop. Your it iterator is never incremented (there is a comment saying as much - the comment is doing the wrong thing!), so the loop sends the same NetworkMessage to the same Client over and over without ever stopping. Do this instead:
void Network::sendMessage(NetworkMessage message)
{
//Loop through clients and send them the message
for (std::list<Client *>::iterator it = m_Clients.begin(); it != m_Clients.end(); ++it)
{
...
}
}
Client::doWrite() is using pop_back() when it should be using pop_front() instead. If there are multiple pending messages in the vector, you will lose messages. Rather than using the [] operator to pass a NetworkMessage directly to send(), you should pop_front() the first pending message and then send() it:
bool Client::doWrite()
{
NetworkMessage message = Messages.pop_front();
int count = send(m_Socket, (char*)&message, sizeof(NetworkMessage), 0);
if( count <= 0 )
{
printf("Client connection closed or broken\n");
return true;
}
printf(" Successfully sent message. %d messages left.\n", Messages.size() );
return false;
}
Your dead check in Network::Update() has a small logic hole in it that can prevent you from deleting dead clients correctly if doRead() returns true and then doWrite() return false. Do this instead:
bool dead = false;
//
// Read data
//
if (FD_ISSET(client->sock(), &readable))
{
if (client->doRead())
dead = true;
}
//
// Write data
//
if (FD_ISSET(client->sock(), &writeable))
{
if (client->doWrite())
dead = true;
}