随笔 - 355  文章 - 8 评论 - 509 trackbacks - 19

这个是我最近写的一个记录调试信息的类,基本功能和以前写过的UdpTraceListener类似:通过UDP数据报文发送调试信息发送出去。为了方便调试,增加了颜色和一些简单的指令功能。感觉还比较方便,这里记录一下,以备后续使用。

    static class Debug
    {
        static UdpClient client;
        static Debug()
        {
            client = new UdpClient();
            client.Connect(new IPEndPoint(IPAddress.Loopback, 3001));

            Clear();
        }

        public static void Write(ConsoleColor color, object obj)
        {
            Write(color, obj.ToString());
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public static void Write(ConsoleColor color, string message)
        {
            if (message == null)
                return;

            var dbgMsg = message;
            if (dbgMsg.Length > 80)
                dbgMsg = dbgMsg.Substring(0, 70) + "..." + Environment.NewLine;

            Console.ForegroundColor = color;
            Console.Write(dbgMsg);

            SendCmd(0xff, (byte)color);
            SendMsg(message);
        }

        public static void WriteLine(ConsoleColor color, object obj)
        {
            if (obj == null)
                return;

            WriteLine(color, obj.ToString());
        }

        public static void WriteLine(ConsoleColor color, string message)
        {
            Write(color, message + Environment.NewLine);
        }

        public static void Clear()
        {
            Console.Clear();
            SendCmd(0xfe, 0);
        }

        static void SendCmd(byte cmdType, byte cmdInfo)
        {
            client.Send(new byte[] { cmdType, cmdInfo }, 2);
        }

        static void SendMsg(string msg)
        {
            var data = Encoding.Default.GetBytes(msg);
            client.Send(data, data.Length);
        }
    }

posted @ 2012-05-12 15:33 天方 阅读(7) 评论(0) 编辑

在前文中介绍过,MSN协议中服务器分为NS(NotificationServer)和SB(Switchboard)等几种,其中NS主要用来传输控制命令,而SB则用来传输会话,每个会话对应着一个SB。在一个MSN客户端中,是连接着一个NS和多个SB的。

创建会话

这里以两个客户端ClientA和ClientB和交谈为例,假定ClientA创建一个会话,并邀请ClientB参加会话,则它们之间的交互过程如下:

1. ClientA发送XFR命令创建SessionBoard,服务器返回SessionBoard地址

    >>> XFR 15 SB\r\n
    <<< XFR 15 SB 207.46.108.37:1863 CKI 17262740.1050826919.32308\r\n

服务器的XFR应答消息中包含三个参数,第一个参数为SB服务器的地址,第二个参数为认证类型,固定为CKI,第三个参数为后面服务器的认证Key值,后面会用到。

2. ClientA连接SessionBoard,并发送USR命令进行身份认证。

    <o> Client Connects to 207.46.108.37 1863 (Switchboard)
    >>> USR 1 example@passport.com 17262740.1050826919.32308\r\n
    <<< USR 1 OK example@passport.com Example%20Name\r\n
    <o> Continue SB Session . . .

USR认证时携带两个参数,第一个参数是当前用户,第二个参数即是前面的XFR应答消息中携带的Key值。

3. ClientA发送CAL命令邀请ClientB

    >>> CAL 2 name_123@hotmail.com\r\n
    <<< CAL 2 RINGING 11752013\r\n

PS:服务器的CAL应答是等成功邀请上ClientB后才回应的,如上图所示。但ClientA感知不到这个过程,故这里就放在一块儿了。

4. NS发送RNG命令通知ClientB参加会话,ClientB根据RNG命令中SessionBoard地址,并发送ANS命令进行身份认证。

    <<< RNG 11752013 207.46.108.38:1863 CKI 849102291.520491113 example@passport.com Example%20Name\r\n
    <o> Client Connects to 207.46.108.38 1863 (Switchboard)
    >>> ANS 1 name_123@hotmail.com 849102291.520491113 11752013\r\n
    <<< IRO 1 1 2 example@passport.com Mike\r\n
    <<< IRO 1 2 2 myname@msn.com My%20Name\r\n
    <<< ANS 1 OK\r\n
    <o> Continue SB Session . . .

从这个协议中不难看出,MSN协议设计时就考虑了多方通信的支持的,只要重复3,4步即可,要实现多方通信主要这里就不累述了。

从常见会话过程中可以看出,建立一个会话时,客户端分为主动连接和被动连接两种,主动连接方主要完成1、2、3这三步,而被动连接方主要完成第4步。

退出会话

退出会话交互过程如下:

1. 主动退出方对SB发送OUT命令,SB则自动中断连接。

    >>> OUT\r\n
    <o> Switchboard Closes Connection

2. SB对会话中的其它成员发送BYE命令,通知其它成员会话有成员退出,如果只剩下一个成员,则和该成员的连接也会被中断。

    <<< BYE example@passport.com 1\r\n
    <o> Switchboard Closes Connection

另外,如果只有两个成员,且他们之间5分钟都没有活动,则服务器会对两个成员都发送BYE命令,此时的交互方式如下。

这时会话双方都是被动退出,交互过程和前面一样,就不累述了。

从前面的交互过程中可以看出,退出时也分为主动方和被动方:主动方需要发送OUT命令,被动方则需要响应BYE通知,还是比较简单的。

开始会话

MSN的对话都是在SB上进行的,并且对话信息都是通过MSG类型的消息发送的,它和我们打电话的过程非常类似。一个简单的对话过程如下:

  1. ClientA把消息发给SB

    >>> MSG 4 N 133\r\n
    MIME-Version: 1.0\r\n
    Content-Type: text/plain; charset=UTF-8\r\n
    X-MMS-IM-Format: FN=Arial; EF=I; CO=0; CS=0; PF=22\r\n
    \r\n
    Hello! How are you?
    <<< ACK 4

  2. SB服务器把消息转发给ClientB(如果是多方通话,则转发给其它的每个成员)

    <<< MSG example@passport.com Mike 133\r\n
    MIME-Version: 1.0\r\n
    Content-Type: text/plain; charset=UTF-8\r\n
    X-MMS-IM-Format: FN=Arial; EF=I; CO=0; CS=0; PF=22\r\n
    \r\n
    Hello! How are you?

  3. ClientB把回复消息发给SB
  4. SB把ClientB的回复消息转发给ClientA(如果是多方通话,则转发给其它的每个成员)

从上可以看出,步骤3,4和步骤1,2是完全一样的,也就是说,MSN对话是不区分发送方和接收方的,所有交互都是通过SB转发,发送的消息都是单向的,接收消息都是靠通知而不是应答。这就意味着:发送方只能知道消息是否发送成功,并不能保证一定会应答,接收方收到消息通知时,并不要求要回应答。这一点和我们实际的交谈过程也是一样的。

 

除了类型为"text/plain"的对话内容通知消息外,另外一个比较常用的通知为那种常见的"xxx 正在输入…"的通知,这个消息的格式为:

    MIME-Version: 1.0\r\n
    Content-Type: text/x-msmsgscontrol\r\n
    TypingUser: example@passport.com\r\n
    \r\n
    \r\n

如果要实现一个较为友好的客户端,需要响应和发送此消息。

由于我最初只打算实现个MSN机器人,除了这两个消息外,其它的消息(如闪屏震动,文件发送等)我都没有实现,它们的处理方式也没有太大难度,如果感兴趣可以参考我前面的参考文献地址自行实现一下,就不要在留言中问我如何实现了。

代码实现:

View Code
  1     class Conversation
  2     {
  3         public IObservable<MsnMessage> MessageNotify { getprivate set; }
  4         public IObservable<string> StateNotify { getprivate set; }
  5         public IObservable<string> LogoutNotify { getprivate set; }
  6 
  7         MsnServer sb;
  8         private Conversation(MsnServer sb)
  9         {
 10             this.sb = sb;
 11 
 12             MessageNotify = from msg in sb.NotifyMsgs
 13                             where msg.Headers.ContainsKey("Content-Type") && msg.Headers["Content-Type"].StartsWith("text/plain")
 14                             select msg;
 15 
 16             StateNotify = from msg in sb.NotifyMsgs
 17                           where msg.Headers.ContainsKey("Content-Type") && msg.Headers["Content-Type"].StartsWith("text/x-msmsgscontrol")
 18                           select msg.Paras[0];
 19 
 20             LogoutNotify = from notify in sb.NotifyCmds
 21                            where notify.CmdType == "BYE"
 22                            select notify.Paras[0];
 23 
 24             MessageNotify.Subscribe(async (msg) =>
 25                 {
 26                     Debug.WriteLine(ConsoleColor.White, "### Recv: " + msg.Content);
 27                     await Send(new string(msg.Content.Reverse().ToArray()), msg.Headers);
 28                 });
 29 
 30             StateNotify.Subscribe(account =>
 31                 {
 32                     Debug.WriteLine(ConsoleColor.White, string.Format("#-# {0} is typing ", account));
 33                 });
 34 
 35             //Test();
 36         }
 37 
 38         //async void Test()
 39         //{
 40         //    await Invite("tianfangxyz@msn.com");
 41         //    await SendMessage("Hello! How are you?");
 42         //}
 43 
 44         public async Task<MsnCommand> Invite(string user)
 45         {
 46             return await sb.SendAsync("CAL", user);
 47         }
 48 
 49 
 50         public Task<MsnCommand> SendMessage(string content, string font = "Arial"int size = 22)
 51         {
 52             Contract.Requires(!string.IsNullOrEmpty(content));
 53 
 54             var headers = new Dictionary<stringstring>();
 55             headers["MIME-Version"] = "1.0";
 56             headers["Content-Type"] = "text/plain; charset=UTF-8";
 57             headers["X-MMS-IM-Format"] = string.Format("FN={0}; EF=I; CO=0; CS=0; PF={1}", font, size);
 58 
 59             return Send(content, headers);
 60         }
 61 
 62         public async Task<MsnCommand> Send(string content, Dictionary<string,string> headers)
 63         {
 64             Contract.Requires(headers != null);
 65 
 66             var msg = new MsnMessage(MsnServer.NewTransactionId(), headers, content);
 67             return await sb.SendAsync(msg);
 68         }
 69 
 70         public static async Task<Conversation> CreateAsync(MsnServer ns,string me)
 71         {
 72             Contract.Requires((ns != null) && (!string.IsNullOrWhiteSpace(me)));
 73 
 74             var reply = await ns.SendAsync("XFR""SB");
 75             var hostAndPort = reply.Paras[1];
 76             var key = reply.Paras[3];
 77 
 78             var sb = await MsnServer.CreateAsync(hostAndPort);
 79             await sb.SendAsync("USR", me, key);
 80 
 81             return new Conversation(sb);
 82         }
 83         
 84         public static IObservable<Conversation> GetInvitations(MsnServer ns, string me)
 85         {
 86             var conversations = from cmd in ns.NotifyCmds
 87                                 where cmd.CmdType == "RNG"
 88                                 from conversation in Observable.FromAsync(() => Conversation.CreateAsync(cmd, me))
 89                                 select conversation;
 90             conversations.Subscribe();
 91             return conversations;
 92         }
 93 
 94         static async Task<Conversation> CreateAsync(MsnCommand rng, string me)
 95         {
 96             var hostAndPort = rng.Paras[0];
 97             var id = (int)rng.TransactionId;
 98             var key = rng.Paras[2];
 99 
100             Debug.WriteLine(ConsoleColor.White, "ProcessInvitation: " + hostAndPort);
101             var sb = await MsnServer.CreateAsync(hostAndPort);
102             await sb.SendAsync("ANS", me, key, id);
103             return new Conversation(sb);
104         }
105     }

 

到此我的MSN系列文章基本完结了,由于时间和精力有限,并不打算继续深入研究和完善。相关代码我基本都在文中已经贴出来了,总共也就3百行左右,非常简单。我实现代码只是为了练习Async编程,因此只实现了一个雏形,并不完善,如果在项目中需要实现MSN协议建议使用MSNSharp等开源库,我并不打算放出完整的工程下载,文章写完后相关代码即刻删除,本地也不会保留,请勿留言索取。

如果文中关于MSN协议的说明有误,欢迎留言指正。但如果是使用本文代码中遇到了问题,请不要留言求助,我没有足够的精力来一一指正,望见谅。

 

posted @ 2012-05-06 15:32 天方 阅读(15) 评论(0) 编辑

前面的文章里已经介绍了如何实现用户登录,本文中主要解决目前遗留下的一个问题:看到远端好友状态和让远端好友看到你的状态。

一、联系人同步

要看到远端好友的状态,首先需要进行的一步就是联系人同步。联系人同步主要是通过SYN命令来进行的,SYN命令格式如下:

    >>> SYN 1 0\r\n
    <<< SYN 1 2 1\r\n

SYN命令只有一个参数,就是当前的同步序号,如果传0即可实现全同步,传大于0的数字时可以实现增量同步,当前同步到的最新序号可以从应答中获取。

服务器收到SYN命令后,然后就会把当前联系人的设置通过通知的方式一一发送过来,一个示例如下:

    >>> SYN 1 0\r\n
    <<< SYN 1 139 5 4\r\n
    
    <<< GTC A\r\n
    <<< BLP AL\r\n
    
    <<< PRP PHH 01%20234\r\n
    <<< PRP PHM 56%20789\r\n
    
    <<< LSG 0 Other%20Contacts 0\r\n
    <<< LSG 1 Coworkers 0\r\n
    <<< LSG 2 Friends 0\r\n
    <<< LSG 3 Family 0\r\n
    
    <<< LST principal1@passport.com principal1 4\r\n
    <<< LST principal2@passport.com principal2 10\r\n
    <<< LST principal3@passport.com principal3 11 1,3\r\n
    <<< LST principal4@passport.com principal4 11 0\r\n
    <<< BPR PHH 01%20234\r\n
    <<< BPR MOB Y\r\n
    <<< LST principal5@passport.com principal5 12\r\n
    <<< LST principal6@passport.com principal6 11 2\r\n
    <<< BPR PHW 45%206789\r\n

由此可见,这里的通知还是比较多的,有GTC、BLP、LSG、LST等,这里就不一一介绍了,要详细了解的话可以看一下这个网页:http://www.hypothetic.org/docs/msn/index.php

由于通知消息是异步发送的,这里就存在一个不知道什么时候结束的问题。我这里的做法是:在发送SYN命令后,紧跟着发送一个Ping命令PNG,这样,可以简单的认为收到QNG时同步已经完成,此时交互如下:

    >>> SYN 1 0\r\n
    <<< SYN 1 139 5 4\r\n
    >>> PNG/r/n
    
    <<< GTC A\r\n
    <<< BLP AL\r\n
    
    <<< PRP PHH 01%20234\r\n
    <<< PRP PHM 56%20789\r\n
    
    <<< LSG 0 Other%20Contacts 0\r\n
    <<< LSG 1 Coworkers 0\r\n
    <<< LSG 2 Friends 0\r\n
    <<< LSG 3 Family 0\r\n
    
    <<< LST principal1@passport.com principal1 4\r\n
    <<< LST principal2@passport.com principal2 10\r\n
    <<< LST principal3@passport.com principal3 11 1,3\r\n
    <<< LST principal4@passport.com principal4 11 0\r\n
    <<< BPR PHH 01%20234\r\n
    <<< BPR MOB Y\r\n
    <<< LST principal5@passport.com principal5 12\r\n
    <<< LST principal6@passport.com principal6 11 2\r\n
    <<< BPR PHW 45%206789\r\n
    <<< QNG/r/n

不过MSN协议并没有规定QNG一定会在SYN通知完成后才通知,这种做法并不一定可靠,一般情况下貌似也没有什么大问题。

前面已经介绍过,SYNC的应答通知有许多种类型,要完全实现还是比较麻烦的。另外,要实现完善的话,只靠这个通知有时还不够,往往还需要根据用户人的账户名称获取详细信息。由于时间有限,我只弄了一个同步所有人的简单实现。也没有对同步结果进行任何处理。请看本文的朋友不要进一步询问相关问题,大可自己参考前面提到的那个网页自行实现。

View Code 
 1     class Sync
 2     {
 3         public static async Task SyncAll(MsnServer server)    //TODO 增量同步
 4         {
 5             var accounts = server.ReceiveUntilPingBack();
 6             accounts.Subscribe(i => Debug.Write(ConsoleColor.Gray, ">>> " + i));
 7  
 8             await server.SendAsync("SYN"0);
 9             await server.SendPingAsync();
10  
11             await accounts;
12         }
13

 

二.接收好友状态变化

同步完联系人后,所有的联系人默认都是离线状态。对于那些非离线状态的联系人,服务器会通过NLN,ILN,FLN命令来通知联系人状态的变化。一个状态通知的示例如下:

    <<< ILN AWY example@passport.com example%20display%20name 268435492

它有四个参数,其中第一个参数表示当前的状态,目前MSN支持如下几种状态:

第二个参数是用户账号,第三个参数是昵称(不完整),第四个参数是ClientID。其内容具体参看http://www.hypothetic.org/docs/msn/notification/presence.php的Client Identification numbers一节,这里就不详细介绍了。

除了ILN外,

三. 设置我的状态

设置自己的状态主要通过CHG命令设置,示例如下:

    >>> CHG 12 NLN 0\r\n
    <<< CHG 12 NLN 0\r\n

CHG命令有两个参数,第一个参数是状态,第二个是ClientID。设置成功后,服务器会回一个CHG应答。如果过于频繁设置状态,则会收到 800错误 。

代码附上:

View Code
 1     enum State
 2     {
 3         AWY,    //离开
 4         NLN,    //可用
 5         BSY,    //忙碌
 6         IDL,    //发呆
 7         BRB,    //马上回来
 8         PHN,    //打电话
 9         LUN,    //外出就餐
10         HDN,    //隐身
11     }
12 
13     class StateMgr
14     {
15         public State State { getprivate set; }
16 
17         MsnServer server;
18         public StateMgr(MsnServer server, State state = State.NLN)
19         {
20             this.server = server;
21             ProcessStateChgNotify();
22 
23             SetState(state);
24         }
25 
26         public async void SetState(State value)
27         {
28             if (this.State == value)
29                 return;
30 
31             this.State = value;
32             await server.SendAsync("CHG", State ,6);
33         }
34 
35         void ProcessStateChgNotify()
36         {
37             var notify = server.NotifyCmds;
38 
39             notify.Where(i => (i.CmdType == "FLN") || (i.CmdType == "NLN")).Subscribe(i =>
40                 {
41                     var account = i.Paras[0];
42                     Debug.WriteLine(ConsoleColor.Cyan, ">>> " + account + "离线了");
43                 });
44             notify.Where(i => i.CmdType == "ILN").Subscribe(i =>
45                 {
46                     var state = i.Paras[0];
47                     var account = i.Paras[1];
48                     var name = i.Paras[2];
49                     Debug.WriteLine(ConsoleColor.Cyan, ">>> " + account + "状态变为" + state);
50                 });
51         }
52     }

 

posted @ 2012-05-06 09:35 天方 阅读(16) 评论(0) 编辑

一、MSN中的Ping

在基于TCP的协议中,一般都会都会设计Ping命令来检测并踢掉僵死的连接,从而节省服务器资源。在成功登陆到服务器后,一般立即会收到到服务器发送的Ping命令CHL,如过不进行正确的应答的话,则会被服务器踢掉。为了后面的功能能正常运行,首先我们必须学习一下MSN的Ping命令。

MSN协议中的Ping命令有两种:服务器端发送的和客户端发送的。客户端发送的Ping命令非常简单:客户端发送PNG命令,服务器应答QNG,都不携带任何参数。
    >>> PNG/r/n
    <<< QNG/r/n

除了客户端Ping服务器外,服务器也会不定时(一般登录后就会收到一次)对客户端发送Ping命令CHL。CHL命令之间没有固定周期,它包含两个参数:第一个总会是0,第二个被称作"Challenge String",客户端会根据它来生成应答。CHL命令格式如下:
    <<< CHL 0 15570131571988941333/r/n

接收Challenge命令后,必须在50秒内发送应答命令QRY到服务器,否则将被断开。应答命令QRY格式如下:

    <<< CHL 0 15570131571988941333/r/n
    >>> QRY 1049 msmsgs@msnmsgr.com 32/r/n 
        8f2f5a91b72102cd28355e9fc9000d6e (no newline)

QRY命令主要有三个参数,第一个参数是客户端ID,第二个参数是32,第三个参数是客户端应答字符串。其实前面介绍过:QRY是一个有效载荷命令,它的第二个参数是后面的内容(应答字符串),因为应答字符串长度是32,因此第二个参数也就固定为32。因此需要我们填充的是第一个参数客户端ID和第三个参数应答字符串。下面就介绍一下这两个参数的生成方式。

首先看一张表:

客户端ID

客户端标识码

msmsgs@msnmsgr.com

Q1P7W2E4J9R8U3S5

PROD0038W!61ZTF9

VT6PX?UQTM4WM%YR

PROD0058#7IL2{QD

QHDCY@7R1TB6W?5B

PROD0061VRRZH@4F

JXQ6J@TUOGYV@N0M

其中客户端ID可以取上表中的一个(貌似也不是任取一个就可以的),而应答字符串则是CHL命令的Challenge String参数和客户端标识码的MD5值(这个也是为什么长度总是32的原因)。

例如,对于前面的CHL命令,我们取客户端字符串为msmsgs@msnmsgr.com,对应的客户端标识码为Q1P7W2E4J9R8U3S5,则对应的应答码的为:MD5(15570131571988941333 + Q1P7W2E4J9R8U3S5) = 8f2f5a91b72102cd28355e9fc9000d6e

有了上面的基础后,用C#实现一个客户端的主动Ping命令和对服务器的ping命令自动应答就十分简单了,代码如下:

View Code
 1     static class Ping
 2     {
 3         public static async Task SendPingAsync(this MsnServer server)
 4         {
 5             await server.SendAsync(new MsnCommand("PNG",null));
 6         }
 7 
 8         public static IObservable<MsnCommand> ReceiveUntilPingBack(this MsnServer server) 
 9         {
10             return server.NotifyCmds.TakeWhile(i => i.CmdType != "QNG"); 
11         }
12 
13         /// <summary>
14         /// 应答服务器的ping命令
15         /// </summary>
16         public static void RegistPingReply(this MsnServer server)
17         {
18             var clientId = "PROD0061VRRZH@4F";
19             var clientCode = "JXQ6J@TUOGYV@N0M";
20             var md5 = new System.Security.Cryptography.MD5CryptoServiceProvider();
21 
22             server.NotifyCmds.Where(i => i.CmdType == "CHL")
23                 .Subscribe(async (cmd) =>
24                 {
25                     var data = md5.ComputeHash(UnicodeEncoding.UTF8.GetBytes(cmd.Paras[0] + clientCode));
26                     var replyKey = string.Join("", data.Select(i => i.ToString("x2")));
27                     var replyCmd = new MsnCommand("QRY"0, clientId, "32") { Content = replyKey };
28                     await server.SendAsync(replyCmd);
29                 });
30         }
31     }

 

二、退出登录

介绍完Ping后,顺带介绍一下上章中忘记介绍的退出登录。

退出登录最简单的方法就是正常关闭socket连接,这种方式也是非常简单有效的。但还有一种方式是对服务器发送OUT指令,服务器受到OUT指令后会主动关闭socket连接。
    <<< OUT/r/n
    <o> Server closes connection

除了上面介绍的客户端主动退出登录外,服务器有时也会发送OUT指令强制客户端退出登录。主要有如下两种情况:

1. 当服务器要进行维护时,将会发送OUT SSD — server shutting down,发送后就立即关闭连接。
    <<< OUT SSD/r/n
    <o> Server closes connection

2. 当有人在别处用你的账号登录时,原来的连接将被关闭,新连接将被建立。关闭连接前,NS将给客户端发送OUT命令并携带参数OTH

    <<< OUT OTH/r/n
    <o> Server closes connection

最后附带一下代码的实现。

View Code
 1     static class Logout
 2     {
 3         public static async void LogoutAsync(MsnServer server)
 4         {
 5             await server.SendAsync(new MsnCommand("OUT"null));
 6         }
 7 
 8         public static IObservable<string> GetLogoutNotify(MsnServer server)
 9         {
10             //CmdType有SSD和OTH两种
11             return server.NotifyCmds.FirstAsync(i => i.CmdType == "OUT")
12                 .Select(i => i.CmdType == "OTH""用户在其他地方登陆":  "服务器故障");
13         }
14     }

 

PS:本来打算在这篇文章中介绍一下如何实现设置和获取联系人的在线状态的,但由于如果不能正确实现对服务器的Ping应答的话,一般刚登上去就会被踢掉的,故先介绍一下。设置和获取联系人的功能待下篇文章中再介绍。

posted @ 2012-04-25 21:59 天方 阅读(35) 评论(0) 编辑
摘要: 上一篇文章里对MSN协议进行了简单的介绍,并实现了消息的解析,今天这里将简单地介绍一下如何实现MSN客户端登陆。阅读全文
posted @ 2012-04-22 10:09 天方 阅读(37) 评论(0) 编辑
摘要: 清明的时候学习一下C# 5.0的新特性async,顺带用它实现了一个C#的MSN客户端,实现了简单的通话功能。 第一篇主要介绍了MSN消息的格式和解析方法。阅读全文
posted @ 2012-04-20 23:39 天方 阅读(78) 评论(0) 编辑
摘要: 背景在传统方式下,很多网站为了实现即时通讯,所用的技术都是轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对伺服器发出HTTP request,然后由伺服器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向伺服器发出请求,然而HTTP request 的header是非常长的,里面包含的数据可能只是一个很小的值,这样会占用很多的带宽和服务器资源。 而比较新的技术去做轮询的效果是Comet,使用了AJAX。但这种技术虽然可达到双向通信,但依然需要发出请求,而且在Comet中,普遍采用了长链接,这也会大量消耗服务器带宽和资源。面对这种状况,HTML5定义了We阅读全文
posted @ 2012-04-02 15:15 天方 阅读(98) 评论(0) 编辑
摘要: 在WPF中,有时需要判断当前是否处于设计模式,以屏蔽设计器执行构造函数里面的部分功能public static bool IsInDesignMode(this Control control){return System.ComponentModel.DesignerProperties.GetIsInDesignMode(control);}阅读全文
posted @ 2012-03-18 14:45 天方 阅读(74) 评论(3) 编辑
摘要: .Net 4.5中增加了一个新的System.Net.Http.HttpClient名字空间(在 System.Net.Http.dll 中),用于发送 HTTP 请求和接收 HTTP 响应。基本操作和以前的HttpWebRequest相比,HttpClient更加简洁,下面就是一个下载www.windows.com页面的示例:string uri = "http://www.window...阅读全文
posted @ 2012-03-10 20:47 天方 阅读(214) 评论(0) 编辑
摘要: 今天见一道算法题,要求实现两个不限长度的正整数的乘法。感觉不难,便顺手做了一下。我这里的实现比较简单,基本上就是模拟算术运算,大概分成如下几步 125x11------- 125125--------1375将乘数每位与乘数相乘,获取到一个m*n的矩阵将矩阵错位相加,得到输出数组对输出中数组进行进位运算一个简单的实现如下 static string Multiplication(string num1, string num2){var input1 = num1.Select(i => (int)(i - '0'));var input2 = num2.Select(i阅读全文
posted @ 2012-03-06 23:58 天方 阅读(71) 评论(0) 编辑
仅列出标题  下一页