I really don't understand what is going wrong here, so I hope somebody may spot something I missed.
I'm writing a user daemon which is accepting a client that I develop in java. For now this client only connects and sends the username password. I developed the code under cygwin and there it works. The daemon sends its introduction, then the client sends the username and password and the daemon responds either with disconnecting the client or sending OK (not yet done).
When I test this using cygwin it works on the localhost. I ported the code to HPUX and the client can connect and also receives the introduction from the daemon. Now when the client sends it's username and password it doesn't work anymore. The daemon only receives one byte and when it tries to read again I get -1 as result with EAGAIN and nothing else. The client doesn't show any error and also on the daemon side there is none. When I step through the code with gdb the messages are revceived completely. :(
The code that I use is this, if more info is needed I can add it:
int TCPSocket::receive(SocketMessage &oMessage, int nBytes)
{
int max = getSendBufferSize();
if(nBytes != -1 && nBytes < max)
max = nBytes;
SocketMessage sb(max);
int rcv_bytes = 0;
int total = 0;
int error = 0;
while(1)
{
rcv_bytes = ::recv(getSocketId(), &sb[0], sb.size(), 0);
error = errno;
FILE_LOG(logDEBUG4) << "Received on socket: " << mSocketId << " bytes: " << rcv_bytes << " expected:" << sb.size() << " total: " << total << " errno: " << error;
if(rcv_bytes == -1)
{
if(error == EAGAIN || error == EWOULDBLOCK)
return total;
throw SocketException(this, SocketException::RECEIVE, error, "Socket", "Client connection error!", __FILE__, __LINE__);
}
//if(rcv_bytes == 0)
// throw SocketException(this, SocketException::RECEIVE, error, "Socket", "Client connection has been closed!");
total += rcv_bytes;
oMessage.insert(oMessage.end(), sb.begin(), sb.begin()+rcv_bytes);
}
return total;
}
The output of the log is this:
16:16:04.391 DEBUG4: Received on socket: 4 bytes: 1 expected:32768 total: 0 errno: 2
16:16:04.391 DEBUG4: Received on socket: 4 bytes: -1 expected:32768 total: 1 errno: 11
So where is the rest of the 30 bytes and why is it not returned?
UPDATE
This is only the part of the code that actually receives the data. The socket class itself only deals with the raw socket without any protocoll. The protocol is implemented in a separate class. This receive function is supposed to grab as many bytes as there are available on the network and put it in a buffer (SocketMessage). It doesn't matter if the number of bytes are multiple messages or only part of a single message, because the controlling class will construct the actual messageblock out of the (maybe) partial messagestream. So that the first call only receives one byte is not the problem, because if the message is not complete the caller waits in a loop until more data arrives and a message becomes completed. since there can be more than one client I'm using non blocking sockets. I didn't want to deal with separate threads, so my server is multiplexing the connections.
The problem here is that the receive only receives one byte while I know that there should be more. The errorcode EAGAIN is handled and when the receive is entered next, it should get more bytes. Even if the network only transmitted only one byte, the rest of the message should still arrive next, but it doesn't. The select which waits on the socket to receive data blocks as if there is nothing there. When I run the same code in dbg and step through it works.
When I connect again with the same client then suddenly more bytes are revceived.
When I use the same code with cygwin using localhost it works fine.
UPDATE
Here is the complete code.
Main.cpp
mServerSocket = new TCPSocket(getListeningPort());
mServerSocket->bindSocket();
mServerSocket->setSocketBlocking(false);
mServerSocket->listenToClient(0);
setupSignals();
while(isSIGTERM() == false)
{
try
{
max = prepareListening();
//memset(&ts, 0, sizeof(struct timespec));
pret = pselect(max+1, &mReaders, &mWriters, &mExceptions, NULL, &mSignalMask);
error_code = errno;
if(pret == 0)
{
// Timeout occured, but we are not interested in that for now.
// Currently this shouldn't happen anyway.
continue;
}
processRequest(pret, error_code);
}
catch (SocketException &excp)
{
removeClientConnection(findClientConnection(excp.getTCPSocket()));
}
} // while sigTERM
BaseSocket.cpp:
#ifdef UNIX
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#endif
#include "support/exceptions/socket_exception.h"
#include "support/logging/simple_log.h"
#include "support/network/base_socket.h"
using namespace std;
BaseSocket::BaseSocket(void)
{
mSocketId = -1;
mSendBufferSize = MAX_SEND_LEN;
}
BaseSocket::BaseSocket(int pNumber)
{
mSocketId = -1;
mPortNumber = pNumber;
mBlocking = 1;
mSendBufferSize = MAX_SEND_LEN;
try
{
if ((mSocketId = ::socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
#ifdef UNIX
throw SocketException(this, SocketException::CONSTRUCTOR, errno, "Socket", "unix: error in socket constructor", __FILE__, __LINE__);
#endif
}
}
catch (SocketException& excp)
{
excp.response();
}
/*
set the initial address of client that shall be communicated with to
any address as long as they are using the same port number.
The clientAddr structure is used in the future for storing the actual
address of client applications with which communication is going
to start
*/
mClientAddr.sin_family = AF_INET;
mClientAddr.sin_addr.s_addr = htonl(INADDR_ANY);
mClientAddr.sin_port = htons(mPortNumber);
updateSendBufferSize(MAX_SEND_LEN);
}
void BaseSocket::updateSendBufferSize(int nNewSize)
{
mSendBufferSize = getSendBufferSize();
if(mSendBufferSize > nNewSize)
mSendBufferSize = nNewSize;
}
BaseSocket::~BaseSocket(void)
{
close();
}
void BaseSocket::setSocketId(int socketFd)
{
mSocketId = socketFd;
}
int BaseSocket::getSocketId()
{
return mSocketId;
}
// returns the port number
int BaseSocket::getPortNumber()
{
return mPortNumber;
}
void BaseSocket::setDebug(int debugToggle)
{
try
{
if (setsockopt(mSocketId, SOL_SOCKET, SO_DEBUG, (char *) &debugToggle, sizeof(debugToggle)) == -1)
{
#ifdef UNIX
throw SocketException(this, SocketException::OPTION, errno, "Socket", "unix: error set debug", __FILE__, __LINE__);
#endif
}
}
catch (SocketException& excp)
{
excp.response();
}
}
void BaseSocket::setReuseAddr(int reuseToggle)
{
try
{
if (setsockopt(mSocketId, SOL_SOCKET, SO_REUSEADDR, (char *) &reuseToggle,
sizeof(reuseToggle)) == -1)
{
#ifdef UNIX
throw SocketException(this, SocketException::OPTION, errno, "Socket", "unix: error set reuse address", __FILE__, __LINE__);
#endif
}
}
catch (SocketException& excp)
{
excp.response();
}
}
void BaseSocket::setKeepAlive(int aliveToggle)
{
try
{
if (setsockopt(mSocketId, SOL_SOCKET, SO_KEEPALIVE, (char *) &aliveToggle,
sizeof(aliveToggle)) == -1)
{
#ifdef UNIX
throw SocketException(this, SocketException::OPTION, errno, "Socket", "unix: error set keep alive", __FILE__, __LINE__);
#endif
}
}
catch (SocketException& excp)
{
excp.response();
}
}
void BaseSocket::setLingerSeconds(int seconds)
{
struct linger lingerOption;
if (seconds > 0)
{
lingerOption.l_linger = seconds;
lingerOption.l_onoff = 1;
}
else
lingerOption.l_onoff = 0;
try
{
if (setsockopt(mSocketId, SOL_SOCKET, SO_LINGER, (char *) &lingerOption,
sizeof(struct linger)) == -1)
{
#ifdef UNIX
throw SocketException(this, SocketException::OPTION, errno, "Socket", "unix: error set linger seconds", __FILE__, __LINE__);
#endif
}
}
catch (SocketException& excp)
{
excp.response();
}
}
void BaseSocket::setLingerOnOff(bool lingerOn)
{
struct linger lingerOption;
if (lingerOn)
lingerOption.l_onoff = 1;
else
lingerOption.l_onoff = 0;
try
{
if (setsockopt(mSocketId, SOL_SOCKET, SO_LINGER, (char *) &lingerOption,
sizeof(struct linger)) == -1)
{
#ifdef UNIX
throw SocketException(this, SocketException::OPTION, errno, "Socket", "unix: error set linger on/off", __FILE__, __LINE__);
#endif
}
}
catch (SocketException& excp)
{
excp.response();
}
}
void BaseSocket::setSendBufferSize(int sendBufSize)
{
if (setsockopt(mSocketId, SOL_SOCKET, SO_SNDBUF, (char *) &sendBufSize, sizeof(sendBufSize)) == -1)
{
#ifdef UNIX
throw SocketException(this, SocketException::OPTION, errno, "Socket", "unix: error send buffer size", __FILE__, __LINE__);
#endif
}
updateSendBufferSize(sendBufSize);
}
void BaseSocket::setReceiveBufferSize(int receiveBufSize)
{
if (setsockopt(mSocketId, SOL_SOCKET, SO_RCVBUF, (char *) &receiveBufSize, sizeof(receiveBufSize)) == -1)
{
#ifdef UNIX
throw SocketException(this, SocketException::OPTION, errno, "Socket", "unix: error set receive buffer size", __FILE__, __LINE__);
#endif
}
}
int BaseSocket::isSocketBlocking()
{
return mBlocking;
}
void BaseSocket::setSocketBlocking(int blockingToggle)
{
if (blockingToggle)
{
if (isSocketBlocking())
return;
else
mBlocking = 1;
}
else
{
if (!isSocketBlocking())
return;
else
mBlocking = 0;
}
try
{
#ifdef UNIX
int flags;
if (-1 == (flags = fcntl(mSocketId, F_GETFL, 0)))
flags = 0;
if(mBlocking)
fcntl(mSocketId, F_SETFL, flags & (~O_NONBLOCK));
else
fcntl(mSocketId, F_SETFL, flags | O_NONBLOCK);
/*if (ioctl(socketId, FIONBIO, (char *) &blocking) == -1)
{
throw SocketException(this, SocketException::OPTION, errno, "Socket", "unix: error set socke blocking");
}*/
#endif
}
catch (SocketException& excp)
{
excp.response();
}
}
int BaseSocket::getDebug()
{
int myOption;
int myOptionLen = sizeof(myOption);
try
{
if (getsockopt(mSocketId, SOL_SOCKET, SO_DEBUG, (void *) &myOption, &myOptionLen) == -1)
{
#ifdef UNIX
throw SocketException(this, SocketException::OPTION, errno, "Socket", "unix: error get debug", __FILE__, __LINE__);
#endif
}
}
catch (SocketException& excp)
{
excp.response();
return -1;
}
return myOption;
}
int BaseSocket::getReuseAddr()
{
int myOption;
int myOptionLen = sizeof(myOption);
try
{
if (getsockopt(mSocketId, SOL_SOCKET, SO_REUSEADDR, (void *) &myOption, &myOptionLen) == -1)
{
#ifdef UNIX
throw SocketException(this, SocketException::OPTION, errno, "Socket", "unix: error get reuse address", __FILE__, __LINE__);
#endif
}
}
catch (SocketException& excp)
{
excp.response();
return -1;
}
return myOption;
}
int BaseSocket::getKeepAlive()
{
int myOption;
int myOptionLen = sizeof(myOption);
try
{
if (getsockopt(mSocketId, SOL_SOCKET, SO_KEEPALIVE, (void *) &myOption, &myOptionLen) == -1)
{
#ifdef UNIX
throw SocketException(this, SocketException::OPTION, errno, "Socket", "unix: error get keep alive", __FILE__, __LINE__);
#endif
}
}
catch (SocketException& excp)
{
excp.response();
return -1;
}
return myOption;
}
int BaseSocket::getLingerSeconds()
{
struct linger lingerOption;
int myOptionLen = sizeof(struct linger);
try
{
if (getsockopt(mSocketId, SOL_SOCKET, SO_LINGER, (void *) &lingerOption, &myOptionLen) == -1)
{
#ifdef UNIX
throw SocketException(this, SocketException::OPTION, errno, "Socket", "unix: error get linger seconds", __FILE__, __LINE__);
#endif
}
}
catch (SocketException& excp)
{
excp.response();
return -1;
}
return lingerOption.l_linger;
}
bool BaseSocket::getLingerOnOff()
{
struct linger lingerOption;
int myOptionLen = sizeof(struct linger);
try
{
if (getsockopt(mSocketId, SOL_SOCKET, SO_LINGER, (void *) &lingerOption, &myOptionLen) == -1)
{
#ifdef UNIX
throw SocketException(this, SocketException::OPTION, errno, "Socket", "unix: error get linger on/off", __FILE__, __LINE__);
#endif
}
}
catch (SocketException& excp)
{
excp.response();
}
if (lingerOption.l_onoff == 1)
return true;
else
return false;
}
int BaseSocket::getSendBufferSize()
{
int sendBuf;
int myOptionLen = sizeof(sendBuf);
try
{
if (getsockopt(mSocketId, SOL_SOCKET, SO_SNDBUF, (void *)&sendBuf, &myOptionLen) == -1)
{
#ifdef UNIX
throw SocketException(this, SocketException::OPTION, errno, "Socket", "unix: error get send buffer size", __FILE__, __LINE__);
#endif
}
}
catch (SocketException& excp)
{
excp.response();
return -1;
}
return sendBuf;
}
int BaseSocket::getReceiveBufferSize()
{
int rcvBuf;
int myOptionLen = sizeof(rcvBuf);
try
{
if (getsockopt(mSocketId, SOL_SOCKET, SO_RCVBUF, (void *) &rcvBuf, &myOptionLen) == -1)
{
#ifdef UNIX
throw SocketException(this, SocketException::OPTION, errno, "Socket", "unix: error get receive buffer size", __FILE__, __LINE__);
#endif
}
}
catch (SocketException& excp)
{
excp.response();
return -1;
}
return rcvBuf;
}
ostream &operator<<(ostream& io, BaseSocket& s)
{
string flagStr = "";
io << endl;
io << "Summary of socket settings:" << endl;
io << " Socket Id: " << s.getSocketId() << endl;
io << " port #: " << s.getPortNumber() << endl;
io << " debug: " << (flagStr = s.getDebug() ? "true" : "false")
<< endl;
io << " reuse addr: " << (flagStr = s.getReuseAddr() ? "true" : "false")
<< endl;
io << " keep alive: " << (flagStr = s.getKeepAlive() ? "true" : "false")
<< endl;
io << " send buf size: " << s.getSendBufferSize() << endl;
io << " recv bug size: " << s.getReceiveBufferSize() << endl;
io << " blocking: "
<< (flagStr = s.isSocketBlocking() ? "true" : "false") << endl;
io << " linger on: "
<< (flagStr = s.getLingerOnOff() ? "true" : "false") << endl;
io << " linger seconds: " << s.getLingerSeconds() << endl;
io << endl;
return io;
}
void BaseSocket::close(void)
{
::close(mSocketId);
}
TCPSocket.cpp:
#ifdef UNIX
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#endif
#include <sstream>
#include "support/logging/log.h"
#include "support/exceptions/socket_exception.h"
#include "support/logging/simple_log.h"
#include "support/network/tcp_socket.h"
using namespace std;
const int MSG_HEADER_LEN = 6;
TCPSocket::TCPSocket()
: BaseSocket()
{
}
TCPSocket::TCPSocket(int portId)
: BaseSocket(portId)
{
}
TCPSocket::~TCPSocket()
{
}
void TCPSocket::initialize()
{
}
void TCPSocket::bindSocket()
{
try
{
if (bind(mSocketId, (struct sockaddr *) &mClientAddr, sizeof(struct sockaddr_in)) == -1)
{
#ifdef UNIX
throw SocketException(this, SocketException::BIND, 0, "Socket", "unix: error calling bind()", __FILE__, __LINE__);
#endif
}
}
catch (SocketException& excp)
{
excp.response();
}
}
void TCPSocket::connectToServer(string& serverNameOrAddr, hostType hType)
{
/*
when this method is called, a client socket has been built already,
so we have the socketId and portNumber ready.
a HostInfo instance is created, no matter how the server's name is
given (such as www.yuchen.net) or the server's address is given (such
as 169.56.32.35), we can use this HostInfo instance to get the
IP address of the server
*/
HostInfo serverInfo(serverNameOrAddr, hType);
// Store the IP address and socket port number
struct sockaddr_in serverAddress;
serverAddress.sin_family = AF_INET;
serverAddress.sin_addr.s_addr = inet_addr(
serverInfo.getHostIPAddress().c_str());
serverAddress.sin_port = htons(mPortNumber);
// Connect to the given address
try
{
if (connect(mSocketId, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) == -1)
{
#ifdef UNIX
throw SocketException(this, SocketException::CONNECT, 0, "Socket", "unix: error calling connect()", __FILE__, __LINE__);
#endif
}
}
catch (SocketException& excp)
{
excp.response();
}
}
TCPSocket *TCPSocket::acceptClient(string& clientHost)
{
int newSocket; // the new socket file descriptor returned by the accept system call
// the length of the client's address
int clientAddressLen = sizeof(struct sockaddr_in);
struct sockaddr_in clientAddress; // Address of the client that sent data
// Accepts a new client connection and stores its socket file descriptor
try
{
if ((newSocket = accept(mSocketId, (struct sockaddr *) &clientAddress, &clientAddressLen)) == -1)
{
#ifdef UNIX
throw SocketException(this, SocketException::ACCEPT, 0, "Socket", "unix: error calling accept()", __FILE__, __LINE__);
#endif
}
}
catch (SocketException& excp)
{
excp.response();
return NULL;
}
// Get the host name given the address
char *sAddress = inet_ntoa((struct in_addr) clientAddress.sin_addr);
HostInfo clientInfo(sAddress, ADDRESS);
clientHost += clientInfo.getHostName();
// Create and return the new TCPSocket object
TCPSocket* retSocket = new TCPSocket();
retSocket->setSocketId(newSocket);
return retSocket;
}
void TCPSocket::listenToClient(int totalNumPorts)
{
try
{
if (listen(mSocketId, totalNumPorts) == -1)
{
#ifdef UNIX
throw SocketException(this, SocketException::LISTEN, 0, "Socket", "unix: error calling listen()", __FILE__, __LINE__);
#endif
}
}
catch (SocketException& excp)
{
excp.response();
}
}
ostream &operator<<(ostream &oStream, const TCPSocket &oSocket)
{
oStream << oSocket.mSocketId;
return oStream;
}
int TCPSocket::send(SocketMessage const &oBuffer, int nSize)
{
int numBytes; // the number of bytes sent
int error = errno;
if(nSize == -1)
nSize = oBuffer.size();
if((unsigned int)nSize > oBuffer.size())
{
std::stringstream ss;
ss << "Invalid Buffersize! Requested: " << (unsigned int)nSize << " Provided: " << oBuffer.size();
std::string s;
ss >> s;
FILE_LOG(logERROR) << s;
throw SocketException(this, SocketException::SEND, 0, "Socket", s, __FILE__, __LINE__);
}
// Sends the message to the connected host
try
{
FILE_LOG(logDEBUG4) << "Sending on socket: "<< mSocketId << " bytes:" << nSize;
numBytes = ::send(mSocketId, &oBuffer[0], nSize, 0);
error = errno;
FILE_LOG(logDEBUG4) << "Sent on socket: "<< mSocketId << " bytes:" << nSize << " errno: " << error;
if(numBytes == -1)
{
#ifdef UNIX
if(error == EAGAIN || error == EWOULDBLOCK)
{
return -1;
}
else
throw SocketException(this, SocketException::SEND, error, "Socket", "unix: error calling send()", __FILE__, __LINE__);
#endif
}
}
catch (SocketException& excp)
{
excp.response();
}
return numBytes;
}
int TCPSocket::receive(SocketMessage &oMessage, int nBytes)
{
int max = getSendBufferSize();
if(nBytes != -1 && nBytes < max)
max = nBytes;
SocketMessage sb(max);
int rcv_bytes = 0;
int total = 0;
int error = 0;
while(1)
{
rcv_bytes = ::recv(getSocketId(), &sb[0], sb.size(), 0);
error = errno;
FILE_LOG(logDEBUG4) << "Received on socket: " << getSocketId() << " bytes: " << rcv_bytes << " expected:" << sb.size() << " total: " << total << " errno: " << error;
if(rcv_bytes == -1)
{
if(error == EAGAIN || error == EWOULDBLOCK)
return total;
throw SocketException(this, SocketException::RECEIVE, error, "Socket", "Client connection error!", __FILE__, __LINE__);
}
// Socket has been closed.
if(rcv_bytes == 0)
return total;
total += rcv_bytes;
oMessage.insert(oMessage.end(), sb.begin(), sb.begin()+rcv_bytes);
}
return total;
}
void TCPSocket::close(void)
{
BaseSocket::close();
}
Are you sure the Nagle Algorithm isn't kicking in here? If you haven't disabled it by setting the TCP_NODELAY socket option your data may not be sent until a certain amount of data (MSS) is available.
A couple of questions first:
- Why are you using non-blocking I/O?
- You apparently know the message should be 30 bytes long, so why are you asking for 32768 bytes?
There's a lot more to sockets than just calling recv if you are using non-blocking I/O. With blocking I/O, every error is a true error. With non-blocking I/O you have to deal with that pesky EAGAIN / EWOULDBLOCK error. Recovery is possible, but you are responsible for that recovery when you configure the device to use non-blocking I/O.
As the first name (EAGAIN) suggests, getting this error result means you need to try again, preferably after waiting a bit. A simple but not very good way to wait is to sleep (or usleep or nanosleep) for some amount of time. The problem with this is that you might have waited too long, or not long enough. Wait too long and your system might become non responsive or the sender might go away. Wait too little and you are making the computer thrash between privileged and non-privileged mode.
The best way to wait for such an event is to use an event-based scheme, but unfortunately those schemes aren't portable. A portable scheme is to use select or poll. You can make select or poll wait indefinitely or you can specify a timeout. I find poll a lot easier to use, particularly when there's only one file descriptor involved. That's personal preference however. Others find select easier to use.
The number of bytes returned by any one call to recv is unpredictable. Many messages are received in several parts, so it is necessary to call recv again if you don't yet have the entire message. But your code does not seem to have a way to determine if the whole messaage has been received. And, your code returns on EWOULDBLOCK, but EWOULDBLOCK is a normal part of socket operations. It does not signify an error or the completion of a message.
Related
I want to send random trigger signals (A and B) from Matlab to a C++ Code. The point where I stuck now is, that whenever I am not sending this trigger signal/message, the C++ Code keeps waiting for it and doesn't continue its process.
How can I make the C++ Code keep running (to collect data) without waiting for the next trigger message. Because now only once it receives the message (UDP transfers trigger signal) it gives me the specific outcome.
----------- BEGIN MATLAB CODE ---------------------
send_trigger_signal = instrfind('Type', 'udp', 'LocalHost', '127.0.0.1','RemoteHost', '192.168.0.100', 'RemotePort', 8888, 'LocalPort', 8844, 'Tag', '');
% Create the udp object if it does not exist otherwise use the object that was found.
if isempty(send_trigger_signal)
send_trigger_signal = udp('127.0.0.1', 'RemotePort', 8888, 'LocalPort', 8844);
else
fclose(send_trigger_signal);
send_trigger_signal = send_trigger_signal(1);
end
send_trigger_signal.DatagramTerminateMode='off';
send_trigger_signal.Timeout=0.0001;
send_trigger_signal.Timerperiod=0.01;
%send_trigger_signal.
% Connect to instrument object, send_trigger_signal.
fopen(send_trigger_signal);
% Communicating with instrument object, send_trigger_signal.
on_trigger_command=typecast(swapbytes(uint16([1 1 0 0])),'uint8'); %trigger on
off_trigger_command=typecast(swapbytes(uint16([0 0 0 0])),'uint8'); %trigger off
while(true)
for i=1:1
fprintf(send_trigger_signal, 'A');
WaitSecs(5);
end
end
fclose(send_trigger_signal);
send_trigger_signal=instrfindall;
delete(send_trigger_signal);
instrfindall;
----------- END MATLAB CODE ---------------------
This is the C++ code which should receive the random trigger signals from Matlab (A and B), while collecting gyro data between those signals.
To test it here the message is send every 5sec. The problem is that I cannot collect the gyro data in within those 5sec. The UDP communication is interrupting the data collection - because it is waiting those 5sec.
----------- START C++ CODE ---------------------
#include <iostream>
#include <winsock2.h>
using namespace std;
#pragma comment(lib,"ws2_32.lib") // Winsock Library
#pragma warning(disable:4996)
#define BUFLEN 512
#define PORT 8888
int receiver(void)
{
int value = 5;
system("title UDP Server");
sockaddr_in server, client;
// initialise winsock
WSADATA wsa;
printf("Initialising Winsock...");
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
{
printf("Failed. Error Code: %d", WSAGetLastError());
exit(0);
}
printf("Initialised.\n");
// create a socket
SOCKET server_socket;
if ((server_socket = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET)
{
printf("Could not create socket: %d", WSAGetLastError());
}
printf("Socket created.\n");
// prepare the sockaddr_in structure
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(PORT);
// bind
if (bind(server_socket, (sockaddr*)&server, sizeof(server)) == SOCKET_ERROR)
{
printf("Bind failed with error code: %d", WSAGetLastError());
exit(EXIT_FAILURE);
}
puts("Bind done.");
while (true)
{
printf("Waiting for data...");
fflush(stdout);
char message[BUFLEN] = {};
// try to receive some data, this is a blocking call
int message_len;
int slen = sizeof(sockaddr_in);
if (message_len = recvfrom(server_socket, message, BUFLEN, 0, (sockaddr*)&client, &slen) == SOCKET_ERROR)
{
printf(message);
printf("recvfrom() failed with error code: %d", WSAGetLastError());
exit(0);
}
if (message[0] == 'A')
{
value = 6;
break;
}
if (message[0] == 'B')
{
value = 7;
break;
}
// print details of the client/peer and the data received
printf("Received packet from %s:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
printf("Data: %s\n", message);
return 0;
}
closesocket(server_socket);
WSACleanup();
return value;
}
int main()
{
while (true)
{
// Reading some gyro data here
// Listening UDP
receiver();
}
return 0;
}
----------- END C++ CODE ---------------------
With a few structural tweaks:
Using non-blocking socket.
You don't want to restart winsock and rebind the socket every time you read from it, so that's spun off to different functions (an RAII wrapper class in the case of winsock).
C-style IO replaced with C++ IO.
exit(0) means the program succeeded, but was used in many cases where failure occurred. Consistently using exit(EXIT_FAILURE);. Might be worth throwing an exception, but it's annoying to get the error code into the exception text.
Removed some of the output because it would be spammed out now that the receive function can immediately return .
Your program could look something like this:
#include <iostream>
#include <winsock2.h>
using namespace std;
#pragma comment(lib,"ws2_32.lib") // Winsock Library
#pragma warning(disable:4996)
// using modern C++ constants
constexpr int BUFLEN = 512;
constexpr int PORT = 8888;
//RAII wrapper to make sure winsock is created and disposed of responsibly
struct winsock_RAII
{
winsock_RAII()
{
WSADATA wsa;
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
{
std::cerr << "Failed to initialize winsock. Error Code: " << WSAGetLastError() << '\n';
exit(EXIT_FAILURE);
}
}
~winsock_RAII()
{
WSACleanup(); // what are we gonna do if it fails? Not much we can do.
}
};
//socket initialization
SOCKET init_sock()
{
SOCKET server_socket;
if ((server_socket = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET)
{
std::cerr << "Failed to get socket. Error Code: " << WSAGetLastError() << '\n';
exit(EXIT_FAILURE);
}
u_long iMode = 1;
//setr socket non-blocking
if (ioctlsocket(server_socket, FIONBIO, &iMode) != NO_ERROR)
{
std::cerr << "Failed to get socket. Error Code: " << WSAGetLastError() << '\n';
exit(EXIT_FAILURE);
}
// prepare the sockaddr_in structure
sockaddr_in server;
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(PORT);
// bind
if (bind(server_socket, (sockaddr*) &server, sizeof(server)) == SOCKET_ERROR)
{
std::cerr << "Bind failed. Error Code: " << WSAGetLastError() << '\n';
exit(EXIT_FAILURE);
}
return server_socket;
}
// read from socket
int receiver(SOCKET server_socket)
{
// try to receive some data, this is a non-blocking call
int slen = sizeof(sockaddr_in);
sockaddr_in client;
char message[BUFLEN + 1]; // no need to clear the whole buffer. We'll know
// exactly where to put the null thanks to message_len
// +1 makes sure we have room for terminator
int message_len = recvfrom(server_socket, message,
BUFLEN,
0,
(sockaddr*) &client,
&slen);
int value = 5;
if (message_len != SOCKET_ERROR)
{
message[message_len] = '\0'; // place terrminator
if (message[0] == 'A')
{
value = 6;
}
if (message[0] == 'B')
{
value = 7;
}
// print details of the client/peer and the data received
std::cout << "Received packet from " << inet_ntoa(client.sin_addr) << ':' << ntohs(client.sin_port) << '\n'
<< "Data: " << message << '\n';
}
else if (WSAGetLastError() != WSAEWOULDBLOCK)
{
// printf(message); no point to printing message. There isn't one
std::cerr << "recvfrom() failed . Error Code: " << WSAGetLastError() << '\n';
exit(EXIT_FAILURE);
}
return value;
}
int main()
{
winsock_RAII winsock; // scoped winsock initializer
SOCKET server_socket = init_sock();
while (true)
{
// Reading some gyro data here
receiver(server_socket);
}
closesocket(server_socket);
return 0;
}
You might want to use select with a short timeout to throttle the loop because it can be a serious and unnecessary CPU-eater if the gyro reading code is also quick.
I have two pairs of simple TCP server/client, i.e., one client per server, running on Windows:
Servers run in a process (app).
Clients run in the other process.
Servers keep sending heartbeats (a string) to their paired client.
The first pair of server/client run their mainloops in their own threads.
Once the first server/client have shaken hands with the first heartbeat, the second pair of server/client start their mainloops in their own threads.
For this test, they run on the same machine with different ports: 2345 and 2346.
Now my problem
The first client receives its server's heartbeat.
The second client does NOT, although the second server sent out heartbeats without errors.
Here is the server code:
// hello_dualchannel_server.cpp : This file contains the 'main' function.
#include "pch.h"
#include <iostream>
#include <thread>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <spdlog/spdlog.h>
#define SPDLOG_WCHAR_TO_UTF8_SUPPORT
#ifdef _DEBUG
#if !defined(SPDLOG_ACTIVE_LEVEL)
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_TRACE
#endif // #if !defined(SPDLOG_ACTIVE_LEVEL)
#define SPDLOG_DEBUG_ON
#define SPDLOG_TRACE_ON
#define _trace SPDLOG_TRACE
#endif // #ifdef _DEBUG
using namespace spdlog;
SOCKET g_sockFirst = 0;
SOCKET g_sockClientFirst = 0;
std::thread g_threadFirst;
uint32_t g_timeLatestHeartBeatFirst = 0;
SOCKET g_sockSecond = 0;
SOCKET g_sockClientSecond = 0;
std::thread g_threadSecond;
uint32_t g_timeLatestHeartBeatSecond = 0;
void SetupLogger() {
#ifdef _DEBUG
spdlog::set_level(spdlog::level::trace);
spdlog::set_pattern("[%H:%M:%S%z][%^%L%$][%t:%s:%#] %v");
#else
spdlog::set_level(spdlog::level::info);
spdlog::set_pattern("[%H:%M:%S][%^%L%$][%t] %v");
#endif // #ifdef _DEBUG
}
int InitWinSock() {
WORD wVersionRequested;
WSADATA wsaData;
/* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */
wVersionRequested = MAKEWORD(2, 2);
int err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0) {
/* Tell the user that we could not find a usable */
/* Winsock DLL. */
printf("WSAStartup failed with error: %d\n", err);
return 1;
}
/* Confirm that the WinSock DLL supports 2.2.*/
/* Note that if the DLL supports versions greater */
/* than 2.2 in addition to 2.2, it will still return */
/* 2.2 in wVersion since that is the version we */
/* requested. */
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
/* Tell the user that we could not find a usable */
/* WinSock DLL. */
printf("Could not find a usable version of Winsock.dll\n");
WSACleanup();
return 1;
}
else
printf("The Winsock 2.2 dll was found okay\n");
return 0;
}
bool Init(int host_port, SOCKET* p_sockServer, SOCKET*p_sockClient) {
int err = 0;
int* p_int = 0;
std::string host_name("127.0.0.1");
struct sockaddr_in my_addr;
int addr_size = 0;
sockaddr_in sadr_client;
if (!*p_sockServer) {
*p_sockServer = socket(AF_INET, SOCK_STREAM, 0);
if (*p_sockServer == -1) {
char log[MAX_PATH];
strerror_s(log, MAX_PATH, errno);
error("Server Error initializing socket: {}", log);
goto FINISH;
}
p_int = (int*)malloc(sizeof(int));
*p_int = 1;
if ((setsockopt(*p_sockServer, SOL_SOCKET, SO_REUSEADDR, (char*)p_int, sizeof(int)) == -1)
|| (setsockopt(*p_sockServer, SOL_SOCKET, SO_KEEPALIVE, (char*)p_int, sizeof(int)) == -1)) {
char log[MAX_PATH];
strerror_s(log, MAX_PATH, errno);
error("Server Error setting options: {}", log);
free(p_int);
goto FINISH;
}
free(p_int);
info("Server socket is set up.");
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(host_port);
memset(&(my_addr.sin_zero), 0, 8);
my_addr.sin_addr.s_addr = INADDR_ANY;
if (bind(*p_sockServer, (sockaddr*)&my_addr, sizeof(my_addr)) == -1) {
char log[MAX_PATH];
strerror_s(log, MAX_PATH, errno);
error("Server Error binding to socket, make sure nothing else is listening on this port: {}", log);
goto FINISH;
}
if (listen(*p_sockServer, 10) == -1) {
char log[MAX_PATH];
strerror_s(log, MAX_PATH, errno);
error("Server Error listening: {}", log);
goto FINISH;
}
info("SUCCESS: Server socket listening ...");
}
info("Server accepting connection ...");
addr_size = sizeof(sockaddr_in);
char sAddress[MAX_PATH];
*p_sockClient = accept(*p_sockServer, (sockaddr*)&sadr_client, &addr_size);
if (*p_sockClient == INVALID_SOCKET) {
char log[MAX_PATH];
strerror_s(log, MAX_PATH, errno);
error("Server error accepting client connection: {}", log);
// DO NOT close sockets here.
return false;
}
inet_ntop(sadr_client.sin_family, &sadr_client.sin_addr, sAddress, MAX_PATH);
g_timeLatestHeartBeatFirst = GetCurrentTime();
info("SUCCESS: Server accepted client connection.");
return true;
FINISH:
closesocket(*p_sockServer);
return false;
}
bool IsConnected(uint32_t timeLatestHeartBeat) {
// CAUTION: denser than client for sure catch
const unsigned long ConnTimeoutMs = 300;
auto cur = GetCurrentTime();
auto latest = timeLatestHeartBeat;
return cur - latest < ConnTimeoutMs;
}
bool StayInTouch(const char* name, SOCKET* pSockClient, uint32_t* pTimeLatestHeartBeat) {
if (IsConnected(*pTimeLatestHeartBeat))
return true;
char heartBeat[] = "biku";
int nBytesSent = 0;
int flags = 0;
int res = send(*pSockClient, heartBeat, sizeof(heartBeat), flags);
if (res == SOCKET_ERROR) {
char log[MAX_PATH];
strerror_s(log, MAX_PATH, errno);
error("{}: Server failed to send heartbeat: {}, Windows error: {}", name, log, GetLastError());
return false;
}
else if (res == 0) {
char log[MAX_PATH];
strerror_s(log, MAX_PATH, errno);
error("{}: Server sent zerobyte heartbeat: {}", name, log);
return false;
}
debug("{}: Heartbeat sent: {}", name, heartBeat);
*pTimeLatestHeartBeat = GetCurrentTime();
return true;
}
void Close(SOCKET* pSock) {
closesocket(*pSock);
*pSock = 0;
}
bool Connect() {
if (g_threadFirst.joinable()) {
warn("FirstTunnel already running. Skipped.");
return true;
}
g_threadFirst = std::thread([&]() {
bool isConnected = false;
while (true) {
while (!isConnected) {
isConnected = Init(2345, &g_sockFirst, &g_sockClientFirst);
}
isConnected = StayInTouch("FirstTunnel", &g_sockClientFirst, &g_timeLatestHeartBeatFirst);
if (!isConnected) {
// We don't close as client.
// We keep connecting
error("About to reconnect ...");
Sleep(1000);
continue;
}
if (!g_threadSecond.joinable()) {
g_threadSecond = std::thread([&]() {
while (true) {
while (!isConnected) {
isConnected = Init(2346, &g_sockSecond, &g_sockClientSecond);
}
isConnected = StayInTouch("SecondTunnel", &g_sockClientSecond, &g_timeLatestHeartBeatSecond);
if (!isConnected) {
// We don't close as client.
// We keep connecting
error("About to reconnect ...");
Sleep(1000);
continue;
}
}
info("SecondTunnel quitting...");
Close(&g_sockSecond);
});
}
}
info("FirstTunnel quitting...");
Close(&g_sockFirst);
});
while (true) {
//info("main thread ...");
Sleep(3000);
}
return g_threadFirst.joinable() ? true : false;
}
int main() {
SetupLogger();
info("Hello World!\n");
if (InitWinSock()) {
critical("Failed to initialize Window socket. Aborted.");
}
Connect();
if (g_threadSecond.joinable()) {
g_threadSecond.join();
}
if (g_threadFirst.joinable()) {
g_threadFirst.join();
}
WSACleanup();
info("Bye!");
}
Here is the client code
// hello_dualchannel_client.cpp : This file contains the 'main' function.
//
#include "pch.h"
#include <iostream>
#include <thread>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <spdlog/spdlog.h>
#define SPDLOG_WCHAR_TO_UTF8_SUPPORT
#ifdef _DEBUG
#if !defined(SPDLOG_ACTIVE_LEVEL)
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_TRACE
#endif // #if !defined(SPDLOG_ACTIVE_LEVEL)
#define SPDLOG_DEBUG_ON
#define SPDLOG_TRACE_ON
#define _trace SPDLOG_TRACE
#endif // #ifdef _DEBUG
using namespace spdlog;
SOCKET g_sockFirst = 0;
std::thread g_threadFirst;
uint32_t g_timeLatestHeartBeatFirst;
SOCKET g_sockSecond = 0;
std::thread g_threadSecond;
uint32_t g_timeLatestHeartBeatSecond;
void SetupLogger() {
#ifdef _DEBUG
spdlog::set_level(spdlog::level::trace);
spdlog::set_pattern("[%H:%M:%S%z][%^%L%$][%t:%s:%#] %v");
#else
spdlog::set_level(spdlog::level::info);
spdlog::set_pattern("[%H:%M:%S][%^%L%$][%t] %v");
#endif // #ifdef _DEBUG
}
int InitWinSock() {
WORD wVersionRequested;
WSADATA wsaData;
/* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */
wVersionRequested = MAKEWORD(2, 2);
int err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0) {
/* Tell the user that we could not find a usable */
/* Winsock DLL. */
printf("WSAStartup failed with error: %d\n", err);
return 1;
}
/* Confirm that the WinSock DLL supports 2.2.*/
/* Note that if the DLL supports versions greater */
/* than 2.2 in addition to 2.2, it will still return */
/* 2.2 in wVersion since that is the version we */
/* requested. */
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
/* Tell the user that we could not find a usable */
/* WinSock DLL. */
printf("Could not find a usable version of Winsock.dll\n");
WSACleanup();
return 1;
}
else
printf("The Winsock 2.2 dll was found okay\n");
return 0;
}
bool Init(int host_port, SOCKET* p_sock) {
int err = 0;
int* p_int = 0;
std::string host_name("127.0.0.1");
struct sockaddr_in my_addr;
char handshake[] = "hello";
//int nBytesSent;
if (!*p_sock) {
*p_sock = socket(AF_INET, SOCK_STREAM, 0);
if (*p_sock == -1) {
char log[MAX_PATH];
strerror_s(log, MAX_PATH, errno);
error("Client Error initializing socket {}", log);
goto FINISH;
}
p_int = (int*)malloc(sizeof(int));
*p_int = 1;
if ((setsockopt(*p_sock, SOL_SOCKET, SO_REUSEADDR, (char*)p_int, sizeof(int)) == -1)
|| (setsockopt(*p_sock, SOL_SOCKET, SO_KEEPALIVE, (char*)p_int, sizeof(int)) == -1)) {
char log[MAX_PATH];
strerror_s(log, MAX_PATH, errno);
error("Client Error setting options {}", log);
free(p_int);
goto FINISH;
}
free(p_int);
info("SUCCESS: Client socket is set up.");
}
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(host_port);
memset(&(my_addr.sin_zero), 0, 8);
inet_pton(my_addr.sin_family, host_name.c_str(), &my_addr.sin_addr);
if (connect(*p_sock, (struct sockaddr*)&my_addr, sizeof(my_addr)) == SOCKET_ERROR) {
char log[MAX_PATH];
strerror_s(log, MAX_PATH, errno);
error("Client Error connecting socket {}", log);
Sleep(1000);
goto FINISH;
}
/*nBytesSent = send(g_sockFirst, handshake, sizeof(handshake), 0);
if (nBytesSent <= 0) {
char log[MAX_PATH];
strerror_s(log, MAX_PATH, errno);
error("Client error sending handshake: {}", log);
goto FINISH;
}*/
g_timeLatestHeartBeatFirst = GetCurrentTime();
info("SUCCESS: Client connected to server.");
return true;
FINISH:
closesocket(*p_sock);
*p_sock = 0;
return false;
}
bool IsConnected(uint32_t timeLatestHeartBeat) {
const unsigned long ConnTimeoutMs = 3000;
auto cur = GetCurrentTime();
auto latest = timeLatestHeartBeat;
//if (cur - latest > ConnTimeoutMs)
//{
// debug("cur: {}, late: {}", cur, latest);
//}
return cur - latest < ConnTimeoutMs;
}
bool StayInTouch(const char* name, SOCKET* pSock, uint32_t* pTimeLatestHeartBeat) {
// Client checks inbox right away and measure timeout later.
char heartBeat[MAX_PATH] = { 0 };
// CAUTION: min 100ms required for receiving heartbeat.
//const uint32_t TimeoutMS = 100;
int flags = 0;
int nBytesRecved = recv(*pSock, heartBeat, sizeof(heartBeat), flags);
bool gotHeartbeat = nBytesRecved > 0;
if (gotHeartbeat) {
debug("{}: Heartbeat received: {}", name, heartBeat);
*pTimeLatestHeartBeat = GetCurrentTime();
}
return IsConnected(*pTimeLatestHeartBeat);
}
void Close(SOCKET* pSock) {
closesocket(*pSock);
*pSock = 0;
}
bool Connect() {
if (g_threadFirst.joinable()) {
warn("FirstTunnel already running. Skipped.");
return true;
}
g_threadFirst = std::thread([&]() {
bool isConnected = false;
while (true) {
while (!isConnected) {
isConnected = Init(2345, &g_sockFirst);
}
isConnected = StayInTouch("FirstTunnel", &g_sockFirst, &g_timeLatestHeartBeatFirst);
if (!isConnected) {
// We don't close as client.
// We keep connecting
Close(&g_sockFirst);
error("About to reconnect ...");
continue;
}
if (!g_threadSecond.joinable()) {
g_threadSecond = std::thread([&]() {
while (true) {
while (!isConnected) {
isConnected = Init(2346, &g_sockSecond);
}
isConnected = StayInTouch("SecondTunnel", &g_sockSecond, &g_timeLatestHeartBeatSecond);
if (!isConnected) {
// We don't close as client.
// We keep connecting
error("About to reconnect ...");
Sleep(1000);
continue;
}
}
info("SecondTunnel quitting...");
Close(&g_sockSecond);
});
}
}
info("FirstTunnel quitting.");
Close(&g_sockFirst);
});
while (true) {
//info("main thread ...");
Sleep(3000);
}
return g_threadFirst.joinable() ? true : false;
}
int main() {
SetupLogger();
info("Hello World!\n");
if (InitWinSock()) {
critical("Failed to initialize Window socket. Aborted.");
}
Connect();
if (g_threadSecond.joinable()) {
g_threadSecond.join();
}
if (g_threadFirst.joinable()) {
g_threadFirst.join();
}
WSACleanup();
info("Bye!");
}
The main connection logic is in the function Connect().
I'd appreciate tips on where I was wrong.
To run the code as is, you need one dependency spdlog
vcpkg install spdlog:x64-Windows
You can also replace all the spdlog-based logging code with your own.
UPDATE
Observation #1
I stepped into the code and confirmed
All the loops are running. So all the threads are spawned.
No redundant threads are spawned due to the joinable() guard.
The only failure point is the recv call of the second client.
So conclusion
No firewalls
No redundant threads
Both threads run on the client and server sides by design.
Observation #2
While running the server and client programs and having let the problem happen, I tried
Keep both programs open.
Run netcat (NMap's netcat port) like this C:\Apps\Nmap\ncat.exe 127.0.0.1 2346
This actually help fix things. It tells me that there is a connection problem, while I could definitely step into the client code and see that the connection is still there.
Observation #3
After I leave breakpoints in only the second connection code of the client program, and run the program, I could not step out into the first connection code. Note that they are in separate threads.
Leaving breakpoints in both connections, and quickly step over between them, I could keep the stepping going without getting trapped in only one thread. This is when I notice that the client starts receiving the messages it should.
So I suspect that there is a threading issue there. If that's the problem, How to fix this then?
I found the problem myself. It was a stupid bug on my part:
The two threads share a state by mistake: isConnected. So the original programs keep kicking each other after one of them gains a new connection or heartbeat. They should have used different states for that.
To give more detail: isConnected, which is initialized by g_threadFirst, falls through the closure of g_threadSecond's anonymous function.
I have adapted the code from http://beej.us/guide/bgnet/output/html/singlepage/bgnet.html (selectserver.c -- a cheezy multiperson chat server) to compile on Windows. The complete code follows below. I compile using gcc version 6.1.0 (x86_64-posix-seh, Built by MinGW-W64 project). I compile using gcc6.1.0 on Linux, too.
Basically, you run it, telnet 2 or more times to port 9034, and whatever you type in one telnet session gets echoed to the other telnet sessions (depending on the system, one has to type Enter before it gets echoed - on Windows it echoes every character typed).
Now the problem :
On Linux AMD64 or ARM, I can connect to it from localhost and from another system, be that Windoes or Linux. On Windows, it only works on localhost, and I fail to understand why. The fact that hints.ai_flags = AI_PASSIVE; is specified makes it listen on all interfaces, if I understand things correctly.
The MSDN doc states:
Setting the AI_PASSIVE flag indicates the caller intends to use the returned socket address structure in a call to the bind function.
When the AI_PASSIVE flag is set and pNodeName is a NULL pointer, the IP address portion of the socket address structure is set to INADDR_ANY for IPv4 addresses and IN6ADDR_ANY_INIT for IPv6 addresses.
The code reads :
hints.ai_flags = AI_PASSIVE;
if ((rv = getaddrinfo(NULL, PORT, &hints, &ai)) != 0)
How do I make this behave correctly on Windows?
It is compiled with :
g++ -O0 -g3 -Wall -c -fmessage-length=0 -o "src\chatserver.o" "..\src\chatserver.cpp"
and linked with
g++ -mwindows -o chatserver.exe "src\chatserver.o" -lws2_32
What do I need to change in the code please?
This is the complete code:
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#ifdef __linux__
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#endif
#ifdef _WIN32
#include <ws2tcpip.h>
#endif
#define PORT "9034" // port we're listening on
// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) { return &(((struct sockaddr_in*)sa)->sin_addr); }
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main(void)
{
#ifdef _WIN32
WSADATA wsaData; // Initialize Winsock
int nResult = WSAStartup(MAKEWORD(2,2), &wsaData);
if (NO_ERROR != nResult) {
printf ("Error occurred while executing WSAStartup().");
}
#endif
fd_set master; // master file descriptor list
fd_set read_fds; // temp file descriptor list for select()
int fdmax; // maximum file descriptor number
int listener; // listening socket descriptor
int newfd; // newly accept()ed socket descriptor
struct sockaddr_storage remoteaddr; // client address
socklen_t addrlen;
char buf[256]; // buffer for client data
int nbytes;
char remoteIP[INET6_ADDRSTRLEN];
int yes=1; // for setsockopt() SO_REUSEADDR, below
int i, j, rv;
struct addrinfo hints, *ai, *p;
FD_ZERO(&master); // clear the master and temp sets
FD_ZERO(&read_fds);
// get us a socket and bind it
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
if ((rv = getaddrinfo(NULL, PORT, &hints, &ai)) != 0) {
fprintf(stderr, "selectserver: %s\n", gai_strerror(rv));
exit(1);
}
for(p = ai; p != NULL; p = p->ai_next) {
listener = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (listener < 0) { continue; }
// lose the pesky "address already in use" error message
setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, (const char *)&yes, sizeof(int));
//setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, "1", sizeof(int));
if (bind(listener, p->ai_addr, p->ai_addrlen) < 0) {
close(listener);
continue;
}
break;
}
// if we got here, it means we didn't get bound
if (p == NULL) {
fprintf(stderr, "selectserver: failed to bind\n");
exit(2);
}
freeaddrinfo(ai); // all done with this
// listen
if (listen(listener, 10) == -1) {
perror("listen");
exit(3);
}
// add the listener to the master set
FD_SET(listener, &master);
// keep track of the biggest file descriptor
fdmax = listener; // so far, it's this one
// main loop
for(;;) {
read_fds = master; // copy it
if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1) {
perror("select");
exit(4);
}
// run through the existing connections looking for data to read
for(i = 0; i <= fdmax; i++) {
if (FD_ISSET(i, &read_fds)) { // we got one!!
if (i == listener) {
// handle new connections
addrlen = sizeof remoteaddr;
newfd = accept(listener,
(struct sockaddr *)&remoteaddr,
&addrlen);
if (newfd == -1) {
perror("accept");
}
else {
FD_SET(newfd, &master); // add to master set
if (newfd > fdmax) { // keep track of the max
fdmax = newfd;
}
std::cout << "selectserver: new connection on socket " << newfd;
/*
printf("selectserver: new connection from %s on "
"socket %d\n",
inet_ntop(remoteaddr.ss_family,get_in_addr((struct sockaddr*)&remoteaddr),remoteIP, INET6_ADDRSTRLEN),newfd);
*/
}
}
else {
// handle data from a client
if ((nbytes = recv(i, buf, sizeof buf, 0)) <= 0) {
// got error or connection closed by client
if (nbytes == 0) {
// connection closed
std::cout << "selectserver: socket " << i << " hung up";
}
else {
perror("recv");
}
close(i); // bye!
FD_CLR(i, &master); // remove from master set
}
else {
// we got some data from a client
for(j = 0; j <= fdmax; j++) {
// send to everyone!
if (FD_ISSET(j, &master)) {
// except the listener and ourselves
if (j != listener && j != i) {
if (send(j, buf, nbytes, 0) == -1) {
perror("send");
}
}
}
}
}
} // END handle data from client
} // END got new incoming connection
} // END looping through file descriptors
} // END for(;;)--and you thought it would never end!
return 0;
}
getaddrinfo() can return multiple IP addresses. You are correctly looping through all of the returned addresses, but you are breaking the loop after the first successful bind(), and then you are calling listen() on that one single socket, regardless of its socket family. Since you are using AF_UNSPEC when calling getaddrinfo(), it is possible that it is returning BOTH INADDR_ANY for IPv4 AND IN6ADDR_ANY_INIT for IPv6.
Change your code to listen on every IP address that getaddrinfo() returns, and to keep track of those sockets so you can use all of them in your select() loop. If you just wanted to listen on either INADDR_ANY or IN6ADDR_ANY_INIT, there would be no point in using getaddrinfo() at all, as you could just hard-code the socket()/bind() calls for those two addresses and get rid of the loop altogether. The purpose of using getaddrinfo() in this manner is to let it decide what you should be listening on, given the AI_PASSIVE hint you provided. Don't make assumptions about its output.
You also cannot use fdmax on Windows, so you need to re-write your select() loop. Sockets on Windows do not use file descriptors, so you can't simply loop from 0 <= fdmax when calling FD_ISSET(), and the first parameter of select() is ignored as well. I suggest not storing your active socket descriptors/handles in a master fd_set to begin with. Use a std::list or other suitable container instead, and then dynamically create a new fd_set whenever you need to call select(). This would be more portable across different platforms.
Try something more like this:
#include <unistd.h>
#include <sys/types.h>
#ifdef __linux__
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#define SOCKET int
#define SOCKET_ERROR -1
#define INVALID_SOCKET -1
inline int closesocket(int s) { return close(s); }
inline int getLastSocketError() { return errno; }
#endif
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
inline int getLastSocketError() { return WSAGetLastError(); }
#endif
#include <iostream>
#include <list>
#include <algorithm>
#include <utility>
#define PORT "9034" // port we're listening on
#ifdef _WIN32
#define SELECT_MAXFD 0
#else
#define SELECT_MAXFD fdmax+1
#endif
enum eSocketType { stListener, stClient };
struct SocketInfo
{
SOCKET sckt;
eSocketType type;
};
SocketInfo makeSocketInfo(SOCKET sckt, eSocketType type) {
SocketInfo info;
info.sckt = sckt;
info.type = type;
return info;
}
// get sockaddr, IPv4 or IPv6:
void* get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main(void)
{
std::list<SocketInfo> master; // socket descriptors
std::list<SocketInfo>::iterator i, j;
SOCKET sckt, newsckt; // socket descriptors
fd_set read_fds; // temp file descriptor list for select()
#ifndef _WIN32
int fdmax; // maximum file descriptor number
#endif
struct sockaddr_storage remoteaddr; // client address
socklen_t addrlen;
char buf[256]; // buffer for client data
int nbytes;
char ipAddr[INET6_ADDRSTRLEN];
int yes = 1; // for setsockopt() SO_REUSEADDR, below
int rv;
struct addrinfo hints, *ai, *p;
#ifdef _WIN32
WSADATA wsaData; // Initialize Winsock
rv = WSAStartup(MAKEWORD(2,2), &wsaData);
if (NO_ERROR != rv) {
std::cerr << "WSA startup failed, error: " << rv << std::endl;
return 1;
}
#endif
// get us the listening sockets and bind them
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
rv = getaddrinfo(NULL, PORT, &hints, &ai);
if (rv != 0) {
std::cerr << "selectserver: getaddrinfo failed, error: " << gai_strerror(rv) << std::endl;
return 2;
}
for(p = ai; p != NULL; p = p->ai_next) {
sckt = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (INVALID_SOCKET == sckt) {
std::cerr << "selectserver: socket failed, error: " << getLastSocketError() << std::endl;
continue;
}
// lose the pesky "address already in use" error message
setsockopt(sckt, SOL_SOCKET, SO_REUSEADDR, (const char *)&yes, sizeof(int));
//setsockopt(sckt, SOL_SOCKET, SO_REUSEADDR, "1", sizeof(int));
if (bind(sckt, p->ai_addr, p->ai_addrlen) < 0) {
std::cerr << "selectserver: bind failed, error: " << getLastSocketError() << std::endl;
closesocket(sckt);
continue;
}
// listen
if (listen(sckt, 10) < 0) {
std::cerr << "selectserver: listen failed, error: " << getLastSocketError() << std::endl;
closesocket(sckt);
continue;
}
/*
std::cout << "selectserver: listening on IP " << inet_ntop(p->ai_family, get_in_addr(p->ai_addr), ipAddr, sizeof(ipAddr)) << ", socket " << sckt << std::endl,
*/
// add the listener to the master list
master.push_back(makeSocketInfo(sckt, stListener));
}
freeaddrinfo(ai); // all done with this
// if we got here, it means we didn't get bound
if (master.empty()) {
std::cerr << "selectserver: failed to bind" << std::endl;
return 3;
}
// main loop
while (1) {
#ifndef _WIN32
fdmax = 0;
#endif
FD_ZERO(&read_fds);
for (i = master.begin(); i != master.end(); ++i) {
sckt = i->sckt;
FD_SET(sckt, &read_fds);
#ifndef _WIN32
fdmax = std::max(fdmax, sckt);
#endif
}
if (select(SELECT_MAXFD, &read_fds, NULL, NULL, NULL) < 0) {
std::cerr << "select failed, error: " << getLastSocketError() << std::endl;
return 4;
}
// run through the existing connections looking for data to read
for(i = master.begin(); i != master.end(); ) {
sckt = i->sckt;
if (!FD_ISSET(sckt, &read_fds)) {
++i;
continue;
}
// we got one!!
if (stListener == i->type) {
// handle a new connection
addrlen = sizeof(remoteaddr);
newsckt = accept(sckt, (struct sockaddr *)&remoteaddr, &addrlen);
if (INVALID_SOCKET == newsckt) {
std::cerr << "accept failed on socket " << sckt << ", error: " << getLastSocketError() << std::endl;
}
else {
master.push_back(makeSocketInfo(newsckt, stClient)); // add to master list
std::cout << "selectserver: new connection, socket " << newsckt << std::endl;
/*
std::cout << "selectserver: new connection from " << inet_ntop(remoteaddr.ss_family, get_in_addr((struct sockaddr*)&remoteaddr), ipAddr, sizeof(ipAddr)) << ", socket " << newsckt << std::endl,
*/
}
}
else {
// handle data from a client
nbytes = recv(sckt, buf, sizeof(buf), 0);
if (nbytes <= 0) {
// got error or connection closed by client
if (nbytes == 0) {
// connection closed
std::cout << "selectserver: socket " << sckt << " disconnected" << std::endl;
}
else {
std::cerr << "selectserver: recv failed on socket " << sckt << ", error: " << getLastSocketError() << std::endl;
}
closesocket(sckt); // bye!
i = master.erase(i); // remove from master list
continue;
}
// send to everyone!
// except a listener and ourselves
for(j = master.begin(); j != master.end(); ) {
if ((j->sckt != sckt) && (stClient == j->type)) {
if (send(j->sckt, buf, nbytes, 0) < 0) {
std::cerr << "selectserver: send failed on socket " << j->sckt << ", error: " << getLastSocketError() << std::endl;
closesocket(j->sckt); // bye!
j = master.erase(j); // remove from master list
continue;
}
}
++j;
}
}
++i;
}
}
for(i = master.begin(); i != master.end(); ++i) {
closesocket(i->sckt);
}
#ifdef _WIN32
WSACleanup();
#endif
return 0;
}
If you are running the code on a system that supports dual-stack sockets (like Windows), you can change AF_UNSPEC to AF_INET6 (or just hard-code socket()/bind() without using getaddrinfo()) to create only IPv6 listener(s) on IN6ADDR_ANY_INIT, and then disable the IPV6_V6ONLY socket option on them. This will allow IPv6 listen sockets to accept both IPv4 and IPv6 clients, reducing the number of listen sockets you need to create.
I'm trying to make a chess game, through sockets.
There is a server that is responsible for sending the board to the players, to get the input and response and so on..
I've tried to create a client-server (actually 2clients and server) but it doesn't work.
The clients connect to the server properly, but the data that is delieved doesn't correct.
For example, I send "E2-C3" from the client ; the recv result (on the server side) is always 0, or it prints garbage.
Client :
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <string>
#include <sys/types.h>
// Need to link with Ws2_32.lib, Mswsock.lib, and Advapi32.lib
#pragma comment (lib, "Ws2_32.lib")
#pragma comment (lib, "Mswsock.lib")
#pragma comment (lib, "AdvApi32.lib")
using namespace std;
#define SERVER_IP "1.1.1.1"
#define SERVER_PORT 8888
#define BUFFER_SIZE 1024
// server side
#define INVALID_MOVE 00
#define PLEASE_ENTER_A_MOVE 15
#define PRINT_BOARD 20
#define END_GAME 30
// client side
#define MOVE 10
int __cdecl main()
{
WSADATA info;
int errorDATA; // configuriation
int socketCreate; // create the socket - empty
SOCKADDR_IN ClientService; // configuriation (stage 3) - data of the server.
int connectResult;
char sendBuf[1024], recvbuf[1024];
int iResult;
/*Configuration*/
errorDATA = WSAStartup(MAKEWORD(2, 2), &info);
if (errorDATA == INVALID_SOCKET)
{
printf("WSAStartup failed with error : %d\n", errorDATA);
return -1;
}
/*Create empty socket*/
socketCreate = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // creating the socket "Clean - empty"
if (socketCreate == INVALID_SOCKET)
{
printf("Error number %d in creating socket!\n", WSAGetLastError());
return -1;
}
printf("Creating socket SUCCEEDED!\n");
/*Confugirate the created socket*/
ClientService.sin_family = AF_INET;
ClientService.sin_addr.s_addr = inet_addr(SERVER_IP); // server's ip
ClientService.sin_port = htons(SERVER_PORT);
/*Asking for connection*/
connectResult = connect(socketCreate, (struct sockaddr*) &ClientService, sizeof(ClientService));
while (1)
{
cout << "Please enter a move : " << endl;
cin >> sendBuf;
iResult = send(socketCreate, sendBuf, (int)strlen(sendBuf), 0);
if (iResult == SOCKET_ERROR) {
printf("send failed with error: %d\n", WSAGetLastError());
closesocket(socketCreate);
WSACleanup();
return 1;
}
// MOVE
iResult = recv(socketCreate, recvbuf, strlen(recvbuf), 0);
if (iResult > 0)
{
if (recvbuf[0] == '0' && recvbuf[1] == '0')
{
cout << "You've entered an illegal move. Please try again." << endl;
continue;
}
else if (recvbuf[0] == '2' && recvbuf[1] == '0')
{
// print the board.
bool keepGoing = 0;
do
{
iResult = recv(socketCreate, recvbuf, strlen(recvbuf), 0);
if (iResult > 0)
{
if (recvbuf[0] == '1' && recvbuf[1] == '5')
{
keepGoing = true;
continue;
}
}
} while (!keepGoing);
}
}
else if (iResult == 0)
printf("Connection closed\n");
else
printf("recv failed with error: %d\n", WSAGetLastError());
}
printf("Bytes Sent: %ld\n", iResult);
// shutdown the connection since no more data will be sent
iResult = shutdown(socketCreate, SD_SEND);
if (iResult == SOCKET_ERROR) {
printf("shutdown failed with error: %d\n", WSAGetLastError());
closesocket(socketCreate);
WSACleanup();
return 1;
}
// cleanup
closesocket(socketCreate);
WSACleanup();
cin.get();
system("pause");
return 0;
}
server:
#include <iostream>
#include <winsock2.h>
#include <string>
#include <windows.h>
#pragma comment(lib,"ws2_32.lib")
#define MAX_NUMBER_OF_PLAYERS 1
#define BUFFER_SIZE 1024
#define LIMIT 1
// server side
#define INVALID_MOVE 00
#define PLEASE_ENTER_A_MOVE 15
#define PRINT_BOARD 20
#define END_GAME 30
// client side
#define MOVE 10
using namespace std;
int main()
{
WSADATA WsaDat;
SOCKET clientsock[2];
int minsock = 0;
int numsocks = MAX_NUMBER_OF_PLAYERS;
if (WSAStartup(MAKEWORD(2, 2), &WsaDat) != 0)
{
std::cout << "WSA Initialization failed!\r\n";
WSACleanup();
system("PAUSE");
return 0;
}
SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (serverSocket == INVALID_SOCKET)
{
std::cout << "Socket creation failed.\r\n";
WSACleanup();
system("PAUSE");
return 0;
}
SOCKADDR_IN serverInf;
serverInf.sin_family = AF_INET;
serverInf.sin_addr.s_addr = INADDR_ANY;
serverInf.sin_port = htons(8888);
if (bind(serverSocket, (SOCKADDR*)(&serverInf), sizeof(serverInf)) == SOCKET_ERROR)
{
std::cout << "Unable to bind socket!\r\n";
WSACleanup();
system("PAUSE");
return 0;
}
listen(serverSocket, 5);
clientsock[0] = accept(serverSocket, NULL, NULL);
cout << "Client 1 has connected." << endl;
clientsock[1] = accept(serverSocket, NULL, NULL);
cout << "Client 2 has connected." << endl;
for (int i = 0; i < 2; i++)
{
cout << clientsock[i] << endl;
}
// If iMode!=0, non-blocking mode is enabled.
u_long iMode = 1;
ioctlsocket(serverSocket, FIONBIO, &iMode);
char client1_buffer[BUFFER_SIZE];
char client2_buffer[BUFFER_SIZE];
char* clientBuffer;
// until there isn't a mate.
bool gameRunning = true;
// user represents if it's user1 (0), or user2(1)
bool user = 0;
while (gameRunning)
{
if (!user)
clientBuffer = client1_buffer;
else
clientBuffer = client2_buffer;
int in = recv(clientsock[0], client1_buffer, 0, 0);
cout << in << endl;
if (in > 0)
{
// CHECKS
// MOVE COMMAND
// IF worked, send the board to both clients. if current user = 1 ==> do user to 0 | if the user = 0 => do user to 11
// ELSE, send the current client (clientsock[user]) Error message and ask for a command again.
cout << client1_buffer << endl;
cout << " IN RECV";
char* szMessage = "15";
send(clientsock[0], szMessage, strlen(szMessage), 0);
}
else if (in == 0)
{
// The connection has closed.
// REMEMBER : SAVE THE GAME SITUATION.
}
else
{
printf("recv failed: %d\n", WSAGetLastError());
// SEND ERROR MESSAGE TO BOTH CLIENTS
}
}
// Shutdown our socket
shutdown(serverSocket, SD_SEND);
// Close our socket entirely
closesocket(serverSocket);
WSACleanup();
system("pause");
return 0;
}
What Hans said. plus this:
int in = recv(clientsock[0], client1_buffer, 0, 0);
Your probably want to say this:
int in = recv(clientsock[0], client1_buffer, BUFFER_SIZE, 0);
Also, you are making a fundamental error that a lot of people make with sockets. You are assuming that your recv call on your server will return as many bytes was passed to the corresponding send call by the client. Fragmentation, segmentation, and other network stuff may cause you to receive only a partial amount of the message that was sent on the other node's send call. (Hence, TCP is a stream protocol as they say).
You should diligently check the return value from recv. Write your code as if the sender was only to going to send 1 byte at a time. You should put delimiters between your messages (a null char is fine) and loop on recv until you get a complete message. Otherwise, what seems to work fine on your own PC and on the local subnet will have strange bugs when deployed to the internet.
In the server file change
int in = recv(clientsock[0], client1_buffer, 0,0);
to
int in = recv(clientsock[0], client1_buffer, 1024,0);
change is the length of the date to be received
The problem is
strlen(recvbuf)
You probably mean
sizeof(recvbuf)
strlen(recvbuf) does not makes sense before you received the data, because your buffer contains just garbage, thus strlen() is just a bad random number generator. strlen(recvbuf) would make sense after you received the data if you would have made sure it was filled with 0. That not being the case, you can use the return value of recv() to find out how many bytes you received.
Communication between a server and a clients works, but the server don't forward the client messages to the other connected client's, but only to the sender.
i want the server react to incoming messages by broadcasting them to all clients like a chat system, but keep my command system without sharring it with all clients, but with with sender.
down below is the sources:
server
/*server*/
#define WIN32_LEAN_AND_MEAN
#include <iostream>
#include <string>
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdlib.h>
#include <stdio.h>
#include <process.h>
// Need to link with Ws2_32.lib, Mswsock.lib, and Advapi32.lib
#pragma comment (lib, "Ws2_32.lib")
#pragma comment (lib, "Mswsock.lib")
#pragma comment (lib, "AdvApi32.lib")
#define PORT "3490"
#define SERVER "localhost"
#include <time.h>
WSADATA wsa;
SOCKET s , new_socket;
struct sockaddr_in server , client;
int c;
char *message;
std::string line;
DWORD WINAPI ProcessClient (LPVOID lpParameter)
{
SOCKET AcceptSocket = (SOCKET) lpParameter;
// Send and receive data.
int bytesSent;
int bytesRecv = SOCKET_ERROR;
char sendbuf[2000]="";
char sendbuf2[2000]="";
char recvbuf[2000]="";
char timebuf[128];
sprintf(sendbuf, "Hello, it's a test server at %s:%d (commands: 1, 2, exit)\n", SERVER, PORT);
bytesSent = send( AcceptSocket, sendbuf, strlen(sendbuf), 0);
if (bytesSent == SOCKET_ERROR)
{
printf( "Error at send hello: %ld\n", WSAGetLastError());
goto fin;
}
while (1)
{
_strtime( timebuf );
ZeroMemory (recvbuf, sizeof(recvbuf));
bytesRecv = recv( AcceptSocket, recvbuf, 32, 0);
printf( "%s Client said: %s\n", timebuf, recvbuf);
sprintf(sendbuf, "%s Client said: %s\n", timebuf, recvbuf);
bytesSent = send( AcceptSocket, sendbuf, strlen(sendbuf), 0);
if (strcmp(recvbuf, "1") == 0)
{
sprintf(sendbuf, "You typed ONE\n");
//printf("Sent '%s'\n", sendbuf);
bytesSent = send( AcceptSocket, sendbuf, strlen(sendbuf), 0);
if (bytesSent == SOCKET_ERROR)
{
printf( "Error at send: %ld\n", WSAGetLastError());
goto fin;
}
}
else if (strcmp(recvbuf, "2") == 0)
{
sprintf(sendbuf, "You typed TWO\n");
//printf("Sent '%s'\n", sendbuf);
bytesSent = send( AcceptSocket, sendbuf, strlen(sendbuf), 0);
if (bytesSent == SOCKET_ERROR)
{
printf( "Error at send: %ld\n", WSAGetLastError());
goto fin;
}
}
else if (strcmp(recvbuf, "exit") == 0)
{
printf( "Client has logged out\n", WSAGetLastError());
goto fin;
}
else
{
// sprintf(sendbuf, "unknown command\n");
//printf("Sent '%s'\n", sendbuf);
// bytesSent = send( AcceptSocket, sendbuf, strlen(sendbuf), 0);
if (bytesSent == SOCKET_ERROR)
{
// printf( "Error at send: %ld\n", WSAGetLastError());
goto fin;
}
}
}
fin:
printf("Client processed\n");
closesocket(AcceptSocket);
return 0;
}
int main(int argc , char *argv[])
{
std::cout << ("\nInitialising Winsock...");
if (WSAStartup(MAKEWORD(2,2),&wsa) != 0)
{
std::cout << ("Failed. Error Code : %d",WSAGetLastError());
return 1;
}
printf("Initialised.\n");
//Create a socket
if((s = socket(AF_INET , SOCK_STREAM , 0 )) == INVALID_SOCKET)
{
std::cout << ("Could not create socket : %d" , WSAGetLastError());
}
std::cout << ("Socket created.\n");
//Prepare the sockaddr_in structure
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons( 3490 );
//Bind
if( bind(s ,(struct sockaddr *)&server , sizeof(server)) == SOCKET_ERROR)
{
std::cout << ("Bind failed with error code : %d" , WSAGetLastError());
exit(EXIT_FAILURE);
}
puts("Bind done");
//Listen to incoming connections
listen(s , 3);
//Accept and incoming connection
std::cout << ("Waiting for incoming connections...");
c = sizeof(struct sockaddr_in);
while(true){
while((new_socket = accept(s , (struct sockaddr *)&client, &c)) != INVALID_SOCKET) {
// Create a new thread for the accepted client (also pass the accepted client socket).
printf( "Client Connected.\n");
DWORD dwThreadId;
CreateThread (NULL, 0, ProcessClient, (LPVOID) new_socket, 0, &dwThreadId);
}
}
if (new_socket == INVALID_SOCKET)
{
std::cout << ("accept failed with error code : %d" , WSAGetLastError());
return 1;
}
closesocket(s);
WSACleanup();
return 0;
}
client
/*client*/
#define WIN32_LEAN_AND_MEAN
#pragma comment(lib,"ws2_32.lib")
#include <iostream>
#include <process.h>
#include <string>
#include <winsock2.h>
SOCKET Socket;
#define SERVER "localhost"
int PORT = 3490;
std::string line;
bool chat = false;
class Buffer
{
public:
int ID;
char Message[256];
}sbuffer;
int ClientThread()
{
char buffer[2000]= "";
for(;; Sleep(10))
{
if(recv(Socket, buffer, sizeof(sbuffer), NULL)!=SOCKET_ERROR)
{
strncpy(sbuffer.Message, buffer, sizeof(sbuffer.Message));
std::cout << "<Client:" << sbuffer.ID << ":> " << sbuffer.Message <<std::endl;
ZeroMemory (buffer, sizeof(buffer));
}
}
return 0;
}
int main(void)
{
WSADATA WsaDat;
if(WSAStartup(MAKEWORD(2,2),&WsaDat)!=0)
{
std::cout<<"Winsock error - Winsock initialization failed\r\n";
WSACleanup();
system("PAUSE");
return 0;
}
// Create our socket
Socket=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(Socket==INVALID_SOCKET)
{
std::cout<<"Winsock error - Socket creation Failed!\r\n";
WSACleanup();
system("PAUSE");
return 0;
}
// Resolve IP address for hostname
struct hostent *host;
if((host=gethostbyname(SERVER))==NULL)
{
std::cout<<"Failed to resolve hostname.\r\n";
WSACleanup();
system("PAUSE");
return 0;
}
// Setup our socket address structure
SOCKADDR_IN SockAddr;
SockAddr.sin_port=htons(PORT);
SockAddr.sin_family=AF_INET;
SockAddr.sin_addr.s_addr=*((unsigned long*)host->h_addr);
// Attempt to connect to server
if(connect(Socket,(SOCKADDR*)(&SockAddr),sizeof(SockAddr))!=0)
{
std::cout<<"Failed to establish connection with server\r\n";
WSACleanup();
system("PAUSE");
return 0;
}
// If iMode!=0, non-blocking mode is enabled.
u_long iMode=1;
ioctlsocket(Socket,FIONBIO,&iMode);
// Main loop
for(;;)
{
// Display message from server
char buffer[1000];
memset(buffer,0,999);
int inDataLength=recv(Socket,buffer,1000,0);
std::cout<<buffer;
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE) ClientThread, NULL, NULL, NULL);
for(;; Sleep(10))
{
std::string buffer;
std::getline(std::cin, buffer);
if (send(Socket, buffer.c_str(), buffer.length(), NULL) < 1){
}
}
int nError=WSAGetLastError();
if(nError!=WSAEWOULDBLOCK&&nError!=0)
{
std::cout<<"Winsock error code: "<<nError<<"\r\n";
std::cout<<"Server disconnected!\r\n";
// Shutdown our socket
shutdown(Socket,SD_SEND);
// Close our socket entirely
closesocket(Socket);
break;
}
Sleep(1000);
}
WSACleanup();
system("PAUSE");
return 0;
}
Please help me fix it, i'm new into socket's. Show me how to do as i'm going understand better with code and it will also be usefull to others who might need it in the future.
If you need the server to communicate with multiple clients, then you need some kind of collection of all the connected clients. Then it's easy to send to all connections, or send to all connection but the originating connection.
How to do it will differ vastly between C and C++, but for C++ look into structures and std::vector.
In pseudo-code it would be something like this:
while (run_server)
{
poll_all_connections();
if (have_new_connection())
{
accept_new_connection();
add_connection_in_collection();
}
else
{
for (connection in all_connections())
{
if (have_input(connection))
{
input = read_from_connection(connection);
for (send_to in all_connections())
write_to_connection(connection, input)
}
}
}
}
If you implement the above pseudo-code, then input from any connection will be sent to all connections.
Don't forget to remove a connection from the collection if the connection is broken (error or disconnect.)
You have to maintain a list of all the client socket connections then send the data to each client one by one.
or you can use threading to implement this as follows :-
Server-thread()
{
while(true)
{
/// Accept Connection in ClientSocket.
HandleClient-Thread(ClientSocket) ; // launch a thread for each client .
}
}
HandleClient-Thread(ClientSocket)
{
// handle this client here
}