导航

与MFC聊天服务器端配套的silverlight客户端

Posted on 2011-02-23 16:20  田园小蛙  阅读(524)  评论(0)    收藏  举报

  一个简单的聊天客户端,主要介绍如何与c++服务器端传递消息、解析消息。使用vs2010和silverlight4.0库开发。

  客户端提供连接服务器,发送和接收通讯消息(发送登陆消息,发送用户列表请求消息等)。

客户端运行后的界面:

解决方案:

一、客户端主逻辑

客户端的主逻辑都在MainPage.xaml.cs文件中

MainPage.xaml
<UserControl x:Class="ChatClientSL.MainPage"
xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d
="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc
="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable
="d" Height="300" Width="570" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">

<Grid x:Name="LayoutRoot" Background="White">
<Button Content="连接到" Height="23" HorizontalAlignment="Left" Margin="12,12,0,0" Name="btCon" VerticalAlignment="Top" Width="75" Click="btCon_Click" />
<Button Content="发送" Height="23" HorizontalAlignment="Right" Margin="0,265,190,0" Name="btSend" VerticalAlignment="Top" Width="80" Click="btSend_Click" />
<TextBox Height="23" HorizontalAlignment="Left" Margin="10,265,0,0" Name="textBoxSend" VerticalAlignment="Top" Width="284" KeyUp="textBoxSend_KeyUp" />
<ScrollViewer x:Name ="scrollChat" VerticalScrollBarVisibility="Auto" Background="White" Margin="10,41,190,41">
<TextBlock x:Name="textBlockShow" Text="" TextWrapping ="Wrap" Width="360" />
</ScrollViewer>
<TextBox Height="23" HorizontalAlignment="Left" Margin="93,12,0,0" Name="tbIP" VerticalAlignment="Top" Width="118" Text="127.0.0.1" />
<TextBox Height="23" HorizontalAlignment="Right" Margin="0,12,307,0" Name="tbPort" Text="4503" VerticalAlignment="Top" Width="46" />
<TextBox Height="23" HorizontalAlignment="Left" Margin="322,12,0,0" Name="tbName" Text="SL" VerticalAlignment="Top" Width="58" />
<ListBox Height="247" Margin="0,41,12,0" Name="listBox" VerticalAlignment="Top" HorizontalAlignment="Right" Width="172" />
<sdk:Label Height="28" HorizontalAlignment="Left" Margin="275,13,0,0" Name="label1" VerticalAlignment="Top" Width="47" Content="昵称:" FontSize="14" />
<sdk:Label Content="参与聊天的人" FontSize="14" Height="28" HorizontalAlignment="Left" Margin="422,13,0,0" Name="label2" VerticalAlignment="Top" Width="95" />
</Grid>
</UserControl>
MainPage.xaml.cs
/********************************************************************
author: 田园小蛙 ourtree@live.cn

purpose:
********************************************************************
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Text;
using System.Collections;
using System.Threading;
using System.Collections.ObjectModel;

namespace ChatClientSL
{
//消息类型
public enum EN_NETMSG_TYPE
{
ROLE_EVENT
= 1,//用户事件消息
CHAT_INFO = 2,//聊天内容消息
ROLE_LIST = 3//用户信息列表消息
}

public enum EN_NETMSG_ROLEEVENT
{
LOGIN
= 1,//用户登陆
VALIDE_LOGIN = 2,//验证用户登陆
REQ_ROLE_LIST = 3,//请求用户列表
ROLE_IN = 4,//连接
ROLE_OUT = 5//退出
}

//聊天类型
public enum EN_CHAT_TYPE
{
ROLE_COMMUNITY
= 1, //用户群聊
SYSTEM = 2//系统广播
}

//消息结束标识
public enum EN_NETMSG_ENDFLAG
{
Normal
= 0,//普通消息
End = 1//结束消息
}

public partial class MainPage : UserControl
{
NetSocket _netSocket;
//连接
string _strRoleName = "";//用户名
int _nRoleIndex = -1;//用户索引
ObservableCollection<string> _listRole = new ObservableCollection<string>();// 绑定的用户列表
Dictionary<int, string> _dictRole = new Dictionary<int, string>();//用户列表

string _strIP = "";//服务器IP
int _nPort = 4503;//服务器端口


public MainPage()
{
InitializeComponent();

Init();
}

private void Init()
{
_netSocket
= new NetSocket();//初始化网络连接

listBox.ItemsSource
= _listRole;

//收到消息
_netSocket.OnReceiveEvent += new NetSocket.ReceiveEventDelegate(_netSocket_OnReceiveEvent);

//网络连接情况
_netSocket.OnConnectEvent += new NetSocket.ConnectEventDelegate(_netSocket_OnConnectEvent);
}

/// <summary>
/// 刷新用户列表
/// </summary>
private void ReflashRoleList()
{
this.Dispatcher.BeginInvoke(
delegate
{
_listRole.Clear();

Dictionary
<int, string>.ValueCollection vc = _dictRole.Values;

foreach (string str in vc)
{
_listRole.Add(str);
}
}
);
}

/// <summary>
/// 处理连接事件
/// </summary>
/// <param name="enEvent">事件类型</param>
void _netSocket_OnConnectEvent(EN_CONNECT_EVENT_TYPE enEvent)
{
switch (enEvent)
{
case EN_CONNECT_EVENT_TYPE.CONNECT_OK:
{
string strMsg = "成功连接 " + _strIP;
ShowText(strMsg);

//发送登陆消息
NetMsg pNetMsg = _netSocket.NetMsgProcessor.GetSendMsg((int)EN_NETMSG_TYPE.ROLE_EVENT);
pNetMsg[
"nType"].IntValue = (int)EN_NETMSG_ROLEEVENT.LOGIN;
pNetMsg[
"szRoleName"].StrValue = _strRoleName;
_netSocket.SendMsg(pNetMsg.GetSendBuffer(), pNetMsg.BufferSize);
}
break;
case EN_CONNECT_EVENT_TYPE.CONNECT_FAILED:
{
string strMsg = "连接失败 " + _strIP;
ShowText(strMsg);
}
break;
default:
break;
}
}

/// <summary>
/// 处理接收到的消息
/// </summary>
/// <param name="pNetMsg"></param>
void _netSocket_OnReceiveEvent(NetMsg pNetMsg)
{
//在这里处理收到的消息
switch (pNetMsg.Type)
{
case (int)EN_NETMSG_TYPE.ROLE_EVENT:
{
int nRoleIndex = pNetMsg["nRoleIndex"].IntValue;
string strRoleName = pNetMsg["szRoleName"].StrValue;

switch (pNetMsg["nType"].IntValue)
{
case (int)EN_NETMSG_ROLEEVENT.VALIDE_LOGIN:
{
//处理登陆验证消息
if (strRoleName != _strRoleName)
{
_strRoleName
= strRoleName;
ShowName(_strRoleName);

_nRoleIndex
= nRoleIndex;
}

//发送用户列表请求消息
NetMsg pNetMsgReq = _netSocket.NetMsgProcessor.GetSendMsg((int)EN_NETMSG_TYPE.ROLE_EVENT);
pNetMsgReq[
"nType"].IntValue = (int)EN_NETMSG_ROLEEVENT.REQ_ROLE_LIST;
pNetMsgReq[
"szRoleName"].StrValue = _strRoleName;
pNetMsgReq[
"nRoleIndex"].IntValue = _nRoleIndex;
_netSocket.SendMsg(pNetMsgReq.GetSendBuffer(), pNetMsgReq.BufferSize);

_dictRole.Clear();
}
break;
case (int)EN_NETMSG_ROLEEVENT.ROLE_IN:
{
//处理用户上线消息

//加上线用户信息加到用户列表中
_dictRole.Add(nRoleIndex, strRoleName);
ReflashRoleList();

string str = strRoleName + " 上线了";
ShowText(str);
}
break;
case (int)EN_NETMSG_ROLEEVENT.ROLE_OUT:
{
//处理用户下线消息

//将用户从用户列表中移除
if (_dictRole.ContainsKey(nRoleIndex))
{
_dictRole.Remove(nRoleIndex);
ReflashRoleList();

string str = strRoleName + " 下线了";
ShowText(str);
}
}
break;
default:
break;
}
}
break;
case (int)EN_NETMSG_TYPE.CHAT_INFO:
{
//处理收到的聊天内容
switch (pNetMsg["nChatType"].IntValue)
{
case (int)EN_CHAT_TYPE.ROLE_COMMUNITY:
case (int)EN_CHAT_TYPE.SYSTEM:
{
//显示聊天内容
string strMsg = pNetMsg["szFromName"].StrValue + " 说: " + pNetMsg["szContent"].StrValue;
ShowText(strMsg);
}
break;
default:
break;
}
}
break;
case (int)EN_NETMSG_TYPE.ROLE_LIST:
{
//处理收到的用户信息列表消息

//提取消息的列表内容,更新用户列表
NetMsgSectionCollection[] secs = pNetMsg["info"].Sections;
for (int i = 0; i < secs.Length; i++)
{
NetMsgSectionCollection sec
= secs[i];

int nIndex = sec.StrCollection["nRoleIndex"].IntValue;
string strName = sec.StrCollection["szRoleName"].StrValue;

_dictRole[nIndex]
= strName;
}

if (pNetMsg.Head.endFlag.IntValue == (int)EN_NETMSG_ENDFLAG.End)
{
//收到最后一个包就更新显示列表
ReflashRoleList();
}
}
break;
default:
break;
}
}

private void btCon_Click(object sender, RoutedEventArgs e)
{
//连接服务器
try
{
_strRoleName
= tbName.Text;
_strIP
= tbIP.Text;
_nPort
= Convert.ToInt32(tbPort.Text);

//开始连接服务器
_netSocket.Connect(_strIP, _nPort);
}
catch (System.Exception ex)
{
MessageBox.Show(ex.Message);
}
}

private void btSend_Click(object sender, RoutedEventArgs e)
{
//发送聊天内容
SendMsg();
}

private void textBoxSend_KeyUp(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
e.Handled
= true;

SendMsg();
}
}

/// <summary>
/// 发送聊天内容
/// </summary>
private void SendMsg()
{
string strSend = textBoxSend.Text.Trim();
if (_netSocket.bConnected && strSend.Length > 0)
{
//组建一个聊天内容消息包
NetMsg pNetMsg = _netSocket.NetMsgProcessor.GetSendMsg((int)EN_NETMSG_TYPE.CHAT_INFO);
pNetMsg[
"nChatType"].IntValue = (int)EN_CHAT_TYPE.ROLE_COMMUNITY;
pNetMsg[
"szFromName"].StrValue = _strRoleName;
pNetMsg[
"szToName"].StrValue = "大家";
pNetMsg[
"szContent"].StrValue = strSend;
_netSocket.SendMsg(pNetMsg.GetSendBuffer(), pNetMsg.BufferSize);
}
}

/// <summary>
/// 显示聊天信息
/// </summary>
/// <param name="strText"></param>
private void ShowText(string strText)
{
this.Dispatcher.BeginInvoke(
delegate
{
textBlockShow.Text
+= strText + "\r\n";
scrollChat.ScrollToVerticalOffset(textBlockShow.ActualHeight);
}
);
}

/// <summary>
/// 显示验证后的用户名
/// </summary>
/// <param name="strName"></param>
private void ShowName(string strName)
{
this.Dispatcher.BeginInvoke(
delegate
{
tbName.Text
= strName;
}
);
}
}
}

MainPage.xaml.cs在加载时会初始化一个NetSocket类,此类提供网络连接,消息包的处理。客户端的逻辑处理见MainPage.xaml.cs代码中的注释。

二、客户端消息

  客户端的消息结构定义在MSGConfig.xml文件中,同服务器端一样只有三种消息结构。

MSGConfig.xml文件:

MSGConfig.xml
<?xml version="1.0" encoding="utf-8"?>
<Config>
<Mapping type="1" name="RoleEvent">
<Int name="nType"/>
<String name="szRoleName" length="65" charset="utf-8"/>
<Int name="nRoleIndex"/>
</Mapping>
<Mapping type="2" name="ChatInfo" comment = " 聊天">
<Int name="nChatType"/>
<String name="szFromName" length="35" charset="utf-8" comment = " 聊天发送者名称"/>
<String name="szToName" length="35" charset="utf-8" comment = " 聊天接收者名称"/>
<String name="szContent" length="300" charset="utf-8" comment = " 聊天内容"/>
</Mapping>
<Mapping type="3" name="RoleInfoList" comment = " 用户列表">
<Int name="nCount" comment = " 总数量"/>
<Array name="info">
<Item>
<Int name="nRoleIndex" comment = "帐号索引"/>
<String name="szRoleName" length="65" charset="utf-8" comment = " 发送人名称"/>
</Item>
</Array>
</Mapping>
</Config>

RoleInfoList用户信息列表消息结构,里面包含info用户信息结构,nCount为用户信息结构数组的数量。客户端的消息解析类约定列表数量“nCount”一定要紧跟在用户信息列表“info”前。(经几个游戏的开发总结,发现可以取消“nCount”,因为消息数组肯定是排在消息最后,且消息数组内的消息不会再包含消息列表)

  

  NetMsg类负责解析消息,将字节流解析成键值对或将键值对转换成字节流,如下:

    1、字节流解析成键值对(NetMsg类中的Parse()方法):将收到的消息字节流按照MSGConfig.xml文件中定义的消息结构,解析成键(字段名)值(字段值)对的形式。

    2、键值对转换成字节流(NetMsg类中的GetSendBuffer()方法):将解析类中的键值对按照MSGConfig.xml文件中定义的消息结构转换成字节流。

 

“NetMsg.cs”文件:

/********************************************************************
	author:		田园小蛙 ourtree@live.cn
	
	purpose:	消息解析类
*********************************************************************/
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections;
using System.Collections.Generic;
using System.Xml.Linq;
using System.IO;

namespace ChatClientSL
{
    /// <summary>
    /// 消息元素类型
    /// </summary>
    public enum NetMsgSectionType
    {
        INT,
        STRING,
        ARRAY,
    }
    
    /// <summary>
    /// 消息内的元素
    /// </summary>
    public class NetMsgSection
    {
        public static readonly int EF_NORMAL = 0;
        public static readonly int EF_FIRST = 1;
        public static readonly int EF_LAST = 2;

        public static int INT_SIZE = 4;

        private NetMsgSectionType _type;
        private int _length;
        private string _name;
        private int _intValue;
        private string _strValue;
        private NetMsgSectionCollection _sectionsDefault;
        private NetMsgSectionCollection[] _sections;

        public NetMsgSectionType Type { get { return _type; } }
        /// <summary>
        /// 类型为INT 时,为4,类型为STRING时,为字节数,类型为ARRAY时,为对象数
        /// </summary>
        public int Length { get { return _length; } }
        public string Name { get { return _name; } }

        public  int IntValue
        {
            get
            {
                if (_type == NetMsgSectionType.INT)
                    return _intValue;

                else
                    throw new Exception("wrong net msg section type");
            }
            set
            {
                if (_type == NetMsgSectionType.INT)
                    _intValue = value;
                else
                    throw new Exception("wrong net msg section type");
            }
        }

        public string StrValue
        {
            get
            {
                if (_type == NetMsgSectionType.STRING)
                    return _strValue;

                else
                    throw new Exception("wrong net msg section type");
            }
            set
            {
                if(_type == NetMsgSectionType.STRING)
                    _strValue = value;

                else
                    throw new Exception("wrong net msg section type");
            }
        }

        /// <summary>
        /// by ourtree 20101014
        /// </summary>
        public NetMsgSectionCollection[] Sections
        {
            get
            {
                if (_type == NetMsgSectionType.ARRAY)
                    return _sections;
                else
                    throw new Exception("wrong net msg section type");
            }
        }

        public static implicit operator int(NetMsgSection section)
        {
            return section.IntValue;
        }

        public NetMsgSection()
        {
            _sectionsDefault = new NetMsgSectionCollection();
        }

        public NetMsgSection(NetMsgSection section)
        {
            _type = section._type;
            _name = section._name;
            _strValue = section._strValue;
            _intValue = section._intValue;
            _length = section._length;
            _sectionsDefault = new NetMsgSectionCollection(section._sectionsDefault);
        }

        public  void    Init(string name, NetMsgSectionType type, int length)
        {
            _type = type;
            _length = length;
            _name = name;
        }

        public  int    Init(XElement el)
        {
            _name = el.Attribute("name").Value;

            switch (el.Name.LocalName)
            {
                
                case "String":
                    {
                        _type = NetMsgSectionType.STRING;
                        _length = Convert.ToInt32(el.Attribute("length").Value);
                        return _length;
                    }
                case "Int":
                    {
                        _type = NetMsgSectionType.INT;
                        _length = 4;
                        return _length;
                    }
                case "Array":
                    {
                        _type = NetMsgSectionType.ARRAY;
                        _length = 1;

                        int index = 0;
                        XElement elItem = el.Element("Item");
                        int sectionSize = INT_SIZE;   //  数量字段
                        IEnumerable<System.Xml.Linq.XElement> elementsSection = elItem.Elements();
                        foreach (XElement elSection in elementsSection)
                        {
                            NetMsgSection section = new NetMsgSection();
                            int size = section.Init(elSection);
                            if (size == 0)
                                throw new Exception("Init netmsg section get size 0");
                            _sectionsDefault.Add(index, section.Name, section);
                            index++;
                            sectionSize += size;
                        }

                        return sectionSize;
                    }
            }
            return 0;
        }

        /// <summary>
        /// 解析
        /// </summary>
        /// <param name="buffer">缓冲</param>
        /// <param name="offset">偏移量</param>
        /// <param name="length">从偏移量算起,还剩的长度</param>
        /// <returns>解析了多少字节</returns>
        public  int Parse(byte[] buffer, int offset, int length)
        {
            int nParseCount = 0;
            
            switch(_type)
            {
                case NetMsgSectionType.INT:
                    {
                        if (length < _length)
                            return 0;
                        _intValue = BitConverter.ToInt32(buffer, offset);
                        return _length;
                    }
                case NetMsgSectionType.STRING:
                    {
                        if (length < _length)
                            return 0;
                        if(buffer[offset] == 0)
                        {
                            _strValue = "";
                            return _length;
                        }
                        else
                        {
                            _strValue = System.Text.Encoding.UTF8.GetString(buffer, offset, _length);
                            int index = _strValue.IndexOf('\0');
                            if(index != -1 && index < _length)
                            {
                                _strValue = _strValue.Substring(0, index);
                            }
                            return _length;
                        }
                    }
                case NetMsgSectionType.ARRAY:
                    {
                        if (length < INT_SIZE)
                            return 0;
                        _length = BitConverter.ToInt32(buffer, offset - 4);
                        if(_length > 200)
                            return 0;

                        nParseCount = 0;
                        _sections = new NetMsgSectionCollection[_length];

                        for(int i = 0; i < _length; i++)
                        {
                            _sections[i] = new NetMsgSectionCollection(_sectionsDefault);
                            int nSectionParseCount =_sections[i].Parse(buffer, offset + nParseCount, length - nParseCount);
                            if(nSectionParseCount == 0)
                                return 0;

                            nParseCount += nSectionParseCount;
                        }

                        return nParseCount;
                    }
            }

            return nParseCount;
        }

        public  int GetSendBuffer(byte[] buffer, int offset, int length)
        {
            switch(_type)
            {
                case NetMsgSectionType.INT:
                    {
                        if (length < INT_SIZE)
                            return 0;
                        byte[] temp =  BitConverter.GetBytes(_intValue);
                        if (temp == null)
                            return 0;
                        Array.Copy(temp, 0, buffer, offset, INT_SIZE);

                        return INT_SIZE;
                    }
                case NetMsgSectionType.STRING:
                    {
                        if (length < _length)
                            return 0;

                        if (_strValue == null)
                            _strValue = "";

                        byte[] temp = System.Text.Encoding.UTF8.GetBytes(_strValue);
                        if (temp == null)
                            return 0;

                        int nCopyLength = temp.Length;
                        if (nCopyLength > _length)
                            nCopyLength = _length;

                        Array.Copy(temp, 0, buffer, offset, nCopyLength);

                        return _length;
                    }
                case NetMsgSectionType.ARRAY:
                    {
                        if (_sections == null)
                            return 0;

                        int sectionsSize = 0;
                        for (int i = 0; i < _sections.Length; i++ )
                        {
                            int size = _sections[i].GetSendBuffer(buffer, offset + sectionsSize, length - sectionsSize);

                            if (size == 0)
                                return 0;

                            sectionsSize += size;
                        }
                        return sectionsSize;
                    }
            }

            return 0;
        }
    }

    /// <summary>
    /// 消息头
    /// </summary>
    public  class NetMsgHead
    {
        public NetMsgHead()
        {
            size = new NetMsgSection();
            type = new NetMsgSection();
            endFlag = new NetMsgSection();
            number = new NetMsgSection();

            size.Init("size", NetMsgSectionType.INT, NetMsgSection.INT_SIZE);
            type.Init("type", NetMsgSectionType.INT, NetMsgSection.INT_SIZE);
            endFlag.Init("endFlag", NetMsgSectionType.INT, NetMsgSection.INT_SIZE);
            number.Init("number", NetMsgSectionType.INT, NetMsgSection.INT_SIZE);
        }

        public NetMsgHead(NetMsgHead netMsgHead)
        {
            size = new NetMsgSection(netMsgHead.size);
            type = new NetMsgSection(netMsgHead.type);
            endFlag = new NetMsgSection(netMsgHead.endFlag);
            number = new NetMsgSection(netMsgHead.number);
        }
        public NetMsgSection size;
        public NetMsgSection type;
        public NetMsgSection endFlag;
        public NetMsgSection number;

        public static int SIZE = NetMsgSection.INT_SIZE * 4;

        public int GetSendBuffer(byte[] buffer, int offset, int length)
        {
            if (buffer == null)
                return 0;
            if (buffer.Length < length)
                return 0;
            if (buffer.Length < SIZE)
                return 0;

            byte[] temp = BitConverter.GetBytes(size);
            Array.Copy(temp, 0, buffer, offset, NetMsgSection.INT_SIZE);
            temp = BitConverter.GetBytes(type);
            Array.Copy(temp, 0, buffer, offset + NetMsgSection.INT_SIZE, NetMsgSection.INT_SIZE);
            temp = BitConverter.GetBytes(endFlag);
            Array.Copy(temp, 0, buffer, offset + NetMsgSection.INT_SIZE * 2, NetMsgSection.INT_SIZE);
            temp = BitConverter.GetBytes(number);
            Array.Copy(temp, 0, buffer, offset + NetMsgSection.INT_SIZE * 3, NetMsgSection.INT_SIZE);

            return SIZE;
        }
    }

    /// <summary>
    /// 消息内的元素集合
    /// </summary>
    public  class NetMsgSectionCollection
    {
        private Dictionary<int, NetMsgSection> _msgSection;
        private Dictionary<string, NetMsgSection> _msgSectionName;
        public  NetMsgSectionCollection()
        {
            _msgSection = new Dictionary<int, NetMsgSection>();
            _msgSectionName = new Dictionary<string, NetMsgSection>();
        }
        public NetMsgSectionCollection(NetMsgSectionCollection sections)
        {
             _msgSection = new Dictionary<int, NetMsgSection>();
            _msgSectionName = new Dictionary<string, NetMsgSection>();
            for(int i = 0; i < sections._msgSection.Count; i++)
            {
                NetMsgSection section = new NetMsgSection(sections._msgSection[i]);
                _msgSectionName.Add(section.Name, section);
                _msgSection.Add(i, section);
            }
        }
        public  Dictionary<int, NetMsgSection> IntCollection
        {
            get{return _msgSection;}
        }
        
        public  Dictionary<string, NetMsgSection> StrCollection
        {
            get{return _msgSectionName;}
        }

        public  NetMsgSection this[int index]
        {
            get{return _msgSection[index];}
        }
        public NetMsgSection this[string name]
        {
            get{return _msgSectionName[name];}
        }
        public void Add(int index, string name, NetMsgSection section)
        {
            _msgSection.Add(index, section);
            _msgSectionName.Add(name, section);
        }

        public  int Parse(byte[] buffer, int offset, int length)
        {
            int nParseCount = 0;
            for(int i = 0; i < _msgSection.Count; i++)
            {
                NetMsgSection section = _msgSection[i];
                 int nSectionParseCount = section.Parse(buffer, offset + nParseCount, length - nParseCount);
                if (nSectionParseCount == 0)
                    return 0;

                nParseCount += nSectionParseCount;
            }

            return nParseCount;
        }

        public int GetSendBuffer(byte[] buffer, int offset, int length)
        {
            int readSize = 0;
            for (int i = 0; i < IntCollection.Count; i++)
            {
                NetMsgSection section = IntCollection[i];
                int size = section.GetSendBuffer(buffer, offset + readSize, length - readSize);

                if (size == 0)
                    return 0;

                readSize += size;
            }

            return readSize;
        }
    }

    /// <summary>
    /// 消息
    /// </summary>
    public class NetMsg
    {
        private static int _idUniverse = 0;
        private int _id;
        private NetMsgSectionCollection _msgSections;
        private NetMsgHead _msgHead;
        private int _bufferSize;
        private byte[] _buffer;
        public int BufferSize { get { return _bufferSize; } }
        public int Type { get { return _msgHead.type; } }
        public NetMsgHead Head
        {
            get { return _msgHead; }
        }
        public NetMsgSectionCollection Sections
        {
            get{return _msgSections;}
        }
        public NetMsgSection this[string sectionName]
        {
            get { return Sections.StrCollection[sectionName]; }
        }
        public NetMsg()
        {
            _msgSections = new NetMsgSectionCollection();
            _msgHead = new NetMsgHead();
            
        }

        public NetMsg(NetMsg pNetMsg)
        {
            if (pNetMsg == null)
                throw new Exception("NetMsg is Null");

            _id = _idUniverse++;

            //复制HEAD
            _msgSections = new NetMsgSectionCollection();
            _msgHead = new NetMsgHead(pNetMsg._msgHead);

            //复制NetMsgSectionCollection
            for (int i = 0; i < pNetMsg.Sections.IntCollection.Count; i++)
            {
                NetMsgSection pNetMsgSection = new NetMsgSection(pNetMsg.Sections.IntCollection[i]);
                NetMsgSection pNetMsgSectionNew = new NetMsgSection(pNetMsgSection);
                this._msgSections.Add(i, pNetMsgSectionNew.Name, pNetMsgSectionNew);
            }

            //复制公共项
            this._bufferSize = pNetMsg.BufferSize;
        }

        public void Init(XElement el)
        {
           _msgHead.type.IntValue = Convert.ToInt32(el.Attribute("type").Value);
            try
            {
                IEnumerable<System.Xml.Linq.XElement> elementsSection = el.Elements();
                int index = 0;
                _bufferSize = NetMsgHead.SIZE;
                foreach (XElement elSection in elementsSection)
                {
                    NetMsgSection section = new NetMsgSection();
                    int size = section.Init(elSection);
                    if (size == 0)
                        throw new Exception("NetMsg init failed because get a section 0 size");
                    _bufferSize += size;
                    _msgSections.Add(index, section.Name, section);
                    index++;
                }
            }
            catch (System.Exception ex)
            {

                throw new Exception(string.Format("NetMsg.Init type{0},  exception:{1}", _msgHead.type.IntValue, ex.Message));
            }
           
        }


        /// <summary>
        /// 解析消息
        /// </summary>
        /// <param name="buffer">缓冲区</param>
        /// <param name="offset">缓冲区偏移量</param>
        /// <param name="length">从偏移量算起,还有多少个字节的有效数据</param>
        /// <returns>解析了多少字节,0为失败</returns>
        public  int    Parse(byte[] buffer, int offset, int length)
        {
            int nParseCount = 0;
            try
            {
                if (buffer.Length < NetMsgHead.SIZE)
                    return nParseCount;

                //解析消息头
                _msgHead.size.IntValue = BitConverter.ToInt32(buffer, offset);
                _msgHead.type.IntValue = BitConverter.ToInt32(buffer, offset + NetMsgSection.INT_SIZE);
                _msgHead.endFlag.IntValue = BitConverter.ToInt32(buffer, offset + NetMsgSection.INT_SIZE * 2);
                _msgHead.number.IntValue = BitConverter.ToInt32(buffer, offset + NetMsgSection.INT_SIZE * 3);

                if (buffer.Length < _msgHead.size)
                    return nParseCount;
                if (buffer.Length < length)
                    return nParseCount;
                if (length < _msgHead.size)
                    return nParseCount;

                //解析消息体
                nParseCount = NetMsgSection.INT_SIZE * 4;
                for(int i = 0; i < _msgSections.IntCollection.Count; i++)
                {
                    NetMsgSection section = _msgSections.IntCollection[i];
                    int nSectionParseCount = section.Parse(buffer, offset + nParseCount, length - nParseCount);
                    if (nSectionParseCount == 0)
                        return 0;

                    nParseCount += nSectionParseCount;
                }             
            }
            catch (System.Exception ex)
            {
                MessageBox.Show(ex.InnerException.Message);
            }
            _bufferSize = _msgHead.size;
            return nParseCount;
        }

        /// <summary>
        /// 得消息字节流
        /// </summary>
        /// <returns></returns>
        public byte[] GetSendBuffer()
        {
            if (_buffer == null)
                _buffer = new byte[_bufferSize];
            else if (_buffer.Length != _bufferSize)
                _buffer = new byte[_bufferSize];

            Head.size.IntValue = _bufferSize;
            Head.GetSendBuffer(_buffer, 0, _bufferSize);

            int size = _msgSections.GetSendBuffer(_buffer, NetMsgHead.SIZE, _bufferSize - NetMsgHead.SIZE);

            return _buffer;
        }
    }

    /// <summary>
    /// 消息管理器,创建消息
    /// </summary>
    public class NetMsgProcessor
    {
        private Dictionary<string, NetMsg> _msgDictName;
        private Dictionary<int, NetMsg> _msgDictID;

        private static int nNumber = 0;

        public  NetMsg GetSendMsg(int type)
        {
            NetMsg pNetMsg = new NetMsg(_msgDictID[type]);
            pNetMsg.Head.number.IntValue = nNumber++;
            return pNetMsg;
        }

        public void Send()
        {

        }

        public NetMsgProcessor()
        {
            _msgDictName = new Dictionary<string, NetMsg>();
            _msgDictID = new Dictionary<int, NetMsg>();
        }

        /// <summary>
        /// 载入消息结构配置文件
        /// </summary>
        /// <param name="strMSGConfigPath"></param>
        public void Init(string strMSGConfigPath)
        {
            XElement elementRoot = XElement.Load(strMSGConfigPath);
            if (elementRoot != null)
            {
                System.Collections.Generic.IEnumerable<System.Xml.Linq.XElement> elements = elementRoot.Elements("Mapping");
                foreach (XElement el in elements)
                {
                    NetMsg netMsg = new NetMsg();
                    netMsg.Init(el);
                    _msgDictID.Add(netMsg.Type, netMsg);
                }
            }
        }

        /// <summary>
        /// 解析缓冲到消息
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="length">缓冲区有效数据</param>
        /// <returns></returns>
        public  NetMsg BuildNetMsgFromBuffer(byte[] buffer, int length)
        {
            if (buffer == null)
                return null;
            if (buffer.Length < NetMsgHead.SIZE)
                return null;
            if (buffer.Length < length)
                return null;
            if (length < NetMsgHead.SIZE)
                return null;

            int size = BitConverter.ToInt32(buffer, 0);
            if(buffer.Length >= size)
            {
                //  有消息了
                int type = BitConverter.ToInt32(buffer, NetMsgSection.INT_SIZE);
                if (!_msgDictID.ContainsKey(type))
                    throw new Exception("NetMsg.BuildNetMsgFromBuffer upsupport msg");
             
                //取得消息结构
                NetMsg msgTemplate = _msgDictID[type];

                //根据字节流生成消息
                NetMsg netMsg = new NetMsg(msgTemplate);
                if (netMsg.Parse(buffer, 0, length) > 0)
                    return netMsg;
            }

            return null;
        }
    }

    /// <summary>
    /// 消息事件
    /// </summary>
    public class NetMsgEventArgs : System.EventArgs
    {
        public NetMsgEventArgs(NetMsg netMsg)
        {
            _netMsg = netMsg;
        }
        private NetMsg _netMsg;
        public NetMsg NetMsg
        {
            get { return _netMsg; }
        }
    }

    /// <summary>
    /// 消息队列类
    /// </summary>
    public  class NetMsgQueue
    {
        private Queue<NetMsg>   _queue;
        public  EventHandler<NetMsgEventArgs>   AllNewNetMsgEvent;
        private Dictionary<int, EventHandler<NetMsgEventArgs>> _netMsgEvent;

        public EventHandler<NetMsgEventArgs>  this[int type]
        {
            get{
                return _netMsgEvent[type];
            }
        }

        public int Size { get { lock (_queue) { return _queue.Count; } } }
        public  NetMsgQueue()
        {
            _queue = new Queue<NetMsg>();
            _netMsgEvent = new Dictionary<int, EventHandler<NetMsgEventArgs>>();
        }
        
        public  void    PushNetMsg(NetMsg netMsg)
        {
            lock (_queue)
            {
                _queue.Enqueue(netMsg);
            }
            
        }

        public  void    ProcessNetMsg(int maxCount)
        {
            lock (_queue)
            {
                while (_queue.Count > 0 && maxCount > 0)
                {
                    NetMsg netMsg = null;

                    netMsg = _queue.Dequeue();

                    if (netMsg != null)
                    {
                        if (AllNewNetMsgEvent != null)
                            AllNewNetMsgEvent.Invoke(this, new NetMsgEventArgs(netMsg));

                        int type = netMsg.Type;
                        if (_netMsgEvent.ContainsKey(type))
                            _netMsgEvent[type].Invoke(this, new NetMsgEventArgs(netMsg));
                    }

                    maxCount--;
                }
            }
        }
    }
}

“NetMsg.cs”文件包含下面这些类:

NetMsgSection类:消息元素(元素名对应元素值)
NetMsgHead类:消息头(16字节)
NetMsgSectionCollection类:消息元素集合
NetMsg类:消息类
NetMsgProcessor:消息辅助类

这些类的功能见代码注释。

 

三、客户通讯连接

“NetSocket.cs”文件:

NetSocket.cs
/********************************************************************
author: 田园小蛙 ourtree@live.cn

purpose: 网络连接
********************************************************************
*/
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Net.Sockets;
using System.Threading;

namespace ChatClientSL
{
/// <summary>
/// 连接事件类型
/// </summary>
public enum EN_CONNECT_EVENT_TYPE
{
NONE
= 0,
CONNECT_OK
= 1,//连接成功
CONNECT_FAILED = 2//连接失败
}

public class NetSocket
{
//分发消息的委托
public delegate void ReceiveEventDelegate(NetMsg netMsg);
public event ReceiveEventDelegate OnReceiveEvent;
private void RaiseReceiveEvent(NetMsg netMsg)
{
if (OnReceiveEvent != null)
{
OnReceiveEvent(netMsg);
}
}

//连接事件委托
public delegate void ConnectEventDelegate(EN_CONNECT_EVENT_TYPE enEvent);
public event ConnectEventDelegate OnConnectEvent;
private void ConnectEvent(EN_CONNECT_EVENT_TYPE enEvent)
{
if (OnConnectEvent != null)
{
OnConnectEvent(enEvent);
}
}

public const int CN_RECV_BUFFER_SIZE = 1024 * 20;//接收缓冲区大小

static ManualResetEvent clientDone = new ManualResetEvent(false);
SocketAsyncEventArgs socketEventArgSend
= null;
Socket netsock
= null;
protected int m_nRecvLen = 0;
protected byte[] m_bufRecvMsg = new byte[CN_RECV_BUFFER_SIZE];//接收缓冲区
private NetMsgProcessor _processor;

public bool bConnected
{
get
{
if (netsock == null)
{
return false;
}
else
{
return netsock.Connected;
}
}
}

public NetMsgProcessor NetMsgProcessor
{
get { return _processor; }
}

/// <summary>
/// 初始化
/// </summary>
public NetSocket()
{
_processor
= new NetMsgProcessor();
_processor.Init(
"MSGConfig.xml");//加载消息格式配置文件
}

/// <summary>
/// 关闭连接
/// </summary>
public void Close()
{
if (netsock != null)
{
netsock.Shutdown(SocketShutdown.Send);
netsock.Close();
clientDone.Set();
}

}

/// <summary>
/// 连接服务器
/// </summary>
/// <param name="strIP"></param>
/// <param name="nPort"></param>
public void Connect(string strIP, int nPort)
{
try
{
if (bConnected)
return;

SocketAsyncEventArgs socketEventArg
= new SocketAsyncEventArgs();
DnsEndPoint hostEntry
= new DnsEndPoint(strIP, nPort);

netsock
= new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

socketEventArg.Completed
+= new EventHandler<SocketAsyncEventArgs>(socketEventArg_Completed);

socketEventArg.RemoteEndPoint
= hostEntry;
socketEventArg.UserToken
= netsock;
netsock.ConnectAsync(socketEventArg);
}
catch(Exception ex)
{
throw ex;
}
}

void socketEventArg_Completed(object sender, SocketAsyncEventArgs e)
{
switch (e.LastOperation)
{
case SocketAsyncOperation.Connect:
{
ProcessConnect(e);
}
break;
case SocketAsyncOperation.Receive:
{
ProcessReceive(e);
}
break;
default:
throw new Exception("Invalid operation completed");
}
}

/// <summary>
/// 连接完成
/// </summary>
/// <param name="e"></param>
private void ProcessConnect(SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success)
{
Socket sock
= e.UserToken as Socket;

socketEventArgSend
= new SocketAsyncEventArgs();
socketEventArgSend.RemoteEndPoint
= e.RemoteEndPoint;
socketEventArgSend.UserToken
= sock;
socketEventArgSend.Completed
+= new EventHandler<SocketAsyncEventArgs>(socketEventArgSend_Completed);

e.SetBuffer(m_bufRecvMsg, m_nRecvLen, CN_RECV_BUFFER_SIZE
- m_nRecvLen);
sock.ReceiveAsync(e);

ConnectEvent(EN_CONNECT_EVENT_TYPE.CONNECT_OK);
}
else
{
ConnectEvent(EN_CONNECT_EVENT_TYPE.CONNECT_FAILED);
}
}

/// <summary>
/// 发送完成
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void socketEventArgSend_Completed(object sender, SocketAsyncEventArgs e)
{
switch (e.LastOperation)
{
case SocketAsyncOperation.Send:
{
ProcessSend(e);
}
break;
default:
throw new Exception("Invalid Send operation completed");
}
}

/// <summary>
/// 接收到数据
/// </summary>
/// <param name="e"></param>
private void ProcessReceive(SocketAsyncEventArgs e)
{
Socket sock
= e.UserToken as Socket;

if (e.BytesTransferred == 0 && e.SocketError == SocketError.Shutdown)
{
return;
}

m_nRecvLen
+= e.BytesTransferred;

int size = BitConverter.ToInt32(m_bufRecvMsg, 0);
while (m_nRecvLen >= size && m_nRecvLen > 0)
{
// 分发消息
NetMsg netMsg = _processor.BuildNetMsgFromBuffer(m_bufRecvMsg, m_nRecvLen);
if (netMsg != null)
{
// 清理缓存
m_nRecvLen -= netMsg.BufferSize;
System.Buffer.BlockCopy(m_bufRecvMsg, netMsg.BufferSize, m_bufRecvMsg,
0, m_nRecvLen);

RaiseReceiveEvent(netMsg);
}
}

e.SetBuffer(m_bufRecvMsg, m_nRecvLen, CN_RECV_BUFFER_SIZE
- m_nRecvLen);
netsock.ReceiveAsync(e);

}

private void ProcessSend(SocketAsyncEventArgs e)
{

}

/// <summary>
/// 发送消息
/// </summary>
/// <param name="sendBuffer">字节流</param>
/// <param name="lenght">字节流长度</param>
public void SendMsg(byte[] sendBuffer, int lenght)
{
socketEventArgSend.SetBuffer(sendBuffer,
0, lenght);
netsock.SendAsync(socketEventArgSend);
}
}
}

封装的socket连接类,接收和发送字节流消息,将收到消息将通知界面处理。

ProcessReceive(SocketAsyncEventArgs e)方法处理收到的字节流,分析字节流是否够组成一个消息包,如果够就组成一个消息包并通知界面,不够就将收到的数据缓存起来。

以上就是整个客户端代码。(完)