Skip to content

Commit

Permalink
Make net usage statistics more meaningful.
Browse files Browse the repository at this point in the history
Previous behaviour: Show bytes sent, before compression, per CPU-second. So with 50%
cpu usage, the numbers would be doubled.

New bevaviour: Show actual bytes sent, and also show bytes sent before compression,
per actual second, independent of CPU usage.
  • Loading branch information
Cyp committed Apr 29, 2012
1 parent e50d513 commit 5e3d4bc
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 107 deletions.
134 changes: 52 additions & 82 deletions lib/netplay/netplay.cpp
Expand Up @@ -95,12 +95,17 @@ SYNC_COUNTER sync_counter; // keeps track on how well we are in sync
// ////////////////////////////////////////////////////////////////////////
// Types

struct Statistic
{
unsigned sent;
unsigned received;
};

struct NETSTATS // data regarding the last one second or so.
{
UDWORD bytesRecvd;
UDWORD bytesSent; // number of bytes sent in about 1 sec.
UDWORD packetsSent;
UDWORD packetsRecvd;
Statistic rawBytes; // Number of actual bytes, in about 1 sec.
Statistic uncompressedBytes; // Number of bytes sent, before compression, in about 1 sec.
Statistic packets; // Number of calls to writeAll, in about 1 sec.
};

struct NET_PLAYER_DATA
Expand Down Expand Up @@ -151,10 +156,15 @@ static char lanaddr[16];
static Socket* tmp_socket[MAX_TMP_SOCKETS] = { NULL };

static SocketSet* tmp_socket_set = NULL;
static NETSTATS nStats = { 0, 0, 0, 0 };
static int32_t NetGameFlags[4] = { 0, 0, 0, 0 };
char iptoconnect[PATH_MAX] = "\0"; // holds IP/hostname from command line

static NETSTATS nStats = {{0, 0}, {0, 0}, {0, 0}};
static NETSTATS nStatsLastSec = {{0, 0}, {0, 0}, {0, 0}};
static NETSTATS nStatsSecondLastSec = {{0, 0}, {0, 0}, {0, 0}};
static const NETSTATS nZeroStats = {{0, 0}, {0, 0}, {0, 0}};
static int nStatsLastUpdateTime = 0;

unsigned NET_PlayerConnectionStatus[CONNECTIONSTATUS_NORMAL][MAX_PLAYERS];

// ////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -207,10 +217,15 @@ static size_t NET_fillBuffer(Socket **pSocket, SocketSet* socket_set, uint8_t *b
return 0;
}

size = readNoInt(socket, bufstart, bufsize);
size_t rawBytes;
size = readNoInt(socket, bufstart, bufsize, &rawBytes);

if ((size != 0 || !socketReadDisconnected(socket)) && size != SOCKET_ERROR)
{
nStats.rawBytes.received += rawBytes;
nStats.uncompressedBytes.received += size;
nStats.packets.received += 1;

return size;
}
else
Expand Down Expand Up @@ -1167,80 +1182,27 @@ int NETclose(void)

// ////////////////////////////////////////////////////////////////////////
// return bytes of data sent recently.
UDWORD NETgetBytesSent(void)
unsigned NETgetStatistic(NetStatisticType type, bool sent)
{
static UDWORD lastsec=0;
static UDWORD timy=0;

if( (UDWORD)clock() > (timy+CLOCKS_PER_SEC) )
{
timy = clock();
lastsec = nStats.bytesSent;
nStats.bytesSent = 0;
}

return lastsec;
}

UDWORD NETgetRecentBytesSent(void)
{
return nStats.bytesSent;
}


UDWORD NETgetBytesRecvd(void)
{
static UDWORD lastsec=0;
static UDWORD timy=0;
if( (UDWORD)clock() > (timy+CLOCKS_PER_SEC) )
unsigned Statistic::*statisticType = sent? &Statistic::sent : &Statistic::received;
Statistic NETSTATS::*statsType;
switch (type)
{
timy = clock();
lastsec = nStats.bytesRecvd;
nStats.bytesRecvd = 0;
case NetStatisticRawBytes: statsType = &NETSTATS::rawBytes; break;
case NetStatisticUncompressedBytes: statsType = &NETSTATS::uncompressedBytes; break;
case NetStatisticPackets: statsType = &NETSTATS::packets; break;
default: ASSERT(false, " "); return 0;
}
return lastsec;
}

UDWORD NETgetRecentBytesRecvd(void)
{
return nStats.bytesRecvd;
}


//return number of packets sent last sec.
UDWORD NETgetPacketsSent(void)
{
static UDWORD lastsec=0;
static UDWORD timy=0;

if( (UDWORD)clock() > (timy+CLOCKS_PER_SEC) )
int time = wzGetTicks();
if ((unsigned)(time - nStatsLastUpdateTime) >= (unsigned)GAME_TICKS_PER_SEC)
{
timy = clock();
lastsec = nStats.packetsSent;
nStats.packetsSent = 0;
nStatsLastUpdateTime = time;
nStatsSecondLastSec = nStatsLastSec;
nStatsLastSec = nStats;
}

return lastsec;
}


UDWORD NETgetRecentPacketsSent(void)
{
return nStats.packetsSent;
}


UDWORD NETgetPacketsRecvd(void)
{
static UDWORD lastsec=0;
static UDWORD timy=0;
if( (UDWORD)clock() > (timy+CLOCKS_PER_SEC) )
{
timy = clock();
lastsec = nStats.packetsRecvd;
nStats.packetsRecvd = 0;
}
return lastsec;
return nStatsLastSec.*statsType.*statisticType - nStatsSecondLastSec.*statsType.*statisticType;
}


Expand Down Expand Up @@ -1287,13 +1249,15 @@ bool NETsend(NETQUEUE queue, NetMessage const *message)
{
uint8_t *rawData = message->rawDataDup();
ssize_t rawLen = message->rawLen();
result = writeAll(sockets[player], rawData, rawLen);
size_t compressedRawLen;
result = writeAll(sockets[player], rawData, rawLen, &compressedRawLen);
delete[] rawData; // Done with the data.

if (result == rawLen)
{
nStats.bytesSent += rawLen;
nStats.packetsSent += 1;
nStats.rawBytes.sent += compressedRawLen;
nStats.uncompressedBytes.sent += rawLen;
nStats.packets.sent += 1;
}
else if (result == SOCKET_ERROR)
{
Expand All @@ -1316,13 +1280,15 @@ bool NETsend(NETQUEUE queue, NetMessage const *message)
{
uint8_t *rawData = message->rawDataDup();
ssize_t rawLen = message->rawLen();
result = writeAll(tcp_socket, rawData, rawLen);
size_t compressedRawLen;
result = writeAll(tcp_socket, rawData, rawLen, &compressedRawLen);
delete[] rawData; // Done with the data.

if (result == rawLen)
{
nStats.bytesSent += rawLen;
nStats.packetsSent += 1;
nStats.rawBytes.sent += compressedRawLen;
nStats.uncompressedBytes.sent += rawLen;
nStats.packets.sent += 1;
}
else if (result == SOCKET_ERROR)
{
Expand Down Expand Up @@ -1365,30 +1331,34 @@ void NETflush()

NETflushGameQueues();

size_t compressedRawLen;
if (NetPlay.isHost)
{
for (int player = 0; player < MAX_CONNECTED_PLAYERS; ++player)
{
// We are the host, send directly to player.
if (connected_bsocket[player] != NULL)
{
socketFlush(connected_bsocket[player]);
socketFlush(connected_bsocket[player], &compressedRawLen);
nStats.rawBytes.sent += compressedRawLen;
}
}
for (int player = 0; player < MAX_TMP_SOCKETS; ++player)
{
// We are the host, send directly to player.
if (tmp_socket[player] != NULL)
{
socketFlush(tmp_socket[player]);
socketFlush(tmp_socket[player], &compressedRawLen);
nStats.rawBytes.sent += compressedRawLen;
}
}
}
else
{
if (bsocket != NULL)
{
socketFlush(bsocket);
socketFlush(bsocket, &compressedRawLen);
nStats.rawBytes.sent += compressedRawLen;
}
}
}
Expand Down
9 changes: 2 additions & 7 deletions lib/netplay/netplay.h
Expand Up @@ -301,13 +301,8 @@ extern void NETaddRedirects(void);
extern void NETremRedirects(void);
extern void NETdiscoverUPnPDevices(void);

extern UDWORD NETgetBytesSent(void); // return bytes sent/recv. call regularly for good results
extern UDWORD NETgetPacketsSent(void); // return packets sent/recv. call regularly for good results
extern UDWORD NETgetBytesRecvd(void); // return bytes sent/recv. call regularly for good results
extern UDWORD NETgetPacketsRecvd(void); // return packets sent/recv. call regularly for good results
extern UDWORD NETgetRecentBytesSent(void); // more immediate functions.
extern UDWORD NETgetRecentPacketsSent(void);
extern UDWORD NETgetRecentBytesRecvd(void);
enum NetStatisticType {NetStatisticRawBytes, NetStatisticUncompressedBytes, NetStatisticPackets};
unsigned NETgetStatistic(NetStatisticType type, bool sent); // Return some statistic. Call regularly for good results.

extern void NETplayerKicked(UDWORD index); // Cleanup after player has been kicked

Expand Down
22 changes: 19 additions & 3 deletions lib/netplay/netsocket.cpp
Expand Up @@ -473,8 +473,12 @@ static int socketThreadFunction(void *)
* Similar to read(2) with the exception that this function won't be
* interrupted by signals (EINTR).
*/
ssize_t readNoInt(Socket* sock, void* buf, size_t max_size)
ssize_t readNoInt(Socket* sock, void* buf, size_t max_size, size_t *rawByteCount)
{
size_t ignored;
size_t &rawBytes = rawByteCount != NULL? *rawByteCount : ignored;
rawBytes = 0;

if (sock->fd[SOCK_CONNECTION] == INVALID_SOCKET)
{
debug(LOG_ERROR, "Invalid socket");
Expand Down Expand Up @@ -502,6 +506,7 @@ ssize_t readNoInt(Socket* sock, void* buf, size_t max_size)

sock->zInflate.next_in = &sock->zInflateInBuf[0];
sock->zInflate.avail_in = received;
rawBytes = received;

if (received == 0)
{
Expand Down Expand Up @@ -551,6 +556,7 @@ ssize_t readNoInt(Socket* sock, void* buf, size_t max_size)

sock->ready = false;

rawBytes = received;
return received;
}

Expand All @@ -565,8 +571,12 @@ bool socketReadDisconnected(Socket *sock)
*
* @return @c size when succesful or @c SOCKET_ERROR if an error occurred.
*/
ssize_t writeAll(Socket* sock, const void* buf, size_t size)
ssize_t writeAll(Socket* sock, const void* buf, size_t size, size_t *rawByteCount)
{
size_t ignored;
size_t &rawBytes = rawByteCount != NULL? *rawByteCount : ignored;
rawBytes = 0;

if (!sock
|| sock->fd[SOCK_CONNECTION] == INVALID_SOCKET)
{
Expand All @@ -592,6 +602,7 @@ ssize_t writeAll(Socket* sock, const void* buf, size_t size)
std::vector<uint8_t> &writeQueue = socketThreadWrites[sock];
writeQueue.insert(writeQueue.end(), static_cast<char const *>(buf), static_cast<char const *>(buf) + size);
wzMutexUnlock(socketThreadMutex);
rawBytes = size;
}
else
{
Expand Down Expand Up @@ -619,8 +630,12 @@ ssize_t writeAll(Socket* sock, const void* buf, size_t size)
return size;
}

void socketFlush(Socket *sock)
void socketFlush(Socket *sock, size_t *rawByteCount)
{
size_t ignored;
size_t &rawBytes = rawByteCount != NULL? *rawByteCount : ignored;
rawBytes = 0;

if (!sock->isCompressed)
{
return; // Not compressed, so don't mess with zlib.
Expand Down Expand Up @@ -663,6 +678,7 @@ void socketFlush(Socket *sock)
//printf("\n");

// Data sent, don't send again.
rawBytes = sock->zDeflateOutBuf.size();
sock->zDeflateInSize = 0;
sock->zDeflateOutBuf.clear();
}
Expand Down
6 changes: 3 additions & 3 deletions lib/netplay/netsocket.h
Expand Up @@ -103,14 +103,14 @@ void socketArrayClose(Socket **sockets, size_t maxSockets); ///< Clo

char const *getSocketTextAddress(Socket const *sock); ///< Gets a string with the socket address.
bool socketReadReady(Socket const *sock); ///< Returns if checkSockets found data to read from this Socket.
ssize_t readNoInt(Socket *sock, void *buf, size_t max_size); ///< Reads up to max_size bytes from the Socket.
ssize_t readNoInt(Socket *sock, void *buf, size_t max_size, size_t *rawByteCount = NULL); ///< Reads up to max_size bytes from the Socket. Raw count of bytes (after compression) returned in rawByteCount.
ssize_t readAll(Socket* sock, void *buf, size_t size, unsigned timeout);///< Reads exactly size bytes from the Socket, or blocks until the timeout expires.
ssize_t writeAll(Socket *sock, const void* buf, size_t size); ///< Nonblocking write of size bytes to the Socket. All bytes will be written asynchronously, by a separate thread.
ssize_t writeAll(Socket *sock, const void* buf, size_t size, size_t *rawByteCount = NULL); ///< Nonblocking write of size bytes to the Socket. All bytes will be written asynchronously, by a separate thread. Raw count of bytes (after compression) returned in rawByteCount, which will often be 0 until the socket is flushed.

// Sockets, compressed.
void socketBeginCompression(Socket *sock); ///< Makes future data sent compressed, and future data received expected to be compressed.
bool socketReadDisconnected(Socket *sock); ///< If readNoInt returned 0, returns true if this is the result of a disconnect, or false if the input compressed data just hasn't produced any output bytes.
void socketFlush(Socket *sock); ///< Actually sends the data written with writeAll. Only useful on compressed sockets. Note that flushing too often makes compression less effective.
void socketFlush(Socket *sock, size_t *rawByteCount = NULL); ///< Actually sends the data written with writeAll. Only useful on compressed sockets. Note that flushing too often makes compression less effective. Raw count of bytes (after compression) returned in rawByteCount.

// Socket sets.
SocketSet *allocSocketSet(void); ///< Constructs a SocketSet.
Expand Down
13 changes: 7 additions & 6 deletions src/keybind.cpp
Expand Up @@ -595,12 +595,13 @@ void kf_FrameRate( void )
frameRate(), loopPieCount, loopPolyCount, loopStateChanges));
if (runningMultiplayer())
{
CONPRINTF(ConsoleString,(ConsoleString,
"NETWORK: Bytes: s-%d r-%d Packets: s-%d r-%d",
NETgetBytesSent(),
NETgetBytesRecvd(),
NETgetPacketsSent(),
NETgetPacketsRecvd() ));
CONPRINTF(ConsoleString, (ConsoleString, "NETWORK: Bytes: s-%d r-%d Uncompressed Bytes: s-%d r-%d Packets: s-%d r-%d",
NETgetStatistic(NetStatisticRawBytes, true),
NETgetStatistic(NetStatisticRawBytes, false),
NETgetStatistic(NetStatisticUncompressedBytes, true),
NETgetStatistic(NetStatisticUncompressedBytes, false),
NETgetStatistic(NetStatisticPackets, true),
NETgetStatistic(NetStatisticPackets, false)));
}
gameStats = !gameStats;
CONPRINTF(ConsoleString, (ConsoleString,"Built at %s on %s",__TIME__,__DATE__));
Expand Down
20 changes: 14 additions & 6 deletions src/multimenu.cpp
Expand Up @@ -812,14 +812,22 @@ static void displayExtraGubbins(UDWORD height)
}

#ifdef DEBUG
unsigned int width;
unsigned width = 0;

sprintf(str,"Traf: %u/%u", NETgetBytesSent(), NETgetBytesRecvd());
width = iV_GetTextWidth(str);
iV_DrawText(str, MULTIMENU_FORM_X, MULTIMENU_FORM_Y + MULTIMENU_FORM_H);
sprintf(str, "Sent/Received —");
iV_DrawText(str, MULTIMENU_FORM_X + width, MULTIMENU_FORM_Y + MULTIMENU_FORM_H);
width += iV_GetTextWidth(str) + 20;

sprintf(str,"Pack: %u/%u", NETgetPacketsSent(), NETgetPacketsRecvd());
iV_DrawText(str, MULTIMENU_FORM_X + 20 + width, MULTIMENU_FORM_Y + MULTIMENU_FORM_H);
sprintf(str, "Traf: %u/%u", NETgetStatistic(NetStatisticRawBytes, true), NETgetStatistic(NetStatisticRawBytes, false));
iV_DrawText(str, MULTIMENU_FORM_X + width, MULTIMENU_FORM_Y + MULTIMENU_FORM_H);
width += iV_GetTextWidth(str) + 20;

sprintf(str, "Uncompressed: %u/%u", NETgetStatistic(NetStatisticUncompressedBytes, true), NETgetStatistic(NetStatisticUncompressedBytes, false));
iV_DrawText(str, MULTIMENU_FORM_X + width, MULTIMENU_FORM_Y + MULTIMENU_FORM_H);
width += iV_GetTextWidth(str) + 20;

sprintf(str, "Pack: %u/%u", NETgetStatistic(NetStatisticPackets, true), NETgetStatistic(NetStatisticPackets, false));
iV_DrawText(str, MULTIMENU_FORM_X + width, MULTIMENU_FORM_Y + MULTIMENU_FORM_H);
#endif
return;
}
Expand Down

0 comments on commit 5e3d4bc

Please sign in to comment.