Jump to content

Basic Guide to Sockets


TG007

Recommended Posts

Release 5 Written by Pasmal

Courtesy of http://helpdesk.zaz.net

 

A few words before I start.

Back to the basics: Opening a Socket

Back to the basics: Closing a Socket

Writing to a socket

Reading data from a socket

A worked example of a basci HTTP Client

Acting as a server

Worked example of acting as a server

Worked example of acting as a direct chat server

Protocol? What Protocol?

The Sockmark

Socket Identifiers

A few words before I start.

 

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" smile.gif

Back to the basics: Opening a Socket

 

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. Below are some common 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

Back to the basics: Closing a Socket

 

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.

 

 

 

Writing to a socket

 

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 basiclly 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 queing 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 smile.gif Of course I only use this method for large data sends (i.e. files over a few k's) in my mIRC Web Server.

 

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.

 

 

Reading data from a socket

 

 

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
   }

 

A worked example of a basic HTTP Client

 

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:
   ; For more information on the HTTP protocol click here.
   }

   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)

Acting as a server

 

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.

Worked example of acting as a one way server

 

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. Its just a matter of know how.

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
}
       

 

Protocol? What Protocol?

 

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. #HelpDesk's Documentation site has an Internet Protocol Section as well.

 

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.

The Sockmark

 

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).

Socket Identifiers

 

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.

Link to comment
Share on other sites

Guest
This topic is now closed to further replies.
×
×
  • Create New...