AlliedModders

AlliedModders (https://forums.alliedmods.net/index.php)
-   Module Coding (https://forums.alliedmods.net/forumdisplay.php?f=9)
-   -   Module: Threaded Sockets (https://forums.alliedmods.net/showthread.php?t=262924)

Shooting King 05-15-2015 13:30

Module: Threaded Sockets
 
7 Attachment(s)
Threaded Sockets - v1.1(1 June, 2016)

Description:
This module is a modification of Default Amxx Sockets module, implementing option for performing threaded socket operations. This Module also include changes done in Sockets2(Bugsy) and Socket_hz in a threaded mode. This module eliminates looping, hidden looping, entity thinks in plugin as well as in the main thread.
Natives:
A list of all natives.
PHP Code:

native socket_open(const _hostname[], _port_protocol SOCKET_TCP, &_error);
native socket_close(_socket);
native socket_recv(_socket_data[], _length);
native socket_send(_socket, const _data[], _length);
native socket_send2(_socket, const _data[], _length);
native socket_change(_socket_timeout=100000);

/**
 * Threaded Natives
*/

native socket_create_t();
native socket_open_t( const iThreadHandle, const szHostname[], const port, const protocol, const CallBackHandler[] );
native socket_send_t( const iThreadHandle, const szData[], const iDataLen, const CallBackHandler[] );
native socket_recv_t( const iThreadHandle, const CallBackHandler[], const iRecvDataLen );
native socket_close_t( const iThreadHandle, const CallBackHandler[] );
native socket_destroy_t( const iThreadHandle );
native socket_get_last_error_t( const iThreadHandle );
native socket_listen_t( const iThreadHandle, const szHostname[], const port, const protocol, const CallBackHandler[] );
native socket_accept_t( const iThreadHandle, const CallBackHandler[] );
native socket_get_sd_t( const iThreadHandle );
native socket_set_sd_t( const iThreadHandle, const iSocketDescriptor ); 

1. socket_create_t()
PHP Code:

/**
 * Spawns a new Thread.
 * returns a ThreadHandle (positive) or negative on error (Could not create a Thread).
 * The ThreadHandle is to be used for all other *_t natives.
*/
native socket_create_t(); 

2. socket_open_t()
PHP Code:

/** 
 * Sets the passed data to thread object and puts the Thread is STATE_CONNECT which 
 * opens a new connection to hostname:port via protocol (either SOCKET_TCP or SOCKET_UDP).
 * 
 * @param iThreadHandle              A ThreadHandle returned by socket_create_t().
 * @param szHostname                 A string containing ip or domain name to be connected to.
 * @param port                       An integer representing the port to which the connection is to be made.
 * @param protocol                   An integer specifying the protocol, 
 *                                        @arg @c SOCKET_TCP for TCP.
 *                                        @arg @c SOCKET_UDP for UDP.
 * @param CallBackHandler            A string containing name of a Public function to be registered as callback.
 * 
 * @fn public CallBackHandler( iThreadState, iReturn )
 * @param iThreadState               STATE_CONNECT in this case.
 * @param iReturn                    0 On Success and Negative for Error (Described above).
 *                                   Call socket_get_last_error_t() to get more specific errorno from OS APIs.
 * 
 * @note ThreadState is set to STATE_IDLE before CallBack. 
 *
 * @return                           0 on Success, negative on error (SOCKET_ERROR on invalid iThreadHandle and SOCKET_COULDNOT_FIND_CLLBKFUNC).
*/
native socket_open_t( const iThreadHandle, const szHostname[], const port, const protocol, const CallBackHandler[] );/ 

3. socket_send_t()
PHP Code:

/**
 * Set the required data to Thread object, Set its state to STATE_SEND to send data via a socket .
 * 
 * @param iThreadHandle              A ThreadHandle returned by socket_create_t().
 * @param szData                     A String containing the data to be sent, can contain null characters.
 * @param iDataLen                   Length of the szData array.
 * @param CallBackHandler            A string containing name of a Public function to be registered as callback.
 * 
 * @fn public CallBackHandler( iThreadState, iReturn )
 * @param iThreadState               STATE_SEND in this case.
 * @param iReturn                    Positive on Success (Length of the data sent) and SOCKET_ERROR on error.
 *                                   Call socket_get_last_error_t() to get more specific errorno from OS APIs.
 * 
 * @note ThreadState is set to STATE_IDLE before CallBack. 
 * 
 * @return                           0 on Success, negative on error (SOCKET_ERROR on invalid iThreadHandle and SOCKET_COULDNOT_FIND_CLLBKFUNC).
*/
native socket_send_t( const iThreadHandle, const szData[], const iDataLen, const CallBackHandler[] ); 

4. socket_recv_t()
PHP Code:

/**
 * Set the required data to Thread object, Set its state to STATE_READ to recieve data from a socket.
 * 
 * @param iThreadHandle            A ThreadHandle returned by socket_create_t().
 * @param iRecvDataLen                Length of the data that must be recieved at a time.
 * @param CallBackHandler            A string containing name of a Public function to be registered as callback.
 * 
 * @fn public CallBackHandler( iThreadState, iReturn, szRecvData[], iRecvDataLen )
 * @param iThreadState                STATE_READ in this case.
 * @param iReturn                 Positive on successful read (Length of the data actually read), 0 on Connection close and
 *                                 SOCKET_ERROR on error. Call socket_get_last_error_t() to get more specific errorno from OS APIs.
 * @param szRecvData[]                A null-terminated string containing the recieved data.
 * @param iRecvDataLen                The length requested by plugin to be read. Actual RecvDataLen may be less than requested. 
 *                                 This is the same length passed to the native.
 * @return                        If a number greater than 0 is returned by callback, iRecvDataLen is dynamically set to that number.
 *
 * @note ThreadState is set to STATE_IDLE, only when the connection is closed or on error, before CallBack. Therefore the CallBack may
 * be called more than one time passing blocks of szRecvData with length less than or equeal to iRecvDataLen.
 * 
 * @return                         0 on Success, negative on error (SOCKET_ERROR on invalid iThreadHandle and SOCKET_COULDNOT_FIND_CLLBKFUNC)
*/
native socket_recv_t( const iThreadHandle, const CallBackHandler[], const iRecvDataLen ); 

5. socket_close_t()
PHP Code:

/**
 * Close the Socket.
 * 
 * @param iThreadHandle              A ThreadHandle returned by socket_create_t().
 * @param CallBackHandler            A string containing name of a Public function to be registered as callback.
 * 
 * @fn public CallBackHandler( iThreadState, iReturn )
 * @param iThreadState               STATE_DISCONNECT in this case.
 * @param iReturn                    0 on successful close and SOCKET_ERROR on error.
 *                                   Call socket_get_last_error_t() to get more specific errorno from OS APIs.
 * 
 * @note ThreadState is set to STATE_IDLE before CallBack. 
 * @note This function only closes the Socket not the thread. So that the thread can be reused for another new socket communication.
 * 
 * @return                           0 on Success, negative on error (SOCKET_ERROR on invalid iThreadHandle and SOCKET_COULDNOT_FIND_CLLBKFUNC)
 * 
*/
native socket_close_t( const iThreadHandle, const CallBackHandler[] ); 

6. socket_destroy_t()
PHP Code:

/**
 * Destroyes the Thread aswell as the Socket by putting the thread is STATE_DESTROY. 
 * 
 * @param iThreadHandle              A ThreadHandle returned by socket_create_t().
 * 
 * @note After destroying no futher references should be made with iThreadHandle, Doing so may result is SEGSIGV faults.
 * The iThreadHandle must be a vaild iThreadHandle, if not, again may result in SEGSIGV faults. So you have to be more careful
 * while using this function. Best way to use this function is,
 * 
 * Example :
 * if( g_iThreadHandle )
 *                     socket_destroy_t(g_iThreadHandle);
 * 
 * @return                           0
*/
native socket_destroy_t( const iThreadHandle ); 

7. socket_get_last_error_t()
PHP Code:

/**
 * Get last error from the OS Socket APIs.
 * 
 * @param iThreadHandle              A ThreadHandle returned by socket_create_t().
 * @return                           ErrorCode of the error.
*/
native socket_get_last_error_t( const iThreadHandle ); 

List of All ErrorCodes returned by this native:
On Windows

On Linux

8. socket_listen_t()
PHP Code:

/** 
 * Sets the passed data to thread object and puts the Thread in STATE_LISTEN which 
 * puts the socket is listen mode for a new connection to localip:port via protocol (either SOCKET_TCP or SOCKET_UDP).
 * 
 * @param iThreadHandle              A ThreadHandle returned by socket_create_t().
 * @param szHostname                 A string containing ip or domain name to be connected to.
 * @param port                       An integer representing the port to which the connection is to be made.
 * @param protocol                   An integer specifying the protocol, 
 *                                        @arg @c SOCKET_TCP for TCP.
 *                                        @arg @c SOCKET_UDP for UDP.
 * @param CallBackHandler            A string containing name of a Public function to be registered as callback.
 * 
 * @fn public CallBackHandler( iThreadState, iReturn )
 * @param iThreadState               STATE_LISTEN in this case.
 * @param iReturn                    0 On Success and Negative for Error (Described above).
 *                                   Call socket_get_last_error_t() to get more specific errorno from OS APIs.
 * 
 * @note ThreadState is set to STATE_IDLE before CallBack. 
 *
 * @return                           0 on Success, negative on error (SOCKET_ERROR on invalid iThreadHandle and SOCKET_COULDNOT_FIND_CLLBKFUNC)
*/
native socket_listen_t( const iThreadHandle, const szHostname[], const port, const protocol, const CallBackHandler[] ); 

9. socket_accept_t()
PHP Code:

/**
 * Registers the CallBackHandler and puts the Thread in STATE_ACCEPT state which
 * will try to accept ONE connection. Therefore, if one needs to accept more connections, this function must again
 * be called in CallBackHandler. The maximum length of the queue of pending connections is 10. 11th client will
 * recieve 'Connection Refused' error.
 * 
 * @param iThreadHandle              A ThreadHandle returned by socket_create_t().
 * @param CallBackHandler            A string containing name of a Public function to be registered as callback.
 * 
 * @fn public CallBackHandler( iThreadState, iReturn, szClientAddr[], iClientAddrLen, clientport )
 * @param iThreadState               STATE_ACCEPT in this case.
 * @param iReturn                    SocketDescriptor on Success and SOCKET_ERROR on error.
 *                                   Call socket_get_last_error_t() to get more specific errorno from OS APIs.
 * @param szClientAddr               On Success, This is a string containing the ip of the new accepted client.
 * @param iClientAddrLen             On Success, This is the length of ClientAddr.
 * @param clientport                 On Success, This will represent the clientport.
 * 
 * @note ThreadState is set to STATE_IDLE, after accepting on client connection, before CallBack. 
 * 
 * @return                           0 on Success, negative on error (SOCKET_ERROR on invalid iThreadHandle and SOCKET_COULDNOT_FIND_CLLBKFUNC).
*/
native socket_accept_t( const iThreadHandle, const CallBackHandler[] ); 

10. socket_get_sd_t()
PHP Code:

/**
 * Get SocketDescriptor of the iThreadHandle passed.
 * 
 * @param iThreadHandle              A ThreadHandle returned by socket_create_t().
 * 
 * @return                           Returns iSocketDescriptor of iThreadHandle.
*/
native socket_get_sd_t( const iThreadHandle ); 

11. socket_set_sd_t()
PHP Code:

/**
 * Set SocketDescriptor of the iThreadHandle passed.
 * 
 * @param iThreadHandle              A ThreadHandle returned by socket_create_t().
 * @param iSocketDescriptor          A SocketDescriptor that must be used by the iThreadHandle obj.
 * 
 * @return                           0
*/
native socket_set_sd_t( const iThreadHandle, const iSocketDescriptor ); 

Examples:
Example 1 : Sockets for Downloading a HTTP page.
PHP Code:

#include <amxmodx>
#include <sockets>

new g_iThreadHandle;
new 
pCvar_HostNamepCvar_Page;
new 
g_File;

public 
plugin_init()
{
    
register_plugin"Threaded Sockets Testing""1.0""Shooting King" );
    
register_concmd"say /test" "TestCmd" ); 
    
register_concmd"/test" "TestCmd" );
    
    
pCvar_HostName register_cvar"sk_ts_hostname""example.com" );
    
pCvar_Page register_cvar"sk_ts_page""/" );
}

public 
TestCmd()
{
    
g_iThreadHandle socket_create_t();
    
    new 
szHostName[64];
    
get_pcvar_stringpCvar_HostNameszHostName64 );
    
    if((
socket_open_t(g_iThreadHandleszHostName80SOCKET_TCP"CBOpenSocket")) < )
    {
        
log_amx"ERROR: Could not open Socket." );
        return;
    }
    
log_amx"Trying to open connection..." );
}

public 
CBOpenSocketiThreadStateiReturn )
{
    if( 
iReturn )
    {
        
log_amx"ERROR: Could not open Socket. Error[%d] : %d"iReturnsocket_get_last_error_t(g_iThreadHandle) );
    }
    else
    {        
        new 
szPacket[128]; 
        new 
szHostName[64], szPage[64];
        
get_pcvar_stringpCvar_HostNameszHostName64 );
        
get_pcvar_stringpCvar_PageszPage64 );
        
        
formatexszPacket127"GET %s HTTP/1.1^r^nHost: %s^r^n^r^n"szPageszHostName );
        
        
log_amx"Socket Opened. Trying to send Data..." );
        if((
socket_send_tg_iThreadHandleszPacketsizeof(szPacket), "CBSendSocket")) < )
        {
            
log_amx"ERROR: Could not Send data." );
        }
    }
}

public 
CBSendSocketiThreadStateiReturn )
{
    if( 
iReturn )
    {
        
log_amx"ERROR: Error Sending Data. Error[%d] : %d"iReturnsocket_get_last_error_t(g_iThreadHandle) );
    }
    else
    {
        
log_amx"Socket Opened. Data Sent. Trying to receive Data..." );
        if( (
socket_recv_tg_iThreadHandle"CBRecvSocket"64 )) < )
        {
            
log_amx"ERROR: Could receive data." );
        }
        
g_File fopen"test.html""w" );
    }
}

public 
CBRecvSocketiThreadStateiReturnszRecvData[], iRecvDataLen )
{
    if( 
iReturn == )
    {
        
log_amx"Connection gracefully closed." );
        
socket_close_t(g_iThreadHandle"CBCloseSocket");
        
fcloseg_File );
    }
    else if( 
iReturn )
    {
        
log_amx"ERROR: Error while receiveing data. Error[%d] : %d"iReturnsocket_get_last_error_t(g_iThreadHandle) );
        
socket_close_t(g_iThreadHandle"CBCloseSocket");
    }
    else
    {
        
log_amx"Successfully Received Data[%d] : Received - %d "iRecvDataLenszRecvDatastrlen(szRecvData));
        
fprintfg_FileszRecvData );
    }
}

public 
CBCloseSocketiThreadStateiReturn )
{
    if( 
iReturn == )
    {
        
log_amx"Socket Closed. Destroying Thread." );
        
socket_destroy_t(g_iThreadHandle);
    }
    else
    {
        
log_amx"Error Closing socket.  Errno : %d"socket_get_last_error_t(g_iThreadHandle) );
    }


Example 2 : Listening and Accepting connections on specific IP and Port.
PHP Code:

#include <amxmodx>
#include <sockets>

new const szListenAddr[] = "192.168.2.2";
new const 
iListenPort 79;

new 
g_iThreadHandle 0g_ClientSThread 0;

public 
plugin_init()
{
    
register_plugin"Threaded Sockets (L/C) Testing""1.0""Shooting King" );
    
register_concmd"say /testla" "TestCmd" ); 
    
register_concmd"/testla" "TestCmd" );
}

public 
TestCmd()
{
    
g_iThreadHandle socket_create_t();
    
    if( 
socket_listen_t(g_iThreadHandleszListenAddriListenPortSOCKET_TCP"CBSocketListen" ) < )
    {
        
log_amx"Error: Could not listen on %s:%d"szListenAddriListenPort );
        return;
    }
    
log_amx"Trying to listen on %s:%d"szListenAddriListenPort );
}

public 
CBSocketListeniThreadState iReturn )
{
    if( 
iReturn )
    {
        
log_amx"Error: Could not listen on %s:%d. Error[%d] : %d"szListenAddriListenPortiReturn );
    }
    else
    {
        
log_amx"Listening on %s:%d. Waiting to accept connection ..."szListenAddriListenPort );
        if( 
socket_accept_tg_iThreadHandle"CBSocketAccept" ) < )
        {
            
log_amx"Error: Could not accept connection." );
        }
    }
}

public 
CBSocketAcceptiThreadStateiReturnszClientAddr[], iClientAddrLenclientport )
{
    if( 
iReturn )
    {
        
log_amx"Error: Could not accept connection. Error[%d] : %d"iReturnsocket_get_last_error_t(g_iThreadHandle) );
    }
    else
    {
        
log_amx"Accepted Connection from %s[%d]:%d"szClientAddriClientAddrLenclientport );
        
socket_accept_t(g_iThreadHandle"CBSocketAccept");
        
        
//iReturn is the new Client Socket.
        
g_ClientSThread socket_create_t();
        
socket_set_sd_tg_ClientSThreadiReturn );
        new 
szData[] = "Hello this is SK.";
        if( 
socket_send_tg_ClientSThreadszDatastrlen(szData), "CBClientSendHandler" ) < )
        {
            
log_amx"Error: Could not send data(%s) to client."szData );
        }
    }
}

public 
CBClientSendHandleriThreadStateiReturn )
{
    if( 
iReturn )
    {
        
log_amx"Data has been sent to client. Closing Client Socket ..." );
        
socket_close_t(g_ClientSThread"CBClientCloseSocket" );
    }
    else
    {
        
log_amx"Error: Could not send data(%s) to client. Error[%d] : %d"iReturnsocket_get_last_error_t(g_ClientSThread) );
    }
}

public 
CBClientCloseSocketiThreadStateiReturn )
{
    
socket_destroy_t(g_ClientSThread);
}

public 
plugin_end()
{
    if( 
g_iThreadHandle )
    {
        
socket_destroy_t(g_iThreadHandle);
    }


A Php-Client for testing the above plugin.

PHP Code:

<?php
error_reporting
(E_ALL);

echo 
"<h2>TCP Connection Tester</h2>\n";

$address '192.168.2.2';
$port 79;

/* Create a TCP/IP socket. */
$socket socket_create(AF_INETSOCK_STREAMSOL_TCP);
if (
$socket === false
{
    echo 
"socket_create() failed: reason: " socket_strerror(socket_last_error()) . "<br/>";
}
else 
{
    echo 
"OK.<br/>";
}

echo 
"Attempting to connect to '$address' on port '$port'...";
$result socket_connect($socket$address$port);
if (
$result === false
{
    echo 
"socket_connect() failed.\nReason: ($result) " socket_strerror(socket_last_error($socket)) . "<br/>";

else 
{
    echo 
"OK.<br/>";
}

echo 
"Reading response:<br/><br/>";
while (
$out socket_read($socket2048)) 
{
    echo 
'<br/><b>'.$out.'</b><br/>';
}

echo 
"Closing socket...";
socket_close($socket);
echo 
"OK.<br/><br/>";
?>

For Developers:
How Module works ?
ThreadState Diagram:
A Thread spawned by module, in its lifetime, exists in one of the following ThreadStates. These states can be altered by plugins.
PHP Code:

// Thread states
enum
{
    
STATE_DESTROY,    
    
/* The Thread will destroy itself as well as the CThreadedSocketItem obj it is associated with */
    
STATE_CONNECT,    
    
/* The Thread will try to connect, Thread state is set to STATE_IDLE before CallBack */
    
STATE_DISCONNECT,
    
/* The Thread will disconnect the socket, Thread state is set to STATE_IDLE before CallBack */
    
STATE_READ,
    
/* The Thread will try to read data from the socket, Thread State is set to STATE_IDLE, if there is an error or if the connection is closed */
    
STATE_SEND,
    
/* The Thread will send data to the socket, Thread State is set to STATE_IDLE before CallBack */
    
STATE_LISTEN,
    
/* The Thread will set the socket to listen mode, Thread State is set to STATE_IDLE before CallBack */
    
STATE_ACCEPT,
    
/* The Thread will try to accept new connections, Thread State is set to STATE_IDLE, when a client is accepted, before CallBack */
    
STATE_IDLE
    
/* Idle state of the thread, thread does nothing except checking for change in ThreadState */
}; 

A diagram showing ThreadStates during lifetime of a Thread.
https://i.ibb.co/cwbzsgc/Thread-States.png

Class Diagram:
There are four main classes in this module. They are,
  • CSockets -
    This class is responsible for all low level socket operations.
  • CThreadedSockets -
    This class is a combination of Sockets and Threads. Setting thread states, thread operations like connect, open, listen, destroy etc are defined here. This Class also stores required data for these operations, acts as a bridge between Mainthread and Child(spawned) thread and makes the communications synchronous. For socket operations, this class implements/uses CSocket class.
  • CThreadedSocketItem -
    Inheriting all members from CThreadedSocket Class, This class makes CThreadedSocket as an Item for CThreadedSocketManager to save in its linked list.
  • CThreadedSocketManager -
    This Class maintains a double linked list of all the spawned Threads. If the spawned threads are not properly destroyed, this Class forcefully destroys the threads at AMXX_DETACH.

These classes are interlinked in a hierarchy as shown below,
https://i.ibb.co/3zzs6LV/Classes.png
How to debug ?
However perfect one tries to be, he cant be 100%. So, I don't give a guarantee that Module will work all the time. So, if you want to complain a bug, you have to specify the event at which it occurs or you can exactly point me to the code, where it crashed, by Debugging the module. I will tell you the most simple method to debug on linux as well as on windows.

On Windows,

I personally used Microsoft Visual Studio 2012 Just-In-Time debugger while making this module.
1. Download Debug Project of this Module.
2. Compile yourself the Project so that you can get the Symbol files (.pdbs).
3. Now Copy the output binary into HLDS and run it.
4. When HLDS crashes, you will get an option to debug.
5. When Just-In-Time debugger opens, it will show you the exception (It will be helpful if you even attach this Exception message with your bug report), Select "Break".
6. You will be pointed to the line at which crash has occurred, post this line, as well as the info from call stack window.
On Linux,

We have to use the good old gdb to debug the crash point.
1. Download gdb if you don't have it already.
for ubuntu you can use the following command,
Code:

sudo apt-get install libc6-dbg gdb
2. Edit the hlds_run as follows,
From ( Ln. 138 )
PHP Code:

    if test -"$PID_IN_PARAMS&& test -"$PID_FILE"then
        HL_CMD
="$HL $PARAMS -pidfile $PID_FILE"
    
else
        
HL_CMD="$HL $PARAMS"
    
fi 

To
PHP Code:

    if test -"$PID_IN_PARAMS&& test -"$PID_FILE"then
        HL_CMD
="$GDB --args $HL $PARAMS -pidfile $PID_FILE"
    
else
        
HL_CMD="$GDB --args $HL $PARAMS"
    
fi 

3. Download debug build version binary of this Module.
4. Now open hlds as
Code:

./hlds_run
5. You should be running with gdb and type the following cmds to run HLDS
Code:

run -game cstrike +map de_dust
with some additional parameters as you like.
6. Do whatever you want to reproduce the crash. After crashing you will be returned to gdb prompt. Type 'bt' or 'where' to get the call stack. Attach those with your bug report.
Now your bug report should contain:
0. Info about what you are trying to do as well as the code.
1. If possible, Exception Message.
2. If possible, Line at which the crash occurred.
3. List of CallStack frames.
4. Complete console log. (Since you are using debug version of Module, it will print some info on console as well)
TODO:
  • Nullify iThreadHandle variable in plugin on socket_destroy_t().
    I tried this but i don't know why it didn't work ._.
    PHP Code:

    static cell AMX_NATIVE_CALL socket_destroy_t(AMX *amxcell *params)  /* 1 param */
    {
        if( !
    params[1] )
            return -
    1;

        
    // Get SocketThread form param[1]
        
    CThreadedSocketItemSocketThread = (CThreadedSocketItem *)params[1];
        
    g_ThreadedSocketManager->RemoveItemFromList(SocketThread);
        
        
    // We have Removed SocketThread from list, now let the Thread destroy it self
        
    SocketThread->ThreadState(STATE_DESTROY);
        
    cell *piThreadHandle MF_GetAmxAddr(amxparams[1]);
        *
    piThreadHandle = (cell)0;
        return 
    0;


  • Convert FTP.inc, HTTP.inc to implement ThreadedSockets.
  • Fix release build of release-project on linux, Error at destroying Threads. Because of this i am distributing debug build of release project for now.
  • Test the plugins with Single Handler function for all ThreadedSocket Operations.
  • Put up a Merge request, for this module, into amxmodx.
Credits:
  • Bugsy and hackziner for their work in implementing listen and accept.
  • Arkshine and claudiuhks for this.
Changelog:
Code:

v1.0 - Initial Release
v1.1 - Added support for Dynamically changing buffer size in socket_recv_t() native.

Git Repo: https://github.com/ShootingKing-AM/ThreadedSockets

Downloads:
I am going to provide two Projects for this Module. One is debug project, which will contain some crappy printf's and some other extra comments, and Release project, which is clean and contains proper documentation.

And coming to binaries, Windows binary is release build of Release project and linux binary is debug build of Release project.

Shooting King 05-15-2015 13:31

Re: Module: Threaded Sockets
 
Reserved.

Arkshine 05-15-2015 16:17

Re: Module: Threaded Sockets
 
Any chance you contribute to AMXX repo on Github at some point?

Shooting King 05-16-2015 02:40

Re: Module: Threaded Sockets
 
Quote:

Originally Posted by Arkshine (Post 2297094)
Any chance you contribute to AMXX repo on Github at some point?

Yes, will do it after sufficient people played with this module :p Its on my todo list.

tousif 05-16-2015 06:07

Re: Module: Threaded Sockets
 
Finally , U released it :D , Good

insanedude 05-16-2015 09:35

Re: Module: Threaded Sockets
 
And now my question comes :)
Could something be made about this: https://forums.alliedmods.net/showthread.php?t=255387 with this module?

Shooting King 05-16-2015 10:23

Re: Module: Threaded Sockets
 
I didn't try such 'two-servers-connecting-at-a-time' thingy till now. And there shouldn't be such a problem. If there is a test code to reproduce such a crash, i will work it out.

insanedude 05-17-2015 16:52

Re: Module: Threaded Sockets
 
3 Attachment(s)
Quote:

Originally Posted by Shooting King (Post 2297372)
I didn't try such 'two-servers-connecting-at-a-time' thingy till now. And there shouldn't be such a problem. If there is a test code to reproduce such a crash, i will work it out.

Well. X-redirect is the plugin.

Shooting King 05-18-2015 02:18

Re: Module: Threaded Sockets
 
I am asking a plugin just to reproduce the problem. I cant figure out whats wrong in XRedirect, is it the code or the sockets, its huge and there are many things apart from just sockets :| Just give me small code using sockets which can reproduce the crash.

Spirit_12 05-30-2015 03:16

Re: Module: Threaded Sockets
 
Last I checked. X-Redirect became obsolete and is no longer officially supported. You might wanna look into a different plugin for your experiments.

joropito 06-01-2015 08:24

Re: Module: Threaded Sockets
 
I was checking your code and I have some doubts.

I didn't spent too much time but I can't find where ThreadMain is called from main module.
If I understand correctly, you tried to do the same as on mysql module and other modules you can found. In that case you manage a list of sockets to be polled at each frame or whatever timeframe you want. I can't find that.

Another question is if you have tried your calls to listent/recv/etc on udp sockets. They work slightly different and could broke how you check for incoming data.

Shooting King 06-01-2015 11:12

Re: Module: Threaded Sockets
 
Quote:

Originally Posted by joropito (Post 2303554)
I didn't spent too much time but I can't find where ThreadMain is called from main module.
If I understand correctly, you tried to do the same as on mysql module and other modules you can found. In that case you manage a list of sockets to be polled at each frame or whatever timeframe you want. I can't find that.

When a plugin calls socket_create_t() native, a Thread is created and its Entry function, TheadMain, is attached to that thread. At this moment thread state is set to STATE_IDLE (through the constructor of CThreadedSocket). So in this state, as you can see see from the main post, the Thread does nothing except checking for change in ThreadState.

I don't know exactly how Sqlite threads work, but when i looked at them, they implemented SourceMod Threads, which are not that useful(too complex) in case of Sockets. As you can see, there are no Mutex's no Semaphores etc, etc in this module. I think my Model of implementation is itself synchronous.

Quote:

Originally Posted by joropito (Post 2303554)
Another question is if you have tried your calls to listent/recv/etc on udp sockets. They work slightly different and could broke how you check for incoming data.

Example :D ? Although i will be implementing SendTo and RecvFrom functions in this module as per Destro's request.

Thanks for your time reviewing this shit :d

joropito 06-01-2015 13:40

Re: Module: Threaded Sockets
 
So you have a standalone thread looping for socket handling?

I'm not sure if calling forwards it's safe from another thread. I haven't checked but I guess isn't safe at least at pawn vm. 99% of tests could work, but you may hit some problem.

Shooting King 06-02-2015 04:06

Re: Module: Threaded Sockets
 
Quote:

Originally Posted by joropito (Post 2303676)
So you have a standalone thread looping for socket handling?

Yes. One thread per socket.

Quote:

Originally Posted by joropito (Post 2303676)
I'm not sure if calling forwards it's safe from another thread. I haven't checked but I guess isn't safe at least at pawn vm. 99% of tests could work, but you may hit some problem.

i guess there should't be such a problem. Did you experience such a thing ?

joropito 06-02-2015 16:31

Re: Module: Threaded Sockets
 
Check yourself

https://wiki.alliedmods.net/Pawn_tutorial

Quote:

No thread-safety
Pawn is targetted toward single-thread instances.
My recomendation is to keep things safe.

What you can do is to have an ipc buffer to communicate between main thread and each child thread so you can keep your thread working like now but forwards should be called from main thread.

Shooting King 06-03-2015 10:33

Re: Module: Threaded Sockets
 
If we make such a buffer, when and how do we check for a change in such buffer in main thread ? Do note a point that we are talking about 1% of all cases.

joropito 06-03-2015 11:10

Re: Module: Threaded Sockets
 
Quote:

Originally Posted by Shooting King (Post 2304338)
If we make such a buffer, when and how do we check for a change in such buffer in main thread ? Do note a point that we are talking about 1% of all cases.

StartFrame() is a good start.
Check dlls/mysqlx/threading.cpp on amxmodx source code.

Shooting King 06-03-2015 13:39

Re: Module: Threaded Sockets
 
Quote:

Originally Posted by joropito (Post 2304349)
StartFrame() is a good start.
Check dlls/mysqlx/threading.cpp on amxmodx source code.

Then we will be using much resources just for checking Buffer :| StartFrame is called too many time per second. Any other good alternative ? I really don't want to depend on engine/game functions.

joropito 06-03-2015 14:30

Re: Module: Threaded Sockets
 
You're doing modules, not plugins. On plugins it's expensive in time because stack and calls.

A lot of modules uses StartFrame, it not too expensive in this case.

You have to do in game loop so it should be done in some game call.
If you don't like to use directly StartFrame, do in 1/10 calls of it.
Also you can create a dummy entity with custom Think time where you check for available data, but you will spent 1 entity from your limited max ent number...

Javivi 06-09-2015 08:43

Re: Module: Threaded Sockets
 
Nice job. I was working on an update but this one is way better, so be sure to push it to amxmodx's github.

Feel free to take any code you want to improve your module, as I've seen that you didn't change much of the default code.

https://github.com/Javivi/amxmodx/co...sockets-update

Biggest changes are:
Code:

- WinSock version changed from 1.1 to 2.2.
- Properly check for WinSock initialization on OnAmxxAttach/Detach.
- Now, natives will not be added if we can't start up WinSock.
- socket_open is now IP version agnostic (both IPv4 and IPv6 are
supported).
- Error reporting has been changed on socket_open, now it returns the
actual libc errors instead of the previous made-up errors.
- socket_close now returns a value on success/failure.
- Code rewritten to be more readable, it should be self-explaining now.


Shooting King 06-11-2015 04:28

Re: Module: Threaded Sockets
 
Quote:

Originally Posted by Javivi (Post 2306168)
Nice job. I was working on an update but this one is way better, so be sure to push it to amxmodx's github.

Sure, Thanks.

Quote:

Originally Posted by Javivi (Post 2306168)
Feel free to take any code you want to improve your module, as I've seen that you didn't change much of the default code.

Yes, I didn't change any of the normal natives code, because i don't think there are any complaints regarding default sockets.

Destro- 06-11-2015 13:44

Re: Module: Threaded Sockets
 
socket_recv_t()

It does not support a binary transfer (null byte)?

@EDIT
suggestions:
- Add the "iThreadHandle" param on callbacks or a array to pass custom data (if you do this, you could remove "iThreadState" and not implement "iThreadHandle")
- Remove param "protocol", only support TCP.
- iRecvDataLen is bytes received
- Check callback valid public fuction
- Support null bytes for recv

@EDIT2
win2008:
50% CPU usage on STATE_READ

code:
Spoiler


@EDIT3
I haven't thought before, but threads are totally unnecessary, you can use non-blocking mode.

@EDIT4
Really I needed something functional now xd.

Github:
Github / Destro- / amxx / sockets_async

Last version


Spoiler

draft 08-18-2015 16:41

Re: Module: Threaded Sockets
 
Destro-, nice job!

But please, add from native module this one:
Code:

/* Same as socket_send but Data can contain null bytes */

native socket_send2(_socket, const _data[], _length);

Or just update current socket_send to able sending null bytes. Thanks in advance if you could do it quickly!)

Destro- 08-19-2015 18:16

Re: Module: Threaded Sockets
 
socket_send support null bytes

draft 08-20-2015 13:40

Re: Module: Threaded Sockets
 
PHP Code:

#include <amxmodx>
#include <sockets_async>

#define PLUGIN "Test ASync Null Bytes"
#define VERSION "1.0"
#define AUTHOR "AImpressor"


public plugin_init() {
    
register_plugin(PLUGINVERSIONAUTHOR)
    
register_srvcmd("test_send""test_send")
}

public 
test_send() {
    new 
SOCKET:socket socket_create(SOCK_TYPE_TCP0)
    
socket_connect(socket"127.0.0.1"80)
}

public 
fw_sockConnected(SOCKET:socketcustomID)
{
    new 
packet[256]
    
formatex(packet255"%c%c%c%c"0x000x000x000x00)
    new 
len socket_send(socketpacket)
    
log_amx("Sending packet len: %d"len)


When im using "test_send", it gives me answer "Sending packet len: 0" - that means it ignores all my null bytes. Tested it on windows server with web. Could you please resolve this issue?

draft 08-21-2015 13:00

Re: Module: Threaded Sockets
 
Thx, this worked for me! But the disadvantage is that we should always count exact number of bytes in code to send them. And if i need, for example, 3 null bytes at the end, it is required to make len + 3 to send them correctly.

Please, update sockets_async.inc to this code. It allows the module to autoload instead of writing it in modules.ini.
PHP Code:

#if AMXX_VERSION_NUM >= 175
    #pragma reqlib sockets_async
    #if !defined AMXMODX_NOAUTOLOAD
        #pragma loadlib sockets_async
    #endif
#else
    #pragma library sockets_async
#endif 

Also, ive found that if 2 or more plugins are using this module there are several memory collisions between them. It means that if im sending info in second plugin, then im recving it in first of them that has fw_sockReadable hook. May be, im doing smth wrong, but in native amxx modules there are not collisions between plugins.

draft 09-10-2015 07:17

Re: Module: Threaded Sockets
 
Hi, in last version of this module in LINUX there is always error 115 (SOCK_INPROGRESS) at crear connection (when im using socket_connect function in plugin. Ive tried to overpass that error by modifying the source of module but now after socket_create nothing happenes, seems, that socket is always non-writable while in windows server this works fine.
Could you please fix this issue?

Shooting King 09-11-2015 13:01

Re: Module: Threaded Sockets
 
Let me make myself clear again,
Quote:

This module eliminates looping, hidden looping, entity thinks in plugin as well as in the main thread.
Destro, you are just continuously looping and checking if there is any change in state of the socket, you don't know the exact time when your asyn sockets respond to your command(connect, send,...) similar to the includes aforementioned and hence looping becomes inevitable. But in game engines, we cannot afford that many number of false checking in loops on the same thread on which main HLDS functions are running (that too on StartFrame()). So what my module actually does is, it separates these loops from the main thread and keeps them on a Child Thread, eliminating false checks on the main thread. These child threads will inform the main thread when there is a change in state of the socket and executes a corresponding forward.

Your module is a module level implementation of what Bugsy's includes and others does and is more efficient (ofcourse). You are free to start a new thread in Module Section for your Async-Sockets, but its different from mine (atleast in theory).

draft 09-15-2015 09:09

Re: Module: Threaded Sockets
 
Shooting King,
Could you please make your API more useful?
I mean that for example we are sending multiply socket queries in one time, how can i understand in callback which one is answering of them? There is no way in plugin to add custom information in callback (for example, playerID for which im sending socket query) and in ur examples u are using single global variable for that purpose which is highly unappreciated.
In AMX there is set_task function that allows us to send custom information in callback, could you upgrade your module and API to something similar? Or may be u can use Destro API, it is also not bad.

BTW:
Quote:

suggestions:
- Add the "iThreadHandle" param on callbacks or a array to pass custom data (if you do this, you could remove "iThreadState" and not implement "iThreadHandle")

Shooting King 09-16-2015 11:35

Re: Module: Threaded Sockets
 
I am not sure i understood you draft, Can you give me an Example ? And tell me what you want to do ? Actually i didn't understand that suggestion too :mrgreen:

Neeeeeeeeeel.- 09-16-2015 15:45

Re: Module: Threaded Sockets
 
Quote:

Originally Posted by Shooting King (Post 2343540)
I am not sure i understood you draft, Can you give me an Example ? And tell me what you want to do ? Actually i didn't understand that suggestion too :mrgreen:

I send 2 packets thought the module. Then, I get 2 responses. How can I know what response corresponds to what packet?

That's what he is trying to explain.

Shooting King 09-17-2015 03:55

Re: Module: Threaded Sockets
 
Quote:

Originally Posted by Neeeeeeeeeel.- (Post 2343639)
I send 2 packets thought the module. Then, I get 2 responses. How can I know what response corresponds to what packet?

That's what he is trying to explain.

Based upon iThreadHandle ? Cmon people give me an example where are you facing the problem :|

Neeeeeeeeeel.- 09-22-2015 14:14

Re: Module: Threaded Sockets
 
Quote:

Originally Posted by Shooting King (Post 2343798)
Based upon iThreadHandle ? Cmon people give me an example where are you facing the problem :|

He meant using the same g_iThreadHandle.

Shooting King 09-23-2015 08:13

Re: Module: Threaded Sockets
 
Quote:

Originally Posted by Neeeeeeeeeel.- (Post 2345911)
He meant using the same g_iThreadHandle.

It depends on what he wants to do. Let him post his problem Neel

Nixon133 11-20-2015 12:17

Re: Module: Threaded Sockets
 
Hi. Can I add a new cornfield to get ip address of the server? His replacement get_user_ip (0, ...).

Shooting King 11-22-2015 05:01

Re: Module: Threaded Sockets
 
Whats a cornfield ? native ? Whats the problem with get_user_ip() ? What exactly are you trying to do ?

Nixon133 11-22-2015 05:37

Re: Module: Threaded Sockets
 
Quote:

Originally Posted by Shooting King (Post 2365355)
Whats a cornfield ? native ? Whats the problem with get_user_ip() ? What exactly are you trying to do ?

Problem in that through cvar. of net_address IP can change (visually). At this get_user_ip(0) will give out this address, but not real IP servers.

Shooting King 11-22-2015 12:22

Re: Module: Threaded Sockets
 
I don't understand why do you change net_address and why you want to get the real address now ? :| Anyways getting Global ip from within the same system is hard to implement and even though if implemented cannot guarantee you the correct result. One thing you can do is get a server (hosted on a different network) to echo your IP back.

If you have a web-server make a simple script to echo REMOTE_ADDR HTTP var. Using php,
PHP Code:

<?php echo "Your Current IP is " $_SERVER["REMOTE_ADDR"];

Using sockets (from amxx plugin) request and read http page and using RegEx extract IP from it. You can do this by just editing the Example1 given in the first post.

A simple code (test yourself),
PHP Code:

#include <amxmodx>
#include <sockets>
#include <regex> 

#define BUFFER_LEN    512

new g_iThreadHandle
new 
pCvar_HostNamepCvar_Page

new 
szBuffer[BUFFER_LEN];

public 
plugin_init()
{
    
register_plugin"Global IP""1.0""Shooting King" );
    
register_concmd"/test" "TestCmd" );

    
pCvar_HostName register_cvar"sk_ts_hostname""localhost" ); // Change this to your hostname
    
pCvar_Page register_cvar"sk_ts_reqpage""/" );
}

public 
TestCmd() 

    
g_iThreadHandle socket_create_t(); 
     
    new 
szHostName[64]; 
    
get_pcvar_stringpCvar_HostNameszHostName64 ); 
     
    
socket_open_t(g_iThreadHandleszHostName80SOCKET_TCP"CBOpenSocket");


public 
CBOpenSocketiThreadStateiReturn 

    if( 
iReturn 
    { 
        
log_amx"ERROR: Could not open Socket. Error[%d] : %d"iReturnsocket_get_last_error_t(g_iThreadHandle) ); 
    } 
    else 
    {         
        new 
szPacket[128];  
        new 
szHostName[64], szPage[64]; 
        
get_pcvar_stringpCvar_HostNameszHostName64 ); 
        
get_pcvar_stringpCvar_PageszPage64 ); 
         
        
formatexszPacket127"GET %s HTTP/1.1^r^nHost: %s^r^n^r^n"szPageszHostName ); 
        
socket_send_tg_iThreadHandleszPacketsizeof(szPacket), "CBSendSocket");
    } 


public 
CBSendSocketiThreadStateiReturn 

    if( 
iReturn 
    { 
        
log_amx"ERROR: Error Sending Data. Error[%d] : %d"iReturnsocket_get_last_error_t(g_iThreadHandle) ); 
    } 
    else 
    {
        
szBuffer[0] = '^0';
        
socket_recv_tg_iThreadHandle"CBRecvSocket"64 );
    } 


public 
CBRecvSocketiThreadStateiReturnszRecvData[], iRecvDataLen 

    if( 
iReturn <= 
    {
        
socket_close_t(g_iThreadHandle"CBCloseSocket"); 
        new 
reterror[2], szIP[16];

        new 
Regex:regex_handle regex_match(szBuffer"Your Current IP is ([0-9.]*)"reterrorcharsmax(error));    
        if( 
regex_handle REGEX_NO_MATCH )
        {
            
regex_substr(regex_handle1szIPcharsmax(szIP));        
            
log_amx"IP : %s" szIP );
            
regex_free(regex_handle);
        }
        else
        {
            
log_amx"No IP found in RecvData" );
        }
    }
    else 
    { 
        
strcatszBufferszRecvDataBUFFER_LEN );
    } 


public 
CBCloseSocketiThreadStateiReturn 

    
socket_destroy_t(g_iThreadHandle); 


You don't specifically need a webserver, any game server or any server capable of listening and sending your ip back can do the trick.

Nixon133 11-22-2015 12:46

Re: Module: Threaded Sockets
 
Whether and it is possible to realize it, native?
To receive IP as you did it in Module: Fake Server Queries. Inet_ntoa function.

Whether also there will be a continuation of the module? Improvement, completion?

Shooting King 11-22-2015 13:31

Re: Module: Threaded Sockets
 
Quote:

Originally Posted by Nixon133 (Post 2365466)
Whether and it is possible to realize it, native?
To receive IP as you did it in Module: Fake Server Queries. Inet_ntoa function.

No

Quote:

Originally Posted by Nixon133 (Post 2365466)
Whether also there will be a continuation of the module? Improvement, completion?

Yes


All times are GMT -4. The time now is 06:22.

Powered by vBulletin®
Copyright ©2000 - 2024, vBulletin Solutions, Inc.