View Single Post
Author Message
ot_207
Veteran Member
Join Date: Jan 2008
Location: Romania The Love Country
Old 02-23-2011 , 15:31   [TUT] Introduction to sockets
Reply With Quote #1

Introduction
This tutorial is meant for beginner scripters that know how to use the set_task natives and explains basic things about sockets.
After that it gives a simple example on how to do a version checker.

What is a socket?
I will give the definition that is provided on wikipedia.
In computer networking, an Internet socket or network socket is an endpoint of a bidirectional inter-process communication flow across an Internet Protocol-based computer network, such as the Internet.
A socket address is the combination of an IP address (the location of the computer) and a port (which is mapped to the application program process) into a single identity, much like one end of a telephone connection is the combination of a phone number and a particular extension.
The Internet Protocol is responsible for addressing hosts and routing datagrams (packets) from a source host to the destination host across one or more IP networks. For this purpose the Internet Protocol defines an addressing system that has two functions. Addresses identify hosts and provide a logical location service.

If we summarize what above has bees stated.
We can conclude that through sockets (a connection) we can transmit to another host (person) using an internet protocol (a known language) information for a specific port (application).
Note: The terms that been placed between () and have been bolded are placed there to enable users to compare a socket with a human dialogue for easy understanding.

What are the basic functions of sockets?
I will also give the list that is posted on wikipedia since it is a good one.
Code:
socket() creates a new socket of a certain socket type, identified by an integer number, and allocates system resources to it.
bind() is typically used on the server side, and associates a socket with a socket address structure, i.e. a specified local port number and IP address.
listen() is used on the server side, and causes a bound TCP socket to enter listening state.
connect() is used on the client side, and assigns a free local port number to a socket. In case of a TCP socket, it causes an attempt to establish a new TCP connection.
accept() is used on the server side. It accepts a received incoming attempt to create a new TCP connection from the remote client, and creates a new socket associated with the socket address pair of this connection.
send() and recv(), or write() and read(), or recvfrom() and sendto(), are used for sending and receiving data to/from a remote socket.
close() causes the system to release resources allocated to a socket. In case of TCP, the connection is terminated.
gethostbyname() and gethostbyaddr() are used to resolve host names and addresses.
select() is used to prune a provided list of sockets for those that are ready to read, ready to write or have errors
poll() is used to check on the state of a socket. The socket can be tested to see if it can be written to, read from or has errors.
Which of these functions can I use in AmxModX scripting?
Well this depends on the module that you would like to use.

There are 2 modules here on AlliedMods:
1. Sockets - this is the default module that comes with the AmxModX package and has some of the functions that are presented above.
2. Sockets_HZ - this is a custom module that is the same as the one before but has an advantage of offering more of the functions that are posted above.

The module that we will talk about will be the basic Sockets module since we want to focus only on the basics.

Info about the functions/defines that come in the socket module.
First of all we take a look at the include since it will provide us with the most information that we need.

At first we look at the defines:
PHP Code:
// Use SOCKET_TCP for TCP Socket connections

#define SOCKET_TCP 1

// Use SOCKET_UDP for UDP Socket connections

#define SOCKET_UDP 2 
These 2 defines represent the protocol type (the language) of the connection.

What is the difference between them?

TCP - stands for Transmission Control Protocol and offers complete and correct data transmission between the end devices but the only problem of it is that if the data between the end devices is damaged it will be retransmitted. Because of this this protocol sometimes can be slow.

UDP - stands for User Datagram Protocol and does not have control of the data integrity. Because of this is faster.

If you want to know more about them you just need to access wikipedia.

And now about the functions
As you can see if you understand the principles and the role of everything that has been explained above you will understand the purpose and the necessity of the arguments in the functions.

PHP Code:
/* Opens a new connection to hostname:port via protocol (either SOCKET_TCP or SOCKET_UDP),
 * returns a socket (positive) or negative or zero on error.
 * States of error:
 * 0 - no error
 * 1 - error while creating socket
 * 2 - couldn't resolve hostname
 * 3 - couldn't connect to given hostname:port 
*/

native socket_open(const _hostname[], _port_protocol SOCKET_TCP, &_error);

/* Closes a Socket */

native socket_close(_socket);

/* Recieves Data to string with the given length */

native socket_recv(_socket_data[], _length);

/* Sends data to the Socket */

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

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

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

/* This function will return true if the state (buffer content) have changed within the last recieve or
* the timeout, where timeout is a value in ÁSeconds, (1 sec =1000000 Ásec). 
* Use to check if new data is in your socket. */

native socket_change(_socket_timeout=100000); 
The info you need before creating a plugin.
I will now give the example on how to check if a plugin has a new version to see how all of these things that have been explained above come together in the end.

First of all we need to think on the things that we want to do.
In this example I want to check the forums from AlliedModders to see if my Block Wallhack plugin is up to date, in order to do that we need the plugin posted somewhere on the forums because we cannot check something that does not exist.

The plugin has the following link:
http://forums.alliedmods.net/showthread.php?t=100886

The next step is changing the link into a socket type addressing schematic.
These are the things we need:
  • Server Address
  • Port
  • Protocol

The server address (host name) is already specified in the link of the plugin:
http://forums.alliedmods.net/showthread.php?t=100886.
I have marked it with the orange color.

Now what about the port, this is also specified in the link of the plugin
http://forums.alliedmods.net/showthread.php?t=100886
.
The port is HTTP (Hyper Text Transfer Protocol), if we search the net we can see that the value of this port is 80 for server access.

Now what about the protocol?
Well this is something that you need to search on the net to see what type of protocol is used with HTTP. In our situation TCP is the one.

What about the last part of the link?
http://forums.alliedmods.net/showthread.php?t=100886.
This will be used to access the correct page in order to do the checks that we are interested in, you will see that in the example.

So the list comes together like this:
  • forums.alliedmods.net
  • 80
  • TCP

The main parts of the plugin.
This is the part where all the theory comes together in one small script.

We already have all the info that we need.
I will first post the functions separately and comment them and after that I will post the entire script for better understanding.

First of all let's make some defines
PHP Code:
// We also need the topic of the plugin in order to get the data from the correct page.
#define PLUGIN_TOPIC                "/showthread.php?t=100886"
#define PLUGIN_HOST                    "forums.alliedmods.net"

// These defines are used for tasks
#define TASKID_GETANSWER            0
#define TASKID_CLOSECONNECTION        1 
Now we need to connect to the AM forums.
PHP Code:
g_Socket socket_open(PLUGIN_HOST80SOCKET_TCPerror
If we have problems and the socket does not work the error argument will have a value different from 0. So we need to take care if something like that happens.
PHP Code:
    switch (error)
    {
        case 
1:
        {
            
log_amx("[BW Version Checker] Unable to create socket.")
            return
        }
        case 
2:
        {
            
log_amx("[BW Version Checker] Unable to connect to hostname.")
            return
        }
        case 
3:
        {
            
log_amx("[BW Version Checker] Unable to connect to the HTTP port.")
            return
        }
    } 
Now we need to send info on the socket so that the server will give us the response that we need.
This will be done through this:
PHP Code:
    new errorsendbuffer[512]

    
format(sendbuffer511"GET %s HTTP/1.1^nHost:%s^r^n^r^n"PLUGIN_TOPICPLUGIN_HOST)
    
    
socket_send(g_Socketsendbuffer511
Note: The way that sendbuffer has been formated is the correct way to send data to a HTTP server for a page request. For more info check this out.

Now we need to use set_tasks in order to wait for a response from the server. We need this part because the network traffic can be huge and the response from the forums might take some time. We just need to make sure that nothing happens.
That is why I will use 2 tasks. One for getting the server respons and another one for closing the connection if no info has been sent.
PHP Code:
    // We repeat the first task 15 times in order to check 15 times every second if we get the response and to see if the info that we want to search is in that response.
    
set_task(1.0"task_waitanswer"TASKID_GETANSWER""0"a"15)
    
// The second task closes the connection if in the first task nothing has been done.
    
set_task(16.0"task_closeconnection"TASKID_CLOSECONNECTION""0""0
Now is the part where we need to access the info from the server.

First of all we check if the socket has info in it.
PHP Code:
socket_change(g_Socket
If it does then we need to get the data from it and see if the buffer already has the title of the plugin and the version of the one released on the internet to compare with our version of the plugin.
PHP Code:
socket_recv(g_Socketg_Data999
This is what you will receive if the connection has been made.

Code:
HTTP/1.1 200 OK

Server: nginx/0.6.32

Date: Wed, 23 Feb 2011 01:10:57 GMT

Content-Type: text/html; charset=ISO-8859-1

Transfer-Encoding: chunked

Connection: keep-alive

X-Powered-By: PHP/5.3.5

Cache-Control: private

Pragma: private

X-UA-Compatible: IE=7

Set-Cookie: bblastvisit=1298423457; expires=Thu, 23-Feb-2012 01:10:57 GMT; path=/

Set-Cookie: bblastactivity=0; expires=Thu, 23-Feb-2012 01:10:57 GMT; path=/



8e07

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html dir="ltr" lang="en">

<head>

	<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />

<meta name="generator" content="vBulletin 3.8.1" />



<meta name="keywords" content="Block Wallhack v7 [CS &amp; CZ], AMX Mod X,AMX Mod,Metamod,Half-Life Plugins,AMXX,AMX,Plugins,Counter-Strike,CSDM,Deathmatch,Servers,C++" />

<meta name="description" content="Block Wallhack v7 [CS &amp; CZ] Approved Plugins" />





<!-- CSS Sty
If we look at it we can see that we already have the title in it so it will not be needed to check again for the buffer so we terminate the connection and end the tasks.
PHP Code:
    socket_close(g_Socket)
            
    
remove_task(TASKID_GETANSWER)
    
remove_task(TASKID_CLOSECONNECTION

Putting all together.
PHP Code:
// The includes
#include <amxmodx>
#include <sockets>

// The info about the plugin, we will use the VERSION define for coparison.
#define PLUGIN    "BW Version Checker"
#define AUTHOR    "OT"
#define VERSION    "8.0"

// Host and topics
#define PLUGIN_TOPIC                "/showthread.php?t=100886"
#define PLUGIN_HOST                    "forums.alliedmods.net"

// Tasks
#define TASKID_GETANSWER            0
#define TASKID_CLOSECONNECTION        1

// The socket handle
new g_Socket

// Data buffer
new g_Data[1000]
// Version string buffer
new g_StringVersion[10]

// Needed to update the plugin?
new bool:g_NeedToUpdate

public plugin_init()
{
    
register_plugin(PLUGINVERSIONAUTHOR)
}
public 
plugin_cfg()
{
    
// Connect
    
new errorsendbuffer[512]
    
g_Socket socket_open(PLUGIN_HOST80SOCKET_TCPerror)
 
    
// If we get error
    
switch (error)
    {
        case 
1:
        {
            
log_amx("[BW Version Checker] Unable to create socket.")
            return
        }
        case 
2:
        {
            
log_amx("[BW Version Checker] Unable to connect to hostname.")
            return
        }
        case 
3:
        {
            
log_amx("[BW Version Checker] Unable to connect to the HTTP port.")
            return
        }
    }
    
    
log_amx("[BW Version Checker] Connection with %s has been established"PLUGIN_HOST)
    
    
// Send page request
    
format(sendbuffer511"GET %s HTTP/1.1^nHost:%s^r^n^r^n"PLUGIN_TOPICPLUGIN_HOST)
    
socket_send(g_Socketsendbuffer511)
    
    
log_amx("[BW Version Checker] Sending Block Wallhack page request.")
    
    
// Setting the tasks for handeling the response
    
set_task(1.0"task_waitanswer"TASKID_GETANSWER""0"a"15)
    
set_task(16.0"task_closeconnection"TASKID_CLOSECONNECTION""0""0)
}

public 
task_waitanswer(id)
{
    
// It changed?
    
if (socket_change(g_Socket))
    {
        
// Get the data
        
socket_recv(g_Socketg_Data999)
        
        
// We seach for the title
        
new Position containi(g_Data"Block Wallhack v")
        
        
// Title found
        
if (Position >= 0)
        {
            
log_amx("[BW Version Checker] Page found, comparing versions.")
            
            
// Now we start processing the buffer
            
            // We get the start of the numbers of the version
            
Position += strlen("Block Wallhack v")
            
            new 
length
            
            
// We copy the valid version characters in another string
            
for (new i=0;i<10;i++)
            {
                if (
'0' <= g_Data[Position i] <= '9' || g_Data[Position i] == '.')
                {
                    
g_StringVersion[length] = g_Data[Position i]
                    
length++
                }
            }
            
            
// Compare the plugin version with the internet version
            
g_NeedToUpdate = (str_to_float(g_StringVersion) > str_to_float(VERSION))
            
            
log_amx("[BW Version Checker] Versions has been compared, closing connection. %s!"g_NeedToUpdate "Plugin is old" "Plugin is updated")
            
            
// Close the connection
            
socket_close(g_Socket)
            
            
// Remove the tasks
            
remove_task(TASKID_GETANSWER)
            
remove_task(TASKID_CLOSECONNECTION)
        }
    }
}

public 
task_closeconnection(id)
{
    
// Close connection if no response in 16 seconds.
    
socket_close(g_Socket)

Sources
Other tuts/applications that can be useful after reading this
__________________
My approved plug-ins | Good for newbies! | Problems?

Back, will come around when I have time.

Last edited by ot_207; 02-23-2011 at 18:06.
ot_207 is offline