mScriptBox Tutorial
Introduction to Sockets

Written by Pasmal
Published with permission

Table of Content

  1. Introduction
  2. Back to the basics: Opening a Socket
  3. Back to the basics: Closing a Socket
  4. Writing to a socket
  5. Reading data from a socket
  6. A worked example of a basic HTTP Client
  7. Acting as a server
  8. Worked example of acting as a one way server
  9. Worked example of acting as a direct chat server
  10. Protocol? What Protocol?
  11. The Sockmark
  12. Socket Identifiers


1. Introduction  Back to Top

Sockets were introduced into mIRC in version 5.3 and are used to help control raw connections to other computers (connected to the internet). A word of warning, if you are not very familiar with aliases, remotes, identifiers and variables then I strongly suggest you learn them up first. In other words, this is not for the newbie scripter.

Please note that this introduction is just that, not a complete guide on how to master sockets, nor does it cover UDP sockets, so everything in this introduction is for TCP sockets.. Mastering sockets takes A LOT of practice, time, and patience. "It may not happen overnight, but it will happen" :)


2. Opening a Socket  Back to Top

It is rather simple to open a socket connect using mIRC. The /sockopen command does this. Its format:

/sockopen sockname server port

Sockname is just a name that you make up, it does not have to stand for anything. Server is the server you want to connect to.

NOTE: http:// and ftp:// is not part of a server name. So if I wanted to open a socket to http://www.cnn.com I would type /sockopen cnn www.cnn.com 80 NOT /sockopen cnn http://www.cnn.com 80. The port you choose is very important, different types of servers use different ports. See the list of some Common Internet Ports and their uses:

  • 80 HTTP (web server)
  • 6667 IRC
  • 110 POP3 (incoming email)

    Of course there are many more.

    The on SOCKOPEN event reacts when a socket is opened. Its format:

    on *:SOCKOPEN:sockname: commands

    I will make several examples once I have covered most of the basics


  • 3. Closing a Socket  Back to Top

    To close a socket you need to know only one thing: the name of the socket. The command to close a socket is /sockclose sockname. The event which reacts when a socket is closed is on SOCKCLOSE. Its format is:

    on *:SOCKCLOSE:sockname: commands

    You may also use wildcards for the sockname.


    4. Writing to a socket  Back to Top

    Writing or sending data to a socket requires the /sockwrite command. Its format is the following:

    /sockwrite -n sockname data

    e.g. to request a file from a web server:

    /sockwrite -n sockname GET /directory/filename HTTP/1.0

    Data is the binary or ASCII text you want to send. If you wanted to get a file from a web server, you would probably use:

    The -n switch is used to append a $crlf (carriage return, line feed) to the end of the data. This basically tells the computer you are sending the data to, that its the end of the line. If you did not use -n or $crlf then the computer usually would not respond until you did use -n or $crlf (i.e. its like queuing lots and lots of lines, then finally letting them go, except the other computer will see it as just one long line).

    Note: The HTTP/1.0 is not required but if you do use it, you need to send an additional sockwrite command to tell the server you have finished sending data (Because the HTTP/1.x protocols allow for addition data to be sent with the request). The additional sockwrite command is /sockwrite sockname$crlf.

    You can also use wildcards in the sockname in the /sockwrite command. e.g. /sockwrite -n www* GET / will send "GET /" to all sockets that start with www.

    Side Note: Once the data from each sockwrite command has been received, the on SOCKWRITE event will trigger. This is a fairly unknown event. What I found useful about it is that it allows you to not "throttle" all your data at once, but send one "packet" at a time until all of the data needed to be sent has been sent. This means you won't overflow the socket and crash mIRC :).

    What I do is I use binary functions (bread, bwrite, &binary-variables) to send a file requested by another computer. I send the file in lots of 4096 bytes. After each lot of 4096 bytes is sent, the on SOCKWRITE event reacts, which I then create a script to make it send the next lot of 4096 bytes etc... This is alot better from a method using loops & $read -l or even loops and binary as it dosn't lock up mIRC/sometimes crash it.


    5. Reading data from a socket  Back to Top

    To read what the computer at the other end of the connection is sending back to you, you need to use the sockread command in conjunction with on SOCKREAD. The format for /sockread is:

    /sockread %variable

    It basically sets whatever data came through the socket to a variable. This command can only be used inside the on SOCKREAD event. The format for on SOCKREAD is:

    on *:SOCKREAD:sockname: commands

    Below is an example of how to read then display the data sent to you:

    on *:SOCKREAD:sockname:{
      if ($sockerr > 0) return
      ; The $sockerr identifier will only return a number greater than 0 when an error has
      ; occured in the connection.
      :nextread
      sockread %temp
      if ($sockbr == 0) return
      ; $sockbr returns the number of bytes read by the sockread command. Its a good way to
      ; check if you actually read anything.
      if (%temp) { echo %temp }
     goto nextread
    }


    6. A worked example of a basic HTTP Client  Back to Top

    In remotes put:
    on *:SOCKOPEN:http: {
      echo -s *** $sockname was just opened, Retrieving file sock.nfo
      ; $sockname stands for the name of the socket, i.e. http
      sockwrite -n $sockname GET /~mcopley/sock.nfo HTTP/1.0
      sockwrite $sockname $crlf
     ; sent data to http socket requesting the file /~mcopley/sock.nfo
     ; you may sometimes get a file not found message even though you know
     ; it exists. This is usually because you need to specify a Host: or Referer:
    }

    on *:SOCKCLOSE:http: {
      echo -s *** $sockname just closed
    }

    on *:SOCKREAD:http: {
      if ($sockerr > 0) return
      :nextread
      sockread %temp
      ; read the data coming from the socket
      if ($sockbr == 0) return
      ; if i've read all the data, stop
      if (%temp) { echo %temp }
      ; if there was stuff received from the socket, then echo it
      goto nextread
    }

    Then in your status window (It does not have to be the status window) type:

    /sockopen http homepages.ihug.co.nz 80

    (Note how I did not put http:// in front of homepages.ihug.co.nz)


    7. Acting as a server  Back to Top

    Yeup, mIRC lets you act as a server - that is accept connections coming from other computers. You need to use the /socklisten command, the format is:

    /socklisten sockname port

    When mIRC does detect someone is trying to connect onto a port you selected with /socklisten, the on SOCKLISTEN event kicks in. Its format is the following:

    on *:SOCKLISTEN:sockname: commands

    But just because someone is trying to connect means they have, you have to use the /sockaccept command to accept their connection. This means that you are allowing whoever it is to connect to your computer. The format for /sockaccept is:

    /sockaccept sockname

    When assigning a name, do not use the same sockname as your on SOCKLISTEN. Either use a modification of it, or a totally different name altogether. After you use the sockaccept command, that particular connection will now be refered to as the name of the socket that you accepted it to.
    Example:

    /socklisten test 90

    on *:SOCKLISTEN:test: {
      echo -s *** Incoming connection.
      sockaccept test $+ $rand(1,100)
    }

    This will accept the incoming connection on port 90, and will name the connection test1-100. It can be any number from 1 to 100. Some people, including myself, like to use $ticks in the socket name, as its highly unlikely the script will even try to accept the connection under a sockname that already exists (as compared to $rand). Even better, I use a combination: sockaccept blah $+ $rand(100,999) $+ $ticks.

    So lets say someone tried to connect to us on port 90: The script will then accept their connection, and name it something like 'test93'. From now on that single socket/connection will be called test93. If we want to send data to that socket we write /sockwrite -switches test93 data NOT /sockwrite -switches test data.

    This is why $sockname comes in handy, as it allows you to interact with a socket without knowing its full name.


    8. Worked example of acting as a one way server  Back to Top

    This is a type of one sided chat (Finger/info reply if you will)
    Server Side:

    Activate socklisten by typing:

    /socklisten inf 90

    In remotes put:

    on *:SOCKLISTEN:inf: {
      echo -s *** Incoming chat connection, sending text to it.
      set -u3 %temp.socketname info $+ $rand(1,300)
      ;make up a half random name for the socket
      sockaccept %temp.socketname
      ;accept the socket
      sockwrite -n %temp.socketname Welcome to my One Sided Chat - A Socket Server
      sockwrite -n %temp.socketname What you are receiving is just info about myself
      sockwrite -n %temp.socketname -
      sockwrite -n %temp.socketname Nick: $me
      sockwrite -n %temp.socketname Time $time
      sockwrite -n %temp.socketname Other Stuff: I like making socket stuff with mIRC
      sockwrite -n %temp.socketname Oh, and be sure to use the latest mIRC!
      sockwrite -n %temp.socketname -
      sockwrite -n %temp.socketname Byeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
      ;send data to the socket
      .timer 1 1 sockclose %temp.socketname
      ;close the socket after 1 second, allowing time for all data to be sent
    }

    on *:SOCKCLOSE:info*:{
    echo -s *** $sockname has been terminated
    }

    Client side:

    To open a connection type:

    /sockopen clientinfo address 90

    In remotes put:

    on *:SOCKOPEN:clientinfo: window @ClientInfo | titlebar @ClientInfo from $sock($sockname).ip

    on *:SOCKCLOSE:clientInfo: echo -s *** Closed connection with $sock($sockname).ip

    on *:SOCKREAD:clientInfo:{
      if ($sockerr > 0) return
      :nextread
      sockread %temp
      if ($sockbr == 0) return
      if (%temp) { aline @ClientInfo %temp }
      ;writes the received text to the window @ClientInfo
     goto nextread
    }

    Of course you are not limited to just one way communication with socklisten, you can do both ways. It is possible to make mail,irc,web, and ftp servers. It is just a matter of know how.


    9. Worked example of acting as a direct chat server  Back to Top

    Worked example of acting as a direct chat server

    Server Side:

    /socklisten chat 6600 [This is typed in the command line, NOT placed in remotes]

    on *:SOCKLISTEN:chat: {
      echo -s *** Incoming chat connection, sending text to it.
      set %temp.socketname chatter $+ $rand(1,300)
      ;make up a half random name for the socket
      sockaccept %temp.socketname
      ;accept the socket
      window -e @Chat
      sockwrite -n %temp.socketname Connection Established. You are talking to $me
      ;opens a window, and tells the other person your nickname.
    }

    on *:SOCKREAD:chatter*: {
      if ($sockerr > 0) return
      :nextread
      sockread %temp
      if ($sockbr == 0) return
      if (%temp) { aline @Chat %temp }
      ;writes the received text to the window @Chat
      goto nextread
    }

    on *:INPUT:@Chat: {
      if ($left($1,1) != /) {
      ;makes sure your not trying to do a command
      sockwrite -n %temp.socketname < $+ $me $+ > $1-
      ;sends the text to the other person
      halt
    }

    on *:SOCKCLOSE:chatter*: aline @Chat >>> Connection has been closed at $time

    Client Side:

    To start a chat, type /sockopen clientchat ip 6600

    on *:SOCKOPEN:clientchat: {
       window -e @Chat
       sockwrite -n $sockname Connection Established. You are talking to $me
      ;opens the window @Chat and sends a message to the other person informing them of your nickname
    }

    on *:SOCKCLOSE:clientchat: aline @Chat >>> Connection has been closed at $time

    on *:INPUT:@Chat: {
      if ($left($1,1) != /) {
        ;checks to see if you are not doing a command
        sockwrite -n ClientChat < $+ $me $+ > $1-
        ;sends your text to the other person
        halt
      }
    }

    on *:SOCKREAD:clientchat: {
      if ($sockerr > 0) return
      :nextread
      sockread %temp
      if ($sockbr == 0) return
      if (%temp) { aline @Chat %temp }
      ;writes the received text to the window @Chat
      goto nextread
    }



    10. Protocol? What Protocol?  Back to Top

    A protocol, simply put, is the format in which clients and servers interact with each other. There is not one protocol, but many. WWW,IRC,E-mail,FTP,etc... all have there own protocols. Without knowing the correct protocol, you will not be able to successfully interact with the server or client. If you ever need to find the protol for a type of server/connection, try searching http://www.altavista.com/ for "server-type protocol" i.e. "http protocol". Or use mIRC to help find it - or at least some of it.

    I have used mIRC to find out the format for requesting a file from a web server. I used the on SOCKLISTEN and on SOCKREAD events.
    Example:

    In Remotes I put:

    on *:SOCKLISTEN:www: {
      ;this event acts as a server, thus when the web browser connects to it, the web browser
      ;will see it as a web server and act like normal.
      sockaccept web $+ $ticks
      echo -s *** Connection Accepted.
    }

    on *:SOCKREAD:web*: {
      sockread %temp
      if (%temp) { echo -s %temp }
      ; echo-s the text sent by the browser to your status window.
      ;The text sent is based on the HTTPd protocol.
    }

    In the status window I typed /socklisten www 80

    Then I opened Netscape and connected to http://127.0.0.1/ (This should try to connect to your computer).
    Goto mIRC and look in your status: you should see some text, these lines are what netscape uses to get files from the web. So what you saw was your browser using the httpd protocol to retrieve a file from the web. Now we use what we learned and reverse the process. Instead of being the server, we then act as the client.

    A httpd client example was done previously in this introduction. You can try to modify this process for use with other types of applications such as an IRC client.


    11. The Sockmark  Back to Top

    The /sockmark command fills the .mark attribute of a socket with the specified info for later reference via the $sock().mark property. If you do not specify any text, the mark is cleared. The mark can hold up to 512 bytes.

    Consider this like each socket can have an individual variable. The command to '/set its variable' is /sockmark sockname text. (Similar to /set %var value if you ask me).

    To recall the mark attribute simply use $sock(sockname).mark

    Example:

    Lets say you have a socket script which connects to an IRC server using the socket "server". Each time you change nicks on the IRC Server, you could get your script to write the new nickname into the sockets mark attribute via something like: /sockmark server NEWNICK

    Then to recall it (perhaps for use with a custom identifier) use $sock(server).mark. Lets say we did make a custom identifier that returned our current nickname, it would use the .mark attribute as shown:

    alias socknick if ($sock(server).mark != $null) { return $sock(server).mark }

    NOTE: You may also use wildcards when using the /sockmark command to set multiple matching sockets to that same information.

    Other types of scripts where the sockmark will come in handy are file downloaders (keep track of header info), and party lines (keep track of nickname, access, other preferences).


    12. Socket Identifiers  Back to Top

    mIRC Sockets only have a few identifiers. Below is a list with a short explanation:

    Identifier: Usage:
    $Sock(Name,N).property Properties (all optional): name, port, ip, status, sent, rcvd, sq, rq, ls, lr, mark, type, saddr, sport, to

    .name returns the name of the socket (e.g. $sock(a*,1).name will return the name of the first socket starting with a.

    .port returns what port the socket is on (Note that when you ACCEPT a socket, it can be using a different port from the port you listened to/accepted from)

    .ip returns the IP Address of the socket. (e.g. $sock(a*,1).ip returns the IP of the first socket that starts with a.

    .status returns the status of the socket (either connecting or active).

    .sent and .rcvd return the number of bytes sent and rcvd over that connection so far.

    .sq and .rq return the number of bytes queued in the send and receive buffers respectively. A use for this is to check if there is room for more data to be sent/received to/from the socket.

    .ls and .lr return the number of seconds since the connection last sent and last received info.

    .mark is a user storage area max. 512 bytes (see /sockmark). It just returns the value in the specififed sockets storage area. You can set this value via /sockmark sockname value.

    .type returns the socket type, TCP or UDP

    .saddr and .sport return the source address and port of the last received UDP packet.

    .to returns the number of seconds the socket has been open.

    NOTE: The N parameter is OPTIONAL. Also, if you want to return the number of all open sockets use $sock(*,0) not $sock(0).
     

    $Sockname This identifier returns the name of the current socket. It can only be used from within a socket event or an alias called by a socket event. 

    One of the most common uses for this identifier is in events where all or part of the socket name is unknown to you
    (See Acting As A Server).

    e.g:
    $sock($sockname).property
    /sockwrite -n $sockname data
    /sockclose $sockname
    etc....

    $Sockerr $sockerr is set to a value after each socket command/event and must be checked after each socket command and before processing an event to see if an error occurred.

    OK I admit I'm slack here, and hardly ever use this identifier.

    $Sockbr $sockbr is set to the number of bytes read by a /sockread command. It is used to test whether any information was in fact read from the buffer.

    This lets you stop an event before the script starts interperating it, thus saving alot of error messages.

    $Portfree(N) Returns $true if the specified port number is not in use, otherwise returns $false.

    e.g. $portfree(6667) would return $true is the port 667 was FREE, else it would return $false isf something was using that port.