Introduction
MSN messenger is a well known program. It lets you communicate with other users through Instant Messaging. Some people like to write their own MSN messenger, so that they can make some own adjustments and cool features. I have written, together with another student, our own MSN client in C#. Recently Microsoft no longer supported protocols below MSNP 8. So I had to adjust our program to use MSNP9 (it used MSNP7).
After I have posted an earlier article about MSN, people asked me to give some more information on how to connect to the MSN servers.
So with this little article I would like to explain how you can connect to MSN with the use of the MSNP9 protocol. This article tells you what commands you have to send, and how to react to it. It uses an article that I have posted before. You can find it here.
Using the code
You can use this code in your own program, or you can look and see how you can connect to MSN in C#. The demo programs shows how you can use the following code.
What do we need?
First we use a separate class that will connect to the MSN server. We call it MSNConnection
. Next we build another class that handles the commands that we receive from the servers, lets call this class ServerCommand
.
Next we need to declare some variables that we will use in the class MSNConnection
.
private long _transactionID = 0;
private TcpClient _socket;
private NetworkStream _stream;
private StreamReader _reader;
private StreamWriter _writer;
The transactionID
is sent with almost every message that we send (also in chat sessions). The _reader
and _writer
are the streams from the socket.
Now we have the above, let's define some functions that will do the work for us. First we need to initialize the socket and StreamReader
s.
_transactionID = 0;
_socket = new TcpClient(host, port);
_stream = _socket.GetStream();
_reader = new StreamReader(_stream, Encoding.ASCII);
_writer = new StreamWriter(_stream, Encoding.ASCII);
_writer.AutoFlush = true;
We call this function ConnectSocket
and it takes two parameters (host
and port
). Every time we make a new connection the transactionId
is set to zero. If we make a function that creates the socket, why not make a function that closes the socket. Let's call it dispose
.
if( _socket != null )
{
_reader.Close();
_writer.Close();
_stream.Close();
_socket.Close();
_socket = null;
}
We are making functions that read and write to the StreamReader
and StreamWriter
. First we define the StreamWriter
. We then make a function that builds the string: WriteCommand
. It takes 3 parameters: first is the command to send, next the parameters and last the option to leave the transactionId
out.
string line;
if (bSendId)
line = string.Format("{0} {1} {2}", command, _transactionID, parameters);
else
line = string.Format("{0} {1}", command, parameters);
WriteLine(line, true);
The function WriteLine
eventually writes the string to the StreamWriter
. This function has two parameters first is the entire string and next is the option to send only a string and no end character.
if (writeNewLine)
_writer.WriteLine(line);
else
_writer.Write(line);
_transactionID++;
We have defined some writing functions. We also need to read the information. Let's call this function ReadCommand
. This function reads from the reader, if there is nothing in the socket then makes an empty ServerCommand
, else we make a ServerCommand
with the given response.
string line = _reader.ReadLine();
Console.WriteLine("Reading: " + line);
if (line == null)
{
Console.WriteLine("Nothing received");
return new ServerCommand();
}
else
{
return new ServerCommand(line);
}
You noticed that I use the ServerCommand
object. Lets look at the ServerCommand
class.
private string _cmdID;
private string _line;
private string[] _params;
public ServerCommand(string line)
{
_line = line;
_cmdID = line.Substring(0, 3);
if (!(_cmdID == "QNG"))
{
_params = line.Substring(4).Split(' ');
}
}
public ServerCommand()
{
_line = "";
_cmdID = "ERROR";
}
If we use the constructor with a valid line, then we will get a ServerCommand
with the right information. If we get a QNG
command then there will be no parameters. The command is always a three letter combination. If we use the constructor with no string, then the program knows that there is something wrong.
The other functions are used for retrieving the data from this class.
public string CommandName
{
get { return _cmdID; }
}
public string Param(int index)
{
return _params[index];
}
Now all the functions are explained above, we can make the Connect
function. This function needs two parameters: first is a valid username that has a passport, next is the password for that username.
First we are going to connect to the server.
string host = "messenger.hotmail.com";
int port = 1863;
ConnectSocket(host, port);
Now we are going to write the server commands, the first command we are going to write is the VER
command. This command indicates what protocol we are using. We are using protocol MSNP9. We read the result in a new ServerCommand
Object.
ServerCommand ServCom
WriteCommand("VER", "MSNP9 CVRO", true);
ServCom = ReadCommand();
Next we are going to check if we received the right information, if not then exit this function.
if (ServCom.CommandName != "VER")
{
return 1;
}
Now we have to send the CVR
command, the parameters are the same like a real MSN messenger client. The server will response with a CVR
command
and a download place where you can get a newer MSN messenger, we just ignore this.
WriteCommand("CVR",
"0x0409 win 4.10 i386 MSNMSGR 5.0.0544 MSMSGS " + UserName, true);
ServCom = ReadCommand();
After this is successful, we send de USR
command with a TWN
parameter and your username. TWN
stands for TWEENER, this is based on the passport authentication.
WriteCommand("USR", "TWN I " + UserName, true);
ServCom = ReadCommand();
If the command was not the USR
command, it was probably the XFR
command, this indicates to us that we have to transfer to another server. In the result is the new host and port, parse it.
string[] arIP = ServCom.Param(2).Split(':');
host = arIP[0];
port = int.Parse(arIP[1]);
Now disconnect and connect again using the new host and port.
Dispose();
We were operating in a while
loop, so the connect sequence starts again. You have to send all the commands again, but this time it is to another server.
If the responsecommand
is USR
then we are going to connect to this server. The response will hold a ChallengeString
, we need this ChallengeString
to get a valid ClientTicket
.
if (ServCom.CommandName == "USR")
{
ChallengeString = ServCom.Param(3);
break;
}
With the given ChallengeString
we will get a valid clientticket
.
string clientticket = GetClientTicket(UserPassword,
UserName, ChallengeString);
This step is a rather large step. I have already wrote an article on this part, read all about it right here.
Finally now we have a ticketID
, send it to the server.
WriteCommand("USR", "TWN S " + clientticket, true);
ServCom = ReadCommand();
If we are in luck, we get the USR |transid| OK
message. This indicates that we have successfully connected to the MSN servers.
Let's get our username and our screen name, this information was send together with de USR
command.
_UserName = ServCom.Param(2);
_ScreenName = ServCom.Param(3);
Last we are going to notify that we are going online, you can put a number of initial status messages here. NLN
just means "Online", the rest are:
BSY
- Busy
IDL
- Idle
BRB
- Be Right Back
AWY
- Away
LUN
- Out to Lunch
PHN
- On the Phone
FLN
- Offline
HDN
- Hidden
WriteCommand("CHG", "NLN", true);
Now you are connected and everybody who have you in their list will see you online.
Right now, you have to get all the contacts, but that part is too big to explain right here.
Conclusion
Connecting to a MSN server is not that hard, you have to understand what to send and how to reply. Hopefully you have liked this article, so maybe now you want to write your own MSN messenger program.
Good luck!