added new networking subsystem (early stage - files are not compiled yet during normal build!)
Note The files i added / modifications i did, are not affecting a normal build nothing gets changed yet! Linux 2.5+ only yet. git-svn-id: https://svn.code.sf.net/p/rathena/svn/trunk@16271 54d463be-8e91-2dee-dedb-b68131a5f0ec
This commit is contained in:
parent
b3b21e6e8c
commit
7332a89352
33
configure
vendored
33
configure
vendored
@ -1,5 +1,5 @@
|
||||
#! /bin/sh
|
||||
# From configure.in Revision: 16221 .
|
||||
# From configure.in Revision: 16226 .
|
||||
# Guess values for system-dependent variables and create Makefiles.
|
||||
# Generated by GNU Autoconf 2.67.
|
||||
#
|
||||
@ -665,6 +665,7 @@ enable_rdtsc
|
||||
enable_profiler
|
||||
enable_64bit
|
||||
enable_lto
|
||||
with_maxconn
|
||||
with_mysql
|
||||
with_MYSQL_CFLAGS
|
||||
with_MYSQL_LIBS
|
||||
@ -1312,6 +1313,8 @@ Optional Features:
|
||||
Optional Packages:
|
||||
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
|
||||
--without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
|
||||
--with-maxconn[=ARG] optionally set the maximum connections the core can
|
||||
handle (default: 16384) NOT USED YET - EXPERIMENTAL
|
||||
--with-mysql[=ARG] optionally specify the path to the mysql_config
|
||||
executable
|
||||
--with-MYSQL_CFLAGS=ARG specify MYSQL_CFLAGS manually (instead of using
|
||||
@ -3530,6 +3533,34 @@ fi
|
||||
|
||||
|
||||
|
||||
#
|
||||
# Optionally set the max number of network conenctions
|
||||
# the core will be support
|
||||
#
|
||||
|
||||
# Check whether --with-maxconn was given.
|
||||
if test "${with_maxconn+set}" = set; then :
|
||||
withval=$with_maxconn;
|
||||
if test "$withval" == "no"; then
|
||||
CFLAGS="$CFLAGS -DMAXCONN=16384"
|
||||
else
|
||||
|
||||
if ! test "$withval" -ge 0 -o "$withval" -lt 0 2>&- ; then
|
||||
as_fn_error $? "Invalid argument --with-maxconn=$withval ... stopping" "$LINENO" 5
|
||||
else
|
||||
CFLAGS="$CFLAGS -DMAXCONN=$withval"
|
||||
fi
|
||||
fi
|
||||
|
||||
else
|
||||
|
||||
CFLAGS="$CFLAGS -DMAXCONN=16384"
|
||||
|
||||
|
||||
fi
|
||||
|
||||
|
||||
|
||||
#
|
||||
# Optionally specify the path to mysql_config
|
||||
#
|
||||
|
28
configure.in
28
configure.in
@ -188,6 +188,34 @@ AC_ARG_ENABLE(
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
# Optionally set the max number of network conenctions
|
||||
# the core will be support
|
||||
#
|
||||
AC_ARG_WITH(
|
||||
[maxconn],
|
||||
AC_HELP_STRING(
|
||||
[--with-maxconn@<:@=ARG@:>@],
|
||||
[optionally set the maximum connections the core can handle (default: 16384) NOT USED YET - EXPERIMENTAL]
|
||||
),
|
||||
[
|
||||
if test "$withval" == "no"; then
|
||||
CFLAGS="$CFLAGS -DMAXCONN=16384"
|
||||
else
|
||||
|
||||
if ! test "$withval" -ge 0 -o "$withval" -lt 0 2>&- ; then
|
||||
AC_MSG_ERROR([Invalid argument --with-maxconn=$withval ... stopping])
|
||||
else
|
||||
CFLAGS="$CFLAGS -DMAXCONN=$withval"
|
||||
fi
|
||||
fi
|
||||
],
|
||||
[
|
||||
CFLAGS="$CFLAGS -DMAXCONN=16384"
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
# Optionally specify the path to mysql_config
|
||||
#
|
||||
|
168
src/common/evdp.h
Normal file
168
src/common/evdp.h
Normal file
@ -0,0 +1,168 @@
|
||||
#ifndef _rA_EVDP_H_
|
||||
#define _rA_EVDP_H_
|
||||
|
||||
#include "../common/cbasetypes.h"
|
||||
|
||||
typedef struct EVDP_DATA EVDP_DATA;
|
||||
|
||||
|
||||
//#idef EVDP_EPOLL
|
||||
#include <sys/epoll.h>
|
||||
struct EVDP_DATA{
|
||||
struct epoll_event ev_data;
|
||||
bool ev_added;
|
||||
};
|
||||
//#endif
|
||||
|
||||
|
||||
enum EVDP_EVENTFLAGS{
|
||||
EVDP_EVENT_IN = 1, // Incomming data
|
||||
EVDP_EVENT_OUT = 2, // Connection accepts writing.
|
||||
EVDP_EVENT_HUP = 4 // Connection Closed.
|
||||
};
|
||||
|
||||
typedef struct EVDP_EVENT{
|
||||
int32 events; // due to performance reasons, this should be the first member.
|
||||
int32 fd; // Connection Identifier
|
||||
} EVDP_EVENT;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Network Event Dispatcher Initialization / Finalization routines
|
||||
*/
|
||||
void evdp_init();
|
||||
void evdp_final();
|
||||
|
||||
|
||||
/**
|
||||
* Will Wait for events.
|
||||
*
|
||||
* @param *out_ev pointer to array in size at least of max_events.
|
||||
* @param max_events max no of events to report with this call (coalesc)
|
||||
* @param timeout_ticks max time to wait in ticks (milliseconds)
|
||||
*
|
||||
* @Note:
|
||||
* The function will block until an event has occured on one of the monitored connections
|
||||
* or the timeout of timeout_ticks has passed by.
|
||||
* Upon successfull call (changed connections) this function will write the connection
|
||||
* Identifier & event to the out_fds array.
|
||||
*
|
||||
* @return 0 -> Timeout, > 0 no of changed connections.
|
||||
*/
|
||||
int32 evdp_wait(EVDP_EVENT *out_fds, int32 max_events, int32 timeout_ticks);
|
||||
|
||||
|
||||
/**
|
||||
* Applys the given mask on the given connection.
|
||||
*
|
||||
* @param fd connection identifier
|
||||
* @param *ep event data pointer for the connection
|
||||
* @param mask new event mask we're monitoring for.
|
||||
*/
|
||||
//void evdp_apply(int32 fd, EVDP_DATA *ep, int32 mask);
|
||||
|
||||
|
||||
/**
|
||||
* Adds a connection (listner) to the event notification system.
|
||||
*
|
||||
* @param fd connection identifier
|
||||
* @param *ep event data pointer for the connection
|
||||
*
|
||||
* @note:
|
||||
* Listener type sockets are edge triggered, (see epoll manual for more information)
|
||||
* - This basicaly means that youll receive one event, adn you have to accept until accept returns an error (nothing to accept)
|
||||
*
|
||||
* MONITORS by default: IN
|
||||
*
|
||||
* @return success indicator.
|
||||
*/
|
||||
bool evdp_addlistener(int32 fd, EVDP_DATA *ep);
|
||||
|
||||
/**
|
||||
* Adds a connection (client connectioN) to the event notification system
|
||||
*
|
||||
* @param fd connection identifier
|
||||
* @param *ep event data pointr for the connection
|
||||
*
|
||||
* @note:
|
||||
*
|
||||
* MONITORS by default: IN, HUP
|
||||
*
|
||||
* @return success indicator.
|
||||
*/
|
||||
bool evdp_addclient(int32 fd, EVDP_DATA *ep);
|
||||
|
||||
/**
|
||||
* Adds a connection (pending / outgoing connection!) to the event notification system.
|
||||
*
|
||||
* @param fd connection identifier
|
||||
* @param *ep event data pointer for the conneciton.
|
||||
*
|
||||
* @note:
|
||||
* Outgoing connection type sockets are getting monitored for connection established
|
||||
* successfull
|
||||
* - if the connection has been established - we're generitng a writable notification .. (send)
|
||||
* this is typical for BSD / posix conform network stacks.
|
||||
* - Additinionally its edge triggered.
|
||||
*
|
||||
* @see evdp_outgoingconnection_established
|
||||
*
|
||||
*
|
||||
* @return success indicator
|
||||
*/
|
||||
bool evdp_addconnecting(int32 fd, EVDP_DATA *ep);
|
||||
|
||||
/**
|
||||
* Adds an outgoing connection to the normal event notification system after it has been successfully established.
|
||||
*
|
||||
* @param fd connection identifier
|
||||
* @param *ep event data pointer for the conneciton.
|
||||
|
||||
* @note
|
||||
* after this call, its handled like a normal "client" connection (incomming)
|
||||
*
|
||||
* @rturn success indicator
|
||||
*/
|
||||
bool evdp_outgoingconnection_established(int32 fd, EVDP_DATA *ep);
|
||||
|
||||
/**
|
||||
* Marks a connection to be monitored for writable.
|
||||
*
|
||||
* @param fd connection identifier
|
||||
* @param *ep event data pointer for the connection
|
||||
*
|
||||
* @note:
|
||||
* the connection must be already added (as client or listener)
|
||||
*
|
||||
*
|
||||
* @return sucess indicator
|
||||
*/
|
||||
bool evdp_writable_add(int32 fd, EVDP_DATA *ep);
|
||||
|
||||
/**
|
||||
* Removes the connection from writable notification monitoring
|
||||
*
|
||||
* @param fd connection identifier
|
||||
* @param *ep event data pointr for the connection
|
||||
*
|
||||
*/
|
||||
void evdp_writable_remove(int32 fd, EVDP_DATA *ep);
|
||||
|
||||
/**
|
||||
* Removes an connectio from the event notification system.
|
||||
*
|
||||
* @param fd connection iditentfir
|
||||
* @param *ep event data pointer for th connection
|
||||
*
|
||||
*
|
||||
* @note:
|
||||
* this will also clear the given EVENT_DATA block
|
||||
* so the connection slot is in an "initial" blank status / ready to get reused.
|
||||
*
|
||||
*/
|
||||
void evdp_remove(int32 fd, EVDP_DATA *ep);
|
||||
|
||||
|
||||
|
||||
#endif
|
233
src/common/evdp_epoll.c
Normal file
233
src/common/evdp_epoll.c
Normal file
@ -0,0 +1,233 @@
|
||||
//
|
||||
// Event Dispatcher Abstraction for EPOLL
|
||||
//
|
||||
// Author: Florian Wilkemeyer <fw@f-ws.de>
|
||||
//
|
||||
// Copyright (c) rAthena Project (www.rathena.org) - Licensed under GNU GPL
|
||||
// For more information, see LICENCE in the main folder
|
||||
//
|
||||
//
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include "../common/cbasetypes.h"
|
||||
#include "../common/showmsg.h"
|
||||
#include "../common/evdp.h"
|
||||
|
||||
|
||||
#define EPOLL_MAX_PER_CYCLE 10 // Max Events to coalesc. per cycle.
|
||||
|
||||
|
||||
static int epoll_fd = -1;
|
||||
|
||||
|
||||
void evdp_init(){
|
||||
|
||||
epoll_fd = epoll_create( EPOLL_MAX_PER_CYCLE );
|
||||
if(epoll_fd == -1){
|
||||
ShowFatalError("evdp [EPOLL]: Cannot create event dispatcher (errno: %u / %s)\n", errno, strerror(errno) );
|
||||
exit(1);
|
||||
}
|
||||
|
||||
}//end: evdp_init()
|
||||
|
||||
|
||||
void evdp_final(){
|
||||
|
||||
if(epoll_fd != -1){
|
||||
close(epoll_fd);
|
||||
epoll_fd = -1;
|
||||
}
|
||||
|
||||
}//end: evdp_final()
|
||||
|
||||
|
||||
int32 evdp_wait(EVDP_EVENT *out_fds, int32 max_events, int32 timeout_ticks){
|
||||
struct epoll_event l_events[EPOLL_MAX_PER_CYCLE];
|
||||
register struct epoll_event *ev;
|
||||
register int nfds, n;
|
||||
|
||||
if(max_events > EPOLL_MAX_PER_CYCLE)
|
||||
max_events = EPOLL_MAX_PER_CYCLE;
|
||||
|
||||
nfds = epoll_wait( epoll_fd, l_events, max_events, timeout_ticks);
|
||||
if(nfds == -1){
|
||||
// @TODO: check if core is in shutdown mode. if - ignroe error.
|
||||
|
||||
ShowFatalError("evdp [EPOLL]: epoll_wait returned bad / unexpected status (errno: %u / %s)\n", errno, strerror(errno));
|
||||
exit(1); //..
|
||||
}
|
||||
|
||||
// Loop thru all events and copy it to the local ra evdp_event.. struct.
|
||||
for(n = 0; n < nfds; n++){
|
||||
ev = &l_events[n];
|
||||
|
||||
out_fds->fd = ev->data.fd;
|
||||
out_fds->events = 0; // clear
|
||||
|
||||
if(ev->events & EPOLLHUP)
|
||||
out_fds->events |= EVDP_EVENT_HUP;
|
||||
|
||||
if(ev->events & EPOLLIN)
|
||||
out_fds->events |= EVDP_EVENT_IN;
|
||||
|
||||
if(ev->events & EPOLLOUT)
|
||||
out_fds->events |= EVDP_EVENT_OUT;
|
||||
|
||||
out_fds++;
|
||||
}
|
||||
|
||||
return nfds; // 0 on timeout or > 0 ..
|
||||
}//end: evdp_wait()
|
||||
|
||||
|
||||
void evdp_remove(int32 fd, EVDP_DATA *ep){
|
||||
|
||||
if(ep->ev_added == true){
|
||||
|
||||
if( epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, &ep->ev_data) != 0){
|
||||
ShowError("evdp [EPOLL]: evdp_remove - epoll_ctl (EPOLL_CTL_DEL) failed! fd #%u (errno %u / %s)\n", fd, errno, strerror(errno));
|
||||
}
|
||||
|
||||
ep->ev_data.events = 0; // clear struct.
|
||||
ep->ev_data.data.fd = -1; // .. clear struct ..
|
||||
|
||||
ep->ev_added = false; // not added!
|
||||
}
|
||||
|
||||
|
||||
}//end: evdp_remove()
|
||||
|
||||
|
||||
bool evdp_addlistener(int32 fd, EVDP_DATA *ep){
|
||||
|
||||
ep->ev_data.events = EPOLLET|EPOLLIN;
|
||||
ep->ev_data.data.fd = fd;
|
||||
|
||||
// No check here for 'added ?'
|
||||
// listeners cannot be added twice.
|
||||
//
|
||||
if( epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ep->ev_data) != 0 ){
|
||||
ShowError("evdp [EPOLL]: evdp_addlistener - epoll_ctl (EPOLL_CTL_ADD) faield! fd #%u (errno %u / %s)\n", fd, errno, strerror(errno));
|
||||
ep->ev_data.events = 0;
|
||||
ep->ev_data.data.fd = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
ep->ev_added = true;
|
||||
|
||||
return true;
|
||||
}//end: evdp_addlistener()
|
||||
|
||||
|
||||
bool evdp_addclient(int32 fd, EVDP_DATA *ep){
|
||||
|
||||
ep->ev_data.events = EPOLLIN | EPOLLHUP;
|
||||
ep->ev_data.data.fd = fd;
|
||||
|
||||
// No check for "added?" here,
|
||||
// this function only gets called upon accpept.
|
||||
//
|
||||
|
||||
if( epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ep->ev_data) != 0){
|
||||
ShowError("evdp [EPOLL]: evdp_addclient - epoll_ctl (EPOLL_CTL_ADD) failed! fd #%u (errno %u / %s)\n", fd, errno, strerror(errno));
|
||||
ep->ev_data.events = 0;
|
||||
ep->ev_data.data.fd = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
ep->ev_added = true;
|
||||
|
||||
return true;
|
||||
}//end: evdp_addclient()
|
||||
|
||||
|
||||
bool evdp_addconnecting(int32 fd, EVDP_DATA *ep){
|
||||
|
||||
ep->ev_data.events = EPOLLET | EPOLLOUT | EPOLLHUP;
|
||||
ep->ev_data.data.fd = fd;
|
||||
|
||||
if( epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ep->ev_data) != 0){
|
||||
ShowError("evdp [EPOLL]: evdp_addconnecting - epoll_ctl (EPOLL_CTL_ADD) failed! fd #%u (errno %u / %s)\n", fd, errno, strerror(errno));
|
||||
ep->ev_data.events = 0;
|
||||
ep->ev_data.data.fd = -1;
|
||||
}
|
||||
|
||||
ep->ev_added = true;
|
||||
|
||||
return true;
|
||||
}//end: evdp_addconnecting()
|
||||
|
||||
|
||||
bool evdp_outgoingconnection_established(int32 fd, EVDP_DATA *ep){
|
||||
int32 saved_mask;
|
||||
|
||||
if(ep->ev_added != true){
|
||||
// !
|
||||
ShowError("evdp [EPOLL]: evdp_outgoingconnection_established fd #%u is not added to event dispatcher! invalid call.\n", fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
saved_mask = ep->ev_data.events;
|
||||
|
||||
ep->ev_data.events = EPOLLIN | EPOLLHUP;
|
||||
|
||||
if( epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &ep->ev_data) != 0){
|
||||
ep->ev_data.events = saved_mask; // restore old mask.
|
||||
ShowError("evdp [EPOLL]: evdp_outgoingconnection_established - epoll_ctl (EPOLL_CTL_MOD) failed! fd #%u (errno %u / %s)\n", fd, errno, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}//end: evdp_outgoingconnection_established()
|
||||
|
||||
|
||||
bool evdp_writable_add(int32 fd, EVDP_DATA *ep){
|
||||
|
||||
if(ep->ev_added != true){
|
||||
ShowError("evdp [EPOLL]: evdp_writable_add - tried to add not added fd #%u\n",fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(! (ep->ev_data.events & EPOLLOUT) ){ //
|
||||
|
||||
ep->ev_data.events |= EPOLLOUT;
|
||||
if( epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &ep->ev_data) != 0 ){
|
||||
ShowError("evdp [EPOLL]: evdp_writable_add - epoll_ctl (EPOLL_CTL_MOD) failed! fd #%u (errno: %u / %s)\n", fd, errno, strerror(errno));
|
||||
ep->ev_data.events &= ~EPOLLOUT; // remove from local flagmask due to failed syscall.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}//end: evdp_writable_add()
|
||||
|
||||
|
||||
void evdp_writable_remove(int32 fd, EVDP_DATA *ep){
|
||||
|
||||
if(ep->ev_added != true){
|
||||
ShowError("evdp [EPOLL]: evdp_writable_remove - tried to remove not added fd #%u\n", fd);
|
||||
return;
|
||||
}
|
||||
|
||||
if( ep->ev_data.events & EPOLLOUT ){
|
||||
|
||||
ep->ev_data.events &= ~EPOLLOUT;
|
||||
if( epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &ep->ev_data) != 0){
|
||||
ShowError("evdp [EPOLL]: evdp_writable_remove - epoll_ctl (EPOLL_CTL_MOD) failed! fd #%u (errno %u / %s)\n", fd, errno, strerror(errno));
|
||||
ep->ev_data.events |= EPOLLOUT; // add back to local flagmask because of failed syscall.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}//end: evdp_writable_remove()
|
||||
|
221
src/common/netbuffer.c
Normal file
221
src/common/netbuffer.c
Normal file
@ -0,0 +1,221 @@
|
||||
|
||||
//
|
||||
// Network Buffer Subsystem (iobuffer)
|
||||
//
|
||||
//
|
||||
// Author: Florian Wilkemeyer <fw@f-ws.de>
|
||||
//
|
||||
// Copyright (c) rAthena Project (www.rathena.org) - Licensed under GNU GPL
|
||||
// For more information, see LICENCE in the main folder
|
||||
//
|
||||
//
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "../common/cbasetypes.h"
|
||||
#include "../common/atomic.h"
|
||||
#include "../common/mempool.h"
|
||||
#include "../common/showmsg.h"
|
||||
#include "../common/raconf.h"
|
||||
#include "../common/thread.h"
|
||||
#include "../common/malloc.h"
|
||||
#include "../common/core.h"
|
||||
|
||||
#include "../common/netbuffer.h"
|
||||
|
||||
|
||||
//
|
||||
// Buffers are available in the following sizes:
|
||||
// 48, 192, 2048, 8192
|
||||
// 65536 (inter server connects may use it for charstatus struct..)
|
||||
//
|
||||
|
||||
|
||||
///
|
||||
// Implementation:
|
||||
//
|
||||
static volatile int32 l_nEmergencyAllocations = 0; // stats.
|
||||
static sysint l_nPools = 0;
|
||||
static sysint *l_poolElemSize = NULL;
|
||||
static mempool *l_pool = NULL;
|
||||
|
||||
|
||||
void netbuffer_init(){
|
||||
char localsection[32];
|
||||
raconf conf;
|
||||
sysint i;
|
||||
|
||||
// Initialize Statistic counters:
|
||||
l_nEmergencyAllocations = 0;
|
||||
|
||||
// Set localsection name according to running serverype.
|
||||
switch(SERVER_TYPE){
|
||||
case ATHENA_SERVER_LOGIN: strcpy(localsection, "login-netbuffer"); break;
|
||||
case ATHENA_SERVER_CHAR: strcpy(localsection, "char-netbuffer"); break;
|
||||
case ATHENA_SERVER_INTER: strcpy(localsection, "inter-netbuffer"); break;
|
||||
case ATHENA_SERVER_MAP: strcpy(localsection, "map-netbuffer"); break;
|
||||
default: strcpy(localsection, "unsupported_type"); break;
|
||||
}
|
||||
|
||||
|
||||
conf = raconf_parse("conf/network.conf");
|
||||
if(conf == NULL){
|
||||
ShowFatalError("Failed to Parse required Configuration (conf/network.conf)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Get Values from config file
|
||||
l_nPools = (sysint)raconf_getintEx(conf, localsection, "netbuffer", "num", 0);
|
||||
if(l_nPools == 0){
|
||||
ShowFatalError("Netbuffer (network.conf) failure - requires at least 1 Pool.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Allocate arrays.
|
||||
l_poolElemSize = (sysint*)aCalloc( l_nPools, sizeof(sysint) );
|
||||
l_pool = (mempool*)aCalloc( l_nPools, sizeof(mempool) );
|
||||
|
||||
|
||||
for(i = 0; i < l_nPools; i++){
|
||||
int64 num_prealloc, num_realloc;
|
||||
char key[32];
|
||||
|
||||
sprintf(key, "pool_%u_size", (uint32)i+1);
|
||||
l_poolElemSize[i] = (sysint)raconf_getintEx(conf, localsection, "netbuffer", key, 4096);
|
||||
if(l_poolElemSize[i] < 32){
|
||||
ShowWarning("Netbuffer (network.conf) failure - minimum allowed buffer size is 32 byte) - fixed.\n");
|
||||
l_poolElemSize[i] = 32;
|
||||
}
|
||||
|
||||
sprintf(key, "pool_%u_prealloc", (uint32)i+1);
|
||||
num_prealloc = raconf_getintEx(conf, localsection, "netbuffer", key, 150);
|
||||
|
||||
sprintf(key, "pool_%u_realloc_step", (uint32)i+1);
|
||||
num_realloc = raconf_getintEx(conf, localsection, "netbuffer", key, 100);
|
||||
|
||||
// Create Pool!
|
||||
sprintf(key, "Netbuffer %u", (uint32)l_poolElemSize[i]); // name.
|
||||
|
||||
// Info
|
||||
ShowInfo("NetBuffer: Creating Pool %u (Prealloc: %u, Realloc Step: %u) - %0.2f MiB\n", l_poolElemSize[i], num_prealloc, num_realloc, (float)((sizeof(struct netbuf) + l_poolElemSize[i] - 32)* num_prealloc)/1024.0f/1024.0f);
|
||||
|
||||
//
|
||||
// Size Calculation:
|
||||
// struct netbuf + requested buffer size - 32 (because the struct already contains 32 byte buffer space at the end of struct)
|
||||
l_pool[i] = mempool_create(key, (sizeof(struct netbuf) + l_poolElemSize[i] - 32), num_prealloc, num_realloc, NULL, NULL);
|
||||
if(l_pool[i] == NULL){
|
||||
ShowFatalError("Netbuffer: cannot create Pool for %u byte buffers.\n", l_poolElemSize[i]);
|
||||
// @leak: clean everything :D
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
}//
|
||||
|
||||
|
||||
raconf_destroy(conf);
|
||||
|
||||
}//end: netbuffer_init()
|
||||
|
||||
|
||||
void netbuffer_final(){
|
||||
sysint i;
|
||||
|
||||
if(l_nPools > 0){
|
||||
/// .. finalize mempools
|
||||
for(i = 0; i < l_nPools; i++){
|
||||
mempool_stats stats = mempool_get_stats(l_pool[i]);
|
||||
|
||||
ShowInfo("Netbuffer: Freeing Pool %u (Peak Usage: %u, Realloc Events: %u)\n", l_poolElemSize[i], stats.peak_nodes_used, stats.num_realloc_events);
|
||||
|
||||
mempool_destroy(l_pool[i]);
|
||||
}
|
||||
|
||||
if(l_nEmergencyAllocations > 0){
|
||||
ShowWarning("Netbuffer: did %u Emergency Allocations, please tune your network.conf!\n", l_nEmergencyAllocations);
|
||||
l_nEmergencyAllocations = 0;
|
||||
}
|
||||
|
||||
aFree(l_poolElemSize); l_poolElemSize = NULL;
|
||||
aFree(l_pool); l_pool = NULL;
|
||||
l_nPools = 0;
|
||||
}
|
||||
|
||||
|
||||
}//end: netbuffer_final()
|
||||
|
||||
|
||||
netbuf netbuffer_get( sysint sz ){
|
||||
sysint i;
|
||||
netbuf nb = NULL;
|
||||
|
||||
// Search an appropriate pool
|
||||
for(i = 0; i < l_nPools; i++){
|
||||
if(sz <= l_poolElemSize[i]){
|
||||
// match
|
||||
|
||||
nb = (netbuf)mempool_node_get(l_pool[i]);
|
||||
nb->pool = i;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// No Bufferpool found that mets there quirements?.. (thats bad..)
|
||||
if(nb == NULL){
|
||||
ShowWarning("Netbuffer: get(%u): => no appropriate pool found - emergency allocation required.\n", sz);
|
||||
ShowWarning("Please reconfigure your network.conf!");
|
||||
|
||||
InterlockedIncrement(&l_nEmergencyAllocations);
|
||||
|
||||
// .. better to check (netbuf struct provides 32 byte bufferspace itself.
|
||||
if(sz < 32) sz = 32;
|
||||
|
||||
// allocate memory using malloc ..
|
||||
while(1){
|
||||
nb = (netbuf) aMalloc( (sizeof(struct netbuf) + sz - 32) );
|
||||
if(nb != NULL){
|
||||
memset(nb, 0x00, (sizeof(struct netbuf) + sz - 32) ); // zero memory! (to enforce commit @ os.)
|
||||
nb->pool = -1; // emergency alloc.
|
||||
break;
|
||||
}
|
||||
|
||||
rathread_yield();
|
||||
}// spin allocation.
|
||||
|
||||
}
|
||||
|
||||
|
||||
nb->refcnt = 1; // Initial refcount is 1
|
||||
|
||||
return nb;
|
||||
}//end: netbuffer_get()
|
||||
|
||||
|
||||
void netbuffer_put( netbuf nb ){
|
||||
|
||||
// Decrement reference counter, if > 0 do nothing :)
|
||||
if( InterlockedDecrement(&nb->refcnt) > 0 )
|
||||
return;
|
||||
|
||||
// Is this buffer an emergency allocated buffer?
|
||||
if(nb->pool == -1){
|
||||
aFree(nb);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Otherwise its a normal mempool based buffer
|
||||
// return it to the according mempool:
|
||||
mempool_node_put( l_pool[nb->pool], nb);
|
||||
|
||||
|
||||
}//end: netbuffer_put()
|
||||
|
||||
|
||||
void netbuffer_incref( netbuf nb ){
|
||||
|
||||
InterlockedIncrement(&nb->refcnt);
|
||||
|
||||
}//end: netbuf_incref()
|
83
src/common/netbuffer.h
Normal file
83
src/common/netbuffer.h
Normal file
@ -0,0 +1,83 @@
|
||||
// Copyright (c) rAthena Project (www.rathena.org) - Licensed under GNU GPL
|
||||
// For more information, see LICENCE in the main folder
|
||||
|
||||
#ifndef _rA_NETBUFFER_H_
|
||||
#define _rA_NETBUFFER_H_
|
||||
|
||||
#include "../common/cbasetypes.h"
|
||||
|
||||
typedef struct netbuf{
|
||||
sysint pool; // The pool ID this buffer belongs to,
|
||||
// is set to -1 if its an emergency allocated buffer
|
||||
|
||||
struct netbuf *next; // Used by Network system.
|
||||
|
||||
volatile int32 refcnt; // Internal Refcount, it gets lowered every call to netbuffer_put,
|
||||
// if its getting zero, the buffer will returned back to the pool
|
||||
// and can be reused.
|
||||
|
||||
int32 dataPos; // Current Offset
|
||||
// Used only for Reading (recv job)
|
||||
// write cases are using the sessions local datapos member due to
|
||||
// shared write buffer support.
|
||||
|
||||
int32 dataLen; // read buffer case:
|
||||
// The length expected to read to.
|
||||
// when this->dataPos == dateLen, read job has been completed.
|
||||
// write buffer case:
|
||||
// The lngth of data in te buffer
|
||||
// when s->dataPos == dataLen, write job has been completed
|
||||
//
|
||||
// Note:
|
||||
// leftBytes = (dateLen - dataPos)
|
||||
//
|
||||
// Due to shared buffer support
|
||||
// dataPos gets not used in write case (each connection has its local offset)
|
||||
//
|
||||
|
||||
// The Bufferspace itself.
|
||||
char buf[32];
|
||||
} *netbuf;
|
||||
|
||||
|
||||
void netbuffer_init();
|
||||
void netbuffer_final();
|
||||
|
||||
/**
|
||||
* Gets a netbuffer that has atleast (sz) byes space.
|
||||
*
|
||||
* @note: The netbuffer system guarantees that youll always recevie a buffer.
|
||||
* no check for null is required!
|
||||
*
|
||||
* @param sz - minimum size needed.
|
||||
*
|
||||
* @return pointer to netbuf struct
|
||||
*/
|
||||
netbuf netbuffer_get( sysint sz );
|
||||
|
||||
|
||||
/**
|
||||
* Returns the given netbuffer (decreases refcount, if its 0 - the buffer will get returned to the pool)
|
||||
*
|
||||
* @param buf - the buffer to return
|
||||
*/
|
||||
void netbuffer_put( netbuf buf );
|
||||
|
||||
|
||||
/**
|
||||
* Increases the Refcount on the given buffer
|
||||
* (used for areasends .. etc)
|
||||
*
|
||||
*/
|
||||
void netbuffer_incref( netbuf buf );
|
||||
|
||||
|
||||
// Some Useful macros
|
||||
#define NBUFP(netbuf,pos) (((uint8*)(netbuf->buf)) + (pos))
|
||||
#define NBUFB(netbuf,pos) (*(uint8*)((netbuf->buf) + (pos)))
|
||||
#define NBUFW(netbuf,pos) (*(uint16*)((netbuf->buf) + (pos)))
|
||||
#define NBUFL(netbuf,pos) (*(uint32*)((netbuf->buf) + (pos)))
|
||||
|
||||
|
||||
|
||||
#endif
|
1062
src/common/network.c
Normal file
1062
src/common/network.c
Normal file
File diff suppressed because it is too large
Load Diff
189
src/common/network.h
Normal file
189
src/common/network.h
Normal file
@ -0,0 +1,189 @@
|
||||
#ifndef _rA_NETWORK_H_
|
||||
#define _rA_NETWORK_H_
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include "../common/cbasetypes.h"
|
||||
#include "../common/netbuffer.h"
|
||||
#include "../common/evdp.h"
|
||||
|
||||
#ifndef MAXCONN
|
||||
#define MAXCONN 16384
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct SESSION{
|
||||
EVDP_DATA evdp_data; // Must be always the frist member! (some evdp's may rely on this fact)
|
||||
|
||||
// Connection Type
|
||||
enum{ NST_FREE=0, NST_LISTENER = 1, NST_CLIENT=2, NST_OUTGOING=3} type;
|
||||
|
||||
// Flags / Settings.
|
||||
bool v6; // is v6?
|
||||
bool disconnect_in_progress; // To prevent stack overflows / recursive calls.
|
||||
|
||||
|
||||
union{ // union to save memory.
|
||||
struct sockaddr_in v4;
|
||||
struct sockaddr_in6 v6;
|
||||
}addr;
|
||||
|
||||
|
||||
// "lowlevel" Handlers
|
||||
// (Implemented by the protocol specific parser)
|
||||
//
|
||||
bool (*onRecv)(int32 fd); // return false = disconnect
|
||||
bool (*onSend)(int32 fd); // return false = disconnect
|
||||
|
||||
// Event Handlers for LISTENER type sockets
|
||||
//
|
||||
// onConnect gets Called when a connection has been
|
||||
// successfully accepted.
|
||||
// Session entry is available in this Handler!
|
||||
// A returncode of false will reejct the connection (disconnect)
|
||||
// Note: When rejecting a connection in onConnect by returning false
|
||||
// The onDisconnect handler wont get called!
|
||||
// Note: the onConnect Handler is also responsible for setting
|
||||
// the appropriate netparser (which implements onRecv/onSend..) [protocol specific]
|
||||
//
|
||||
// onDisconnect gets called when a connection gets disconnected
|
||||
// (by peer as well as by core)
|
||||
//
|
||||
bool (*onConnect)(int32 fd); // return false = disconnect (wont accept)
|
||||
void (*onDisconnect)(int32 fd);
|
||||
|
||||
|
||||
//
|
||||
// Parser specific data
|
||||
//
|
||||
void *netparser_data; // incase of RO Packet Parser, pointer to packet len table (uint16array)
|
||||
void (*onPacketComplete)(int32 fd, uint16 op, uint16 len, netbuf buf);
|
||||
|
||||
|
||||
//
|
||||
// Buffers
|
||||
//
|
||||
struct{
|
||||
enum NETREADSTATE { NRS_WAITOP = 0, NRS_WAITLEN = 1, NRS_WAITDATA = 2} state;
|
||||
|
||||
uint32 head_left;
|
||||
uint16 head[2];
|
||||
|
||||
netbuf buf;
|
||||
} read;
|
||||
|
||||
struct{
|
||||
uint32 max_outstanding;
|
||||
uint32 n_outstanding;
|
||||
|
||||
uint32 dataPos;
|
||||
|
||||
netbuf buf, buf_last;
|
||||
} write;
|
||||
|
||||
// Application Level data Pointer
|
||||
// (required for backward compatibility with previous athena socket system.)
|
||||
void *data;
|
||||
|
||||
} SESSION;
|
||||
|
||||
|
||||
/**
|
||||
* Subsystem Initialization / Finalization.
|
||||
*
|
||||
*/
|
||||
void network_init();
|
||||
void network_final();
|
||||
|
||||
|
||||
/**
|
||||
* Will do the net work :) ..
|
||||
*/
|
||||
void network_do();
|
||||
|
||||
|
||||
/**
|
||||
* Adds a new listner.
|
||||
*
|
||||
* @param v6 v6 listner?
|
||||
* @param *addr the address to listen on.
|
||||
* @param port port to listen on
|
||||
*
|
||||
* @return -1 on error otherwise the identifier of the new listener.
|
||||
*/
|
||||
int32 network_addlistener(bool v6, const char *addr, uint16 port);
|
||||
|
||||
|
||||
/**
|
||||
* Tries to establish an outgoing connection.
|
||||
*
|
||||
* @param v6 operate with IPv6 addresses?
|
||||
* @param addr the address to connect to
|
||||
* @param port the port to connect to
|
||||
* @param from_addr the address to connect from (local source / optional if auto -> NULL)
|
||||
* @param from_port the port to connect from (local source / optional if auto -> 0)
|
||||
* @param onConnectionEstablishedHandler callback that gets called when the connection is established.
|
||||
* @param onConnectionLooseHandler callback that gets called when the connection gets disconnected (or the connection couldnt be established)
|
||||
*
|
||||
* @return -1 on error otherwise the identifier of the new connection
|
||||
*/
|
||||
int32 network_connect(bool v6,
|
||||
const char *addr,
|
||||
uint16 port,
|
||||
const char *from_addr,
|
||||
uint16 from_port,
|
||||
bool (*onConnectionEstablishedHandler)(int32 fd),
|
||||
void (*onConnectionLooseHandler)(int32 fd)
|
||||
);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Disconnects the given connection
|
||||
*
|
||||
* @param fd connection identifier.
|
||||
*
|
||||
* @Note:
|
||||
* - onDisconnect callback gets called!
|
||||
* - cleares (returns) all assigned buffers
|
||||
*
|
||||
*/
|
||||
void network_disconnect(int32 fd);
|
||||
|
||||
|
||||
/**
|
||||
* Attach's a netbuffer at the end of sending queue to the given connection
|
||||
*
|
||||
* @param fd connection identifier
|
||||
* @param buf netbuffer to attach.
|
||||
*/
|
||||
void network_send(int32 fd, netbuf buf);
|
||||
|
||||
|
||||
/**
|
||||
* Sets the parser to RO Protocol like Packet Parser.
|
||||
*
|
||||
* @param fd connection identifier
|
||||
* @param *packetlentable pointer to array of uint16 in size of UINT16_MAX,
|
||||
* @param onComplteProc callback for packet completion.
|
||||
*
|
||||
* @note:
|
||||
* PacketLen Table Fromat:
|
||||
* each element's offsets represents th ro opcode.
|
||||
* value is length.
|
||||
* a length of 0 means the packet is dynamic.
|
||||
* a length of UINT16_MAX means the packet is unknown.
|
||||
*
|
||||
* Static Packets must contain their hader in len so (0x64 == 55 ..)
|
||||
*
|
||||
*/
|
||||
void network_parser_set_ro(int32 fd,
|
||||
int16 *packetlentable,
|
||||
void (*onPacketCompleteProc)(int32 fd, uint16 op, uint16 len, netbuf buf)
|
||||
);
|
||||
#define ROPACKET_UNKNOWN UINT16_MAX
|
||||
#define ROPACKET_DYNLEN 0
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
Loading…
x
Reference in New Issue
Block a user