管理软件IM开发

一、即时通讯介绍

即时通讯(Instant Messenger,简称IM),是一种基于互联网的即时交流消息的业务,代表有:阿里旺旺、QQ、百度Hi等。

企业内部应用即时通讯的特点和要求

特点是沟通成本低,高效,方便,快捷,具备一定的用户黏度

1.即时通讯,包括文字,图片,语音,视频,文件。

2.群组功能,讨论组。

3.重点考虑商务沟通过程中的特点,比如视频会议功能,PPT演示,无线连接投影仪,再比如重要聊天记录保存,还有方便快捷的转账业务。

4.UI简洁高效,大方美观,特别是无广告。

5.用户联系方式导入,支持手机通讯里,EXCEL,MSN通讯录等等。

6.快捷键,常用功能,如截图,关闭。

7.注重隐私保护,身份验证,各种状态,在线,隐身。

8.设计要稳重,比如个人资料要设置为名片,企业形象等等。

二、Openfire介绍

服务端Openfire

源码下载地址http://www.igniterealtime.org/downloads/source.jsp

客户端Spark

源码下载地址http://fisheye.igniterealtime.org/browse/spark/trunk

主要功能

1、支持多服务器集群;

2、防火强穿透;

3、在线、离线消息;

4、文件,图片实时传输;

5、自动升级;

6、插件功能;

7、群聊;

8、好友添加。

三、企业IM开发规划

1、技术选型:采用DotNet平台 c#语言开发,数据库采用SQL Server 2008 R2 ,服务端  Socket App集成后台管理。

2、客户端升级:采用智能客户端实现升级。

     a.创建一个升级程序,连接 webservice 来获取程序版本和升级程序的信息;

     b.将升级程序下载到本机的一个目录然后覆盖现有的程序;

     c.在配置文件中记录本次升级的信息。

4、架构图:

5、数据协议

采用XMPP格式数据进行传输

XMPP : The Extensible Messaging and Presence Protocol。

中文全称:可扩展通讯和表示协议。

XMPP是一种基于XML的协议,它继承了在XML环境中灵活的发展性。因此,基于XMPP的应用具有超强的可扩展性。

而XMPP的核心部分就是一个在网络上分片段发送XML的流协议。

XMPP节详解

XML节通过XML流来发送,XMPP定义了三种顶级XML节

<message />    --用户之间的信息发送

<iq />                --服务器请求返回信息

<presence />   --状态

XMPP给这三种节定义了五种通用属性

to         属性指定接收节的JID

from    属性指定发送节的JID

id         并且,在接收应用(通常是一个服务器)中是唯一的。注意:流ID可能是严格安全的,并且因此必须是即不能预测也不能重复的

type    属性指定目的或消息上下文,出席或IQ节的详细信息。

xml:lang

<message />节定义了消息语义,<message />节可被看作“推”机制,一个实体推信息给其它实体,与EMAIL系统中发生的通信类似。所有消息节应该拥有‘to’属性,指定有意的消息接收者;根据接收到那样的一个节,服务器应该路由或传送它到有意的接收者。
message用于“发送后即忘”的传输(发送后不验证消息是否接收成功,功能上并非技术上的),这样的传输主要应用与人类可读的文本、警告、通知等信息。

<iq />节定义了请求语义,<iq />节可被看作一个请求-响应机制,与[HTTP]在某些方面相似。IQ语义让一个实体向其它实体请求或接收其它实体的响应成为可能。请求与响应的数据内容由IQ无素的直接子元素的命名空间声明定义,并且,交互由请求实体通过使用‘id’属性来跟踪。因此,IQ交互遵从结构化数据交换的一个通用模式,此交换例如得到/结果或设置/结果(虽然如果合适的话,对一个请求的响应可能会以错误返回)。

<presence />节定义了出席语义,<presence />节可被看作基本广播或“出版-订阅”机制,多实体收到他们已订阅(在这种情况下,网络可利用信息)实体的信息。总的来说,出版实体应该发送一个不带‘to’属性的出席节,在这种情况下,与此实体相连的服务器应该广播给所有订阅实体。然而,一个出版实体也可能发送一个带有‘to’属性的出席节,此种情况下,服务器应该路由或传送节到有意的接收者。
presence用于向那些订阅实体广播网络可用性。

6、XMPP通信原理

所有从一个client到另一个client的消息和数据都要通过xmpp server。

1.client连接到server;

2.server利用本地目录系统的证书对其认证;

3.client制定目标地址,让server告知目标状态;

4.server查找,连接并进行相互认证;

5.client间进行交互。

8、基于XMPP协议.net实现

 四、后台数据库集成

集成主要的表结构

五、Socket开发技术

 

      P2P技术(P2P打洞)

一个异步Socket通信的源码

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace Chatting
{
    public abstract class SocketFunc
    {
        //不管是服务端还是客户端, 建立连接后用这个Socket进行通信
        public Socket communicateSocket = null;

        //服务端和客户端建立连接的方式稍有不同, 子类会重载
        public abstract void Access(string IP, System.Action AccessAciton);

        //发送消息的函数
        public void Send(string message)
        {
            if (communicateSocket.Connected == false)
            {
                throw new Exception("还没有建立连接, 不能发送消息");
            }
            Byte[] msg = Encoding.UTF8.GetBytes(message);
            communicateSocket.BeginSend(msg,0, msg.Length, SocketFlags.None,
                ar => {
                
                }, null);
        }

        //接受消息的函数
        public void Receive(System.Action<string> ReceiveAction)
        {
            //如果消息超过1024个字节, 收到的消息会分为(总字节长度/1024 +1)条显示
            Byte[] msg = new byte[1024];
            //异步的接受消息
            communicateSocket.BeginReceive(msg, 0, msg.Length, SocketFlags.None,
                ar => {
                    //对方断开连接时, 这里抛出Socket Exception
                    //An existing connection was forcibly closed by the remote host 
                        communicateSocket.EndReceive(ar); 
                    ReceiveAction(Encoding.UTF8.GetString(msg).Trim('\0',' '));
                    Receive(ReceiveAction);
                }, null);
        }
    }


    public class ServerSocket:SocketFunc
    {
        //服务端重载Access函数
        public override void Access(string IP, System.Action AccessAciton)
        {
            Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //本机预使用的IP和端口
            IPEndPoint serverIP = new IPEndPoint(IPAddress.Any, 9050);
            //绑定服务端设置的IP
            serverSocket.Bind(serverIP);
            //设置监听个数
            serverSocket.Listen(1);
            //异步接收连接请求
            serverSocket.BeginAccept(ar =>
                {
                    base.communicateSocket = serverSocket.EndAccept(ar);
                    AccessAciton();
                }, null);
        }
    }

    public class ClientSocket:SocketFunc
    {
        //客户端重载Access函数
        public override void Access(string IP, System.Action AccessAciton)
        {
            base.communicateSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            base.communicateSocket.Bind(new IPEndPoint(IPAddress.Any, 9051));
            
            //服务器的IP和端口
            IPEndPoint serverIP;
            try
            {
                serverIP = new IPEndPoint(IPAddress.Parse(IP), 9050);
            }
            catch
            {
                throw new Exception(String.Format("{0}不是一个有效的IP地址!", IP));
            }
            
            //客户端只用来向指定的服务器发送信息,不需要绑定本机的IP和端口,不需要监听
            try
            {
                base.communicateSocket.BeginConnect(serverIP, ar =>
                {
                    AccessAciton();
                }, null);
            }
            catch
            {
                throw new Exception(string.Format("尝试连接{0}不成功!", IP));
            }
        }
    }
}
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Net.Sockets;

namespace Chatting
{
    public partial class MainForm : Form
    {

        public MainForm()
        {
            InitializeComponent();
        }

        SocketFunc socket;
        System.Action<string> ReceiveAction;
        System.Action AccessAction;

        private void MainForm_Load(object sender, EventArgs e)
        {
            //异步建立连接回调
            AccessAction = () =>
            {
                this.Invoke((MethodInvoker)delegate()
                {
                    lblFriendIP.Visible = false;
                    txtIP.Visible = false;
                    btnConnect.Visible = false;
                    btnWaitAccess.Visible = false;

                    String friendIP = socket.communicateSocket.RemoteEndPoint.ToString();
                    lblState.Text = String.Format("连接成功. 对方IP:{0}", friendIP);

                    try
                    {
                        socket.Receive(ReceiveAction);
                    }
                    catch (Exception exp)
                    {
                        MessageBox.Show(exp.Message, "错误");
                    }
                });

            };
            //异步接收消息回调
            ReceiveAction = msg =>
            {
                txtGetMsg.Invoke((MethodInvoker)delegate()
                {
                    UpdateGetMsgTextBox(false, msg, Color.Red);
                });
            };
        }

        private void btnWaitAccess_Click(object sender, EventArgs e)
        {
            this.socket = new ServerSocket();
            try
            {
                this.socket.Access("", this.AccessAction);
            }
            catch (Exception ecp)
            {
                MessageBox.Show(ecp.Message, "错误");
            }

            lblState.Text = "等待对方连接...";
        }

        private void btnConnect_Click(object sender, EventArgs e)
        {
            this.socket = new ClientSocket();
            try
            {
                this.socket.Access(txtIP.Text, this.AccessAction);
            }
            catch (Exception ecp)
            {
                MessageBox.Show(ecp.Message, "错误");
            }
            lblState.Text = "正在连接对方...";
        }

        private void btnSendMsg_Click(object sender, EventArgs e)
        {
            string message = txtSendMsg.Text.Trim();
            if (string.IsNullOrEmpty(message))
            {
                MessageBox.Show("消息内容不能为空!", "错误");
                txtSendMsg.Focus();
                return;
            }

            try
            {
                socket.Send(message);
            }
            catch(Exception ecp)
            {
                MessageBox.Show(ecp.Message, "错误");
                return;
            }

            UpdateGetMsgTextBox(true, message, Color.Blue);
            txtSendMsg.Text = "";
        }

        private void UpdateGetMsgTextBox(bool sendMsg, string message, Color color)
        {
            string appendText;
            if (sendMsg == true)
            {
                appendText = "Me:           " + System.DateTime.Now.ToString()
                    + Environment.NewLine
                    + message + Environment.NewLine;
            }
            else
            {
                appendText = "Friend:           " + System.DateTime.Now.ToString()
                    + Environment.NewLine
                    + message + Environment.NewLine;
            }
            int txtGetMsgLength = txtGetMsg.Text.Length;
            txtGetMsg.AppendText(appendText);
            txtGetMsg.Select(txtGetMsgLength, appendText.Length - Environment.NewLine.Length*2 -message.Length);
            txtGetMsg.SelectionColor = color;

            txtGetMsg.ScrollToCaret();
        }
    }
}

 

namespace Chatting
{
    partial class MainForm
    {
        /// <summary>
        /// 必需的设计器变量。
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// 清理所有正在使用的资源。
        /// </summary>
        /// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows 窗体设计器生成的代码

        /// <summary>
        /// 设计器支持所需的方法 - 不要
        /// 使用代码编辑器修改此方法的内容。
        /// </summary>
        private void InitializeComponent()
        {
            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm));
            this.label1 = new System.Windows.Forms.Label();
            this.label2 = new System.Windows.Forms.Label();
            this.txtSendMsg = new System.Windows.Forms.TextBox();
            this.lblFriendIP = new System.Windows.Forms.Label();
            this.txtIP = new System.Windows.Forms.TextBox();
            this.btnConnect = new System.Windows.Forms.Button();
            this.btnSendMsg = new System.Windows.Forms.Button();
            this.btnWaitAccess = new System.Windows.Forms.Button();
            this.txtGetMsg = new System.Windows.Forms.RichTextBox();
            this.lblState = new System.Windows.Forms.Label();
            this.SuspendLayout();
            // 
            // label1
            // 
            resources.ApplyResources(this.label1, "label1");
            this.label1.BackColor = System.Drawing.Color.Transparent;
            this.label1.ForeColor = System.Drawing.SystemColors.Window;
            this.label1.Name = "label1";
            // 
            // label2
            // 
            resources.ApplyResources(this.label2, "label2");
            this.label2.BackColor = System.Drawing.Color.Transparent;
            this.label2.ForeColor = System.Drawing.SystemColors.Window;
            this.label2.Name = "label2";
            // 
            // txtSendMsg
            // 
            this.txtSendMsg.BorderStyle = System.Windows.Forms.BorderStyle.None;
            resources.ApplyResources(this.txtSendMsg, "txtSendMsg");
            this.txtSendMsg.Name = "txtSendMsg";
            // 
            // lblFriendIP
            // 
            resources.ApplyResources(this.lblFriendIP, "lblFriendIP");
            this.lblFriendIP.ForeColor = System.Drawing.SystemColors.Window;
            this.lblFriendIP.Name = "lblFriendIP";
            // 
            // txtIP
            // 
            resources.ApplyResources(this.txtIP, "txtIP");
            this.txtIP.Name = "txtIP";
            // 
            // btnConnect
            // 
            resources.ApplyResources(this.btnConnect, "btnConnect");
            this.btnConnect.Name = "btnConnect";
            this.btnConnect.UseVisualStyleBackColor = true;
            this.btnConnect.Click += new System.EventHandler(this.btnConnect_Click);
            // 
            // btnSendMsg
            // 
            resources.ApplyResources(this.btnSendMsg, "btnSendMsg");
            this.btnSendMsg.Name = "btnSendMsg";
            this.btnSendMsg.UseVisualStyleBackColor = true;
            this.btnSendMsg.Click += new System.EventHandler(this.btnSendMsg_Click);
            // 
            // btnWaitAccess
            // 
            resources.ApplyResources(this.btnWaitAccess, "btnWaitAccess");
            this.btnWaitAccess.Name = "btnWaitAccess";
            this.btnWaitAccess.UseVisualStyleBackColor = true;
            this.btnWaitAccess.Click += new System.EventHandler(this.btnWaitAccess_Click);
            // 
            // txtGetMsg
            // 
            this.txtGetMsg.BackColor = System.Drawing.SystemColors.Window;
            this.txtGetMsg.BorderStyle = System.Windows.Forms.BorderStyle.None;
            resources.ApplyResources(this.txtGetMsg, "txtGetMsg");
            this.txtGetMsg.Name = "txtGetMsg";
            this.txtGetMsg.ReadOnly = true;
            // 
            // lblState
            // 
            resources.ApplyResources(this.lblState, "lblState");
            this.lblState.ForeColor = System.Drawing.SystemColors.Window;
            this.lblState.Name = "lblState";
            // 
            // MainForm
            // 
            this.AcceptButton = this.btnSendMsg;
            resources.ApplyResources(this, "$this");
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.BackColor = System.Drawing.Color.Navy;
            this.Controls.Add(this.lblState);
            this.Controls.Add(this.txtGetMsg);
            this.Controls.Add(this.btnWaitAccess);
            this.Controls.Add(this.btnSendMsg);
            this.Controls.Add(this.btnConnect);
            this.Controls.Add(this.txtIP);
            this.Controls.Add(this.lblFriendIP);
            this.Controls.Add(this.txtSendMsg);
            this.Controls.Add(this.label2);
            this.Controls.Add(this.label1);
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
            this.MaximizeBox = false;
            this.Name = "MainForm";
            this.Load += new System.EventHandler(this.MainForm_Load);
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.Label label1;
        private System.Windows.Forms.Label label2;
        private System.Windows.Forms.TextBox txtSendMsg;
        private System.Windows.Forms.Label lblFriendIP;
        private System.Windows.Forms.TextBox txtIP;
        private System.Windows.Forms.Button btnConnect;
        private System.Windows.Forms.Button btnSendMsg;
        private System.Windows.Forms.Button btnWaitAccess;
        private System.Windows.Forms.RichTextBox txtGetMsg;
        private System.Windows.Forms.Label lblState;
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;

namespace Chatting
{
    static class Program
    {
        /// <summary>
        /// 应用程序的主入口点。
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new MainForm());
        }
    }
}

 

posted @ 2013-05-28 10:07  神在卖炉子  阅读(1518)  评论(2编辑  收藏  举报