和飞秋的通讯实现

飞秋是什么?

飞秋(FeiQ)是一款局域网聊天传送文件的绿色软件,它参考了飞鸽传书(IPMSG)和QQ,
完全兼容飞鸽传书(IPMSG)协议,具有局域网传送方便,速度快,操作简单的优点,同
时具有QQ中的一些功能。

功能说明:
- 飞秋(FeiQ)是一款局域网内即时通信软件, 基于 TCP/IP(UDP).
- 完全兼容网上广为流传的飞鸽传书并比原来飞鸽功能更加强大.
- 不需要服务器支持.
- 支持文件/文件夹的传送 (支持大文件传送[4G以上]), 发送方和接收方

先给飞秋做下广告。以上的说明简介转自华军软件园:http://www.newhua.com/soft/66046.htm

下面转入正题,我们公司同时之间习惯使用飞秋来进行通讯,同时也用QQ,他们各有各的好处的特点,飞秋的好处就是,不需要注册帐号,简单方便。但是飞秋在windows2003系统下经常会卡死,不知道是公司网络问题,还是其他什么问题。后来也考虑使用其他的即时im来取代它,尝试过,后来放弃了,毕竟习惯难以改变。既然如此,我们就继续用吧,不过我们想为飞秋增加一些功能,或者想使用飞秋做提醒,那么原来的飞秋就不那么方便了。于是我考虑写个客户端和它来通讯。

 

飞秋支持ipmsg协议,那就好办了。我们只需要遵守协议和消息机制,就能和其他飞秋客户端互发消息了。

ipmsg协议的内容,参考文档http://www.cnblogs.com/hnrainll/archive/2011/05/07/2039567.html,同时对译者感谢。飞秋的消息发送使用的是udp,消息的格式为:

1:100:shirouzu:Jupiter:32:Hello,以冒号作为分割,分为6短。各段的意思为:

版本号:消息编号:发送人姓名:发送人机器名:命令字:附加内容。

 

那么我依靠这些就能互发消息了。然后还要知道“命令字”的内容。这些命令字的常量定义,我没有找到官方文档。不过协议里提到的指令(命令字)对应的数字(int)是多少?却没有找到,而且到处找不到。后来在一篇文章里见到作者写的代码里有包含一些代码中提到了这些常量的定义,就拿来用了。该文章的地址找不到了,对作者表示歉意。

 

/*  ----------------------------------------------------------
* @名称    :    
* @描述    :    
* @创建人  :    张云飞
* @创建日期:    2011/7/11 13:27:24
* @修改记录:
* ----------------------------------------------------------
*/

using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Text;

namespace  FeiQAPI
{
    
public   enum  IPMSG_Header
    {
        
/*  header  */
        IPMSG_VERSION 
=   0x0001 ,
        IPMSG_DEFAULT_PORT 
=   0x0979 ,
    }

    
public   enum  IPMSG_FileattachCommand
    {
        
/*  file types for fileattach command  */
        IPMSG_FILE_REGULAR 
=   0x00000001 ,
        IPMSG_FILE_DIR 
=   0x00000002 ,
        IPMSG_LISTGET_TIMER 
=   0x0104 ,
        IPMSG_LISTGETRETRY_TIMER 
=   0x0105
    }

    
public   enum  IPMSG_COMMAND :  int
    {
        
/*  command  */
        IPMSG_NOOPERATION 
=   0x00000000 ,

        IPMSG_BR_ENTRY 
=   0x00000001 ,
        IPMSG_BR_EXIT 
=   0x00000002 ,
        IPMSG_ANSENTRY 
=   0x00000003 ,
        IPMSG_BR_ABSENCE 
=   0x00000004 ,

        IPMSG_BR_ISGETLIST 
=   0x00000010 ,
        IPMSG_OKGETLIST 
=   0x00000011 ,
        IPMSG_GETLIST 
=   0x00000012 ,
        IPMSG_ANSLIST 
=   0x00000013 ,
        IPMSG_FILE_MTIME 
=   0x00000014 ,
        IPMSG_FILE_CREATETIME 
=   0x00000016 ,
        IPMSG_BR_ISGETLIST2 
=   0x00000018 ,

        IPMSG_SENDMSG 
=   0x00000020 ,
        IPMSG_RECVMSG 
=   0x00000021 ,
        IPMSG_READMSG 
=   0x00000030 ,
        IPMSG_DELMSG 
=   0x00000031 ,

        
/*  option for all command  */
        IPMSG_ABSENCEOPT 
=   0x00000100 ,
        IPMSG_SERVEROPT 
=   0x00000200 ,
        IPMSG_DIALUPOPT 
=   0x00010000 ,
        IPMSG_FILEATTACHOPT 
=   0x00200000 ,
    }
}

 

有了命令字,然后就是消息对象的MODEL了。我们构建一个消息类的实体,以便在使用中传递消息对象。

 

/*  ----------------------------------------------------------
* @名称    :    
* @描述    :    
* @创建人  :    张云飞
* @创建日期:    2011/7/11 13:35:37
* @修改记录:
* ----------------------------------------------------------
*/

using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Text;

namespace  FeiQAPI
{
    
///   <summary>
    
///  版本号(1):包编号:发送者姓名:发送者主机名:命令字:附加信息
    
///   </summary>
     public   class  IPMSG
    {
        
string  _Version;

        
///   <summary>
        
///  版本号(1)
        
///   </summary>
         public   string  Version
        {
            
get  {  return  _Version; }
            
set  { _Version  =  value; }
        }

        
string  _PackageID;

        
///   <summary>
        
///  包编号
        
///   </summary>
         public   string  PackageID
        {
            
get  {  return  _PackageID; }
            
set  { _PackageID  =  value; }
        }

        
string  _SenderName;

        
///   <summary>
        
///  发送者姓名
        
///   </summary>
         public   string  SenderName
        {
            
get  {  return  _SenderName; }
            
set  { _SenderName  =  value; }
        }

        
string  _SenderHostName;

        
///   <summary>
        
///  发送者主机名
        
///   </summary>
         public   string  SenderHostName
        {
            
get  {  return  _SenderHostName; }
            
set  { _SenderHostName  =  value; }
        }
        IPMSG_COMMAND _Msg_Command;

        
///   <summary>
        
///  命令字
        
///   </summary>
         public  IPMSG_COMMAND Msg_Command
        {
            
get  {  return  _Msg_Command; }
            
set  { _Msg_Command  =  value; }
        }

        
string  _Option_Data;

        
///   <summary>
        
///  附加信息
        
///   </summary>
         public   string  Option_Data
        {
            
get  {  return  _Option_Data; }
            
set  { _Option_Data  =  value; }
        }

        
///   <summary>
        
///  转换
        
///   </summary>
         public   static  IPMSG Parse(Byte[] bytes,  int  index,  int  count)
        {
            
return  Parse(Encoding.Default.GetString(bytes,index,count));
        }

        
///   <summary>
        
///  转换
        
///   </summary>
        
///   <param name="msgStr"></param>
        
///   <returns></returns>
         public   static  IPMSG Parse( string  msgStr)
        {
            
if  ( string .IsNullOrEmpty(msgStr))
            {
                
throw   new  ArgumentNullException();
            }

            
///  版本号(1):包编号:发送者姓名:发送者主机名:命令字:附加信息
             string [] strArr  =  msgStr.Split( new   char [] {  ' : '  }, StringSplitOptions.None);
            Console.WriteLine(
" Receive:{0} ,Split array length={1} " , msgStr, strArr.Length);
            
if  (strArr.Length  ==   6 )
            {
                IPMSG msg 
=   new  IPMSG();
                msg.Version 
=  strArr[ 0 ];
                msg.PackageID 
=  strArr[ 1 ];
                msg.SenderName 
=  strArr[ 2 ];
                msg.SenderHostName 
=  strArr[ 3 ];
                msg.Msg_Command 
=  (IPMSG_COMMAND)Enum.Parse( typeof (IPMSG_COMMAND), strArr[ 4 ]);
                msg.Option_Data 
=  strArr[ 5 ];
                
return  msg;
            }
            
return   null ;

        }

        
///   <summary>
        
///  转化到消息
        
///   </summary>
        
///   <returns></returns>
         public   string  ToMsgStr()
        {
            
return   string .Format( " {0}:{1}:{2}:{3}:{4}:{5} " ,
                Version 
??   " 1 " ,
                PackageID 
??  DateTime.Now.Ticks.ToString(),
                SenderName 
??   "" ,
                SenderHostName 
??   "" ,
                ((
int )Msg_Command).ToString(),
                Option_Data 
??   "" );
        }

        
public   byte [] ToBytes()
        {
            
string  str  =  ToMsgStr();
            Console.WriteLine(
" send Msg:  "   +  str);
            
return  Encoding.Default.GetBytes(str);
        }
    }
}

 

上面的实体基本 包含了6个属性,对应协议里的6个部分。同时写了 转换的方法

ToBytes,ToMsgStr,Parse等。实现了消息对象和 字节 之间的转换,便于传输。

 

好了,现在我们构建Socket对象。

 

               _socket  =   new  Socket(AddressFamily.InterNetwork, SocketType.Dgram,
                     ProtocolType.Udp);

                IPEndPoint ip 
=   new  IPEndPoint(IPAddress.Any, _Port);
                _socket.Bind(ip);

 

标准的构建socket的代码。端口我们指定为:         int _Port = 2425;

 

现在尝试发送消息,如下代码所示Dns.GetHostName是获得本级机器名的方法。 在下面的方法里,构建里一个IPMSG 对象,并对该对象的各个部分赋值,然后调用socket.SendTo方法,将数据包发送出去。

        public   void  SendMsg( string  targetIP,  string  msgText)
        {
            
byte [] byts  =   null ;
            IPMSG msg 
=   new  IPMSG();
            msg.SenderName 
=  MyName;
            msg.SenderHostName 
=  Dns.GetHostName();
            msg.Msg_Command 
=  IPMSG_COMMAND.IPMSG_SENDMSG; // IPMSG_SENDMSG
            msg.Option_Data  =  msgText;
            byts 
=  msg.ToBytes();

            EndPoint ep 
=  (EndPoint) new  IPEndPoint(IPAddress.Parse(targetIP), _Port);
            _socket.SendTo(byts, ep);
        }

 

那么如何接收消息呢?

 

                 byte [] buf  =   new   byte [ 1024 ];
                
while  (_Running)
                {
                    
// Socket socketClient = socket.Accept();
                    EndPoint remote  =  (EndPoint)( new  IPEndPoint(IPAddress.Any,  0 ));

                    
int  len  =  _socket.ReceiveFrom(buf,  ref  remote);
                    
if  (len  >   0 )
                    {
                        
string  msg  =  Encoding.Default.GetString(buf,  0 , len);
                        Console.WriteLine(msg);
                        IPMSG msg1 
=  IPMSG.Parse(buf,  0 , len);
                        Action
< IPMSG >  act  =   new  Action < IPMSG > (ProcessMessage);
                        
if  (msg  !=   null )
                        {
                            act.BeginInvoke(msg1, 
null null );
                        }
                        
// Println(msg + Environment.NewLine);

                        len 
=   0 ;
                    }
                }

 

调用socket.ReceiveFrom方法接受到网络发来的数据包,使用我们写好的解析法方法,我们的解析方法遵守了ipmsg协议。

                     string msg = Encoding.Default.GetString(buf, 0, len);
                     IPMSG msg1 
= IPMSG.Parse(buf, 0, len);
这样我们就拿到一个IPMSG对象了,该消息对象包含了其他客户机发来的消息内容,这些内容可能是,文本消息,上线消息,回复消息,文件消息等。根据消息里的 指令类型 我们需要对接受到的消息做不同的处理。

注意上面的代码中:

                       Action<IPMSG> act = new Action<IPMSG>(ProcessMessage);
                        
if (msg != null)
                        {
                            act.BeginInvoke(msg1, 
nullnull);
                        }
我是使用了一个异步委托。代码执行到这里的时候不至于阻塞。

 

我们接收到了消息后,根据消息的内容做不同的处理,下面的处理消息的代码:

         private   void  ProcessMessage(IPMSG msg)
        {
            Console.WriteLine(msg.Msg_Command);

            IPMSG_COMMAND mode 
=  msg.Msg_Command;

            
if  ((IPMSG_COMMAND.IPMSG_ANSENTRY  &  mode)  ==  IPMSG_COMMAND.IPMSG_ANSENTRY)
            {
                
// 让用户上线
                ProcessUseOnLine(msg);
                
return ;
            }
            
if  ((IPMSG_COMMAND.IPMSG_BR_ENTRY  &  mode)  ==  IPMSG_COMMAND.IPMSG_BR_ENTRY)
            {
                
// 让用户上线
                
// ReceiveMsg(msg);
                 return ;
            }
            
if  ((IPMSG_COMMAND.IPMSG_SENDMSG  &  mode)  ==  IPMSG_COMMAND.IPMSG_SENDMSG)
            {
                
// 让用户上线
                ReceiveMsg(msg);
                
return ;
            }
            
// if ((IPMSG_COMMAND.IPMSG_RECVMSG & mode) == IPMSG_COMMAND_SENDCHECKOPT)
            
// {
            
//      // 让用户上线
            
//     ReceiveMsg(msg);
            
// }
        }

 

我们是如何通知 我们上线了呢? 向局域网内的其他用户发送广播消息。收到这些消息的客户端就会将我们加入到他们的好友列表,同时各个客户端会回复一个消息给我们,籍此,我们就知道了网络上那些客户端,我们把这些客户端加入到我们的好友列表中。

 

        //发送广播,通知我上线了
        public   void  SendMyOnline()
        {
            
byte [] byts  =   null ;
            IPMSG msg 
=   new  IPMSG();
            msg.SenderName 
=  MyName;
            msg.SenderHostName 
=  Dns.GetHostName();
            msg.Msg_Command 
=  IPMSG_COMMAND.IPMSG_BR_ENTRY;
            msg.Option_Data 
=   string .Format( " {0}\0{1}\0{1}{2} " , MyName, MyGroup,  "" );
            byts 
=  msg.ToBytes();

            EndPoint ep 
=  (EndPoint) new  IPEndPoint(IPAddress.Parse( " 192.168.3.255 " ), _Port);
            _socket.SendTo(byts, ep);
        }

然后要处理收到的 其他客户机 给我们的回复,该回复了包含了 其他各个客户机的位置信息

 

             if  ((IPMSG_COMMAND.IPMSG_ANSENTRY  &  mode)  ==  IPMSG_COMMAND.IPMSG_ANSENTRY)
            {
                
// 让用户上线
                ProcessUseOnLine(msg);
                
return ;
            }

 

        //处理其他人 的上线消息,就是把这些消息内包含的用户地址等,加入到我们的好友列表中去。
        public   void  ProcessUseOnLine(IPMSG msg)
        {
            
string  optionData  =  msg.Option_Data;
            
string [] arr  =  optionData.Split( new   char [ ' / ' ], StringSplitOptions.None);
            
string  displayName  =   "" ;
            
string  group  =   "" ;
            
if  (arr  !=   null   &&  arr.Length  >   0 )
            {
                
if  (arr.Length  >   0 )
                {
                    displayName 
=  arr[ 0 ];
                }
                
if  (arr.Length  >   1 )
                {
                    group 
=  arr[ 1 ];

                }
            }

            _UserList.AddUser(msg.SenderName, msg.SenderHostName, displayName, group);
        }

 

下面是我的好友类,和好友列表类:

 

/*  ----------------------------------------------------------
* @名称    :    
* @描述    :    
* @创建人  :    张云飞
* @创建日期:    2011/7/11 14:27:48
* @修改记录:
* ----------------------------------------------------------
*/

using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Text;

namespace  FeiQAPI
{
    
///   <summary>
    
///  用户
    
///   </summary>
     public   struct  User
    {
        
public   string  UserName;    // 用户名
         public   string  HostName;   // 机器名

        
// public string id;                // 节点ID。
         public   string  HostIp;      // 存储IP信息,避免重复添加

        
public   string  Displayname;
        
public   string  Group;

        
// public object Inet;   // 存储网络信息

    }
}
/*  ----------------------------------------------------------
* @名称    :    
* @描述    :    
* @创建人  :    张云飞
* @创建日期:    2011/7/11 14:35:45
* @修改记录:
* ----------------------------------------------------------
*/

using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Text;
using  System.Net;
using  System.Net.Sockets;

namespace  FeiQAPI
{
    
///   <summary>
    
///  用户列表
    
///   </summary>
     public   class  UserList
    {
        Dictionary
< string , User >  _lstUsers  =   new  Dictionary < string , User > ();
        
public   event  EventHandler < UserEventArgs >  OnUserAdded;


        
public   void  AddUser( string  userName,  string  hostName,  string  displayName,  string  group)
        {
            
string  ip  =   null ;
            
try
            {
                
if  (Dns.GetHostEntry(hostName)  !=   null   &&  Dns.GetHostEntry(hostName).AddressList  !=   null   &&  Dns.GetHostEntry(hostName).AddressList.Length  >   0 )
                {
                    ip 
=  Dns.GetHostEntry(hostName).AddressList.Last().ToString();
                    User user 
=   new  User();
                    user.UserName 
=  userName;
                    user.HostName 
=  hostName;
                    user.HostIp 
=  ip;
                    user.Displayname 
=  displayName;
                    user.Group 
=  group;

                    AddUser(user);
                }
            }
            
catch  (SocketException)
            {

            }
            
catch  (Exception)
            {

                
throw ;
            }

        }

        
public   void  AddUser(User user)
        {
            
if  ( ! _lstUsers.ContainsKey(user.HostIp))
            {
                _lstUsers.Add(user.HostIp, user);
                
if  (OnUserAdded  !=   null )
                {
                    OnUserAdded(
this new  UserEventArgs(user));
                }
            }
        }

        
///   <summary>
        
///  获得用户
        
///   </summary>
        
///   <param name="senderName"></param>
        
///   <param name="hostName"></param>
        
///   <returns></returns>
         public  User ?  GetUser( string  senderName,  string  hostName)
        {
            
return  _lstUsers.Values.SingleOrDefault(p  =>  p.UserName  ==  senderName  &&  p.HostName  ==  hostName);
        }
    }
}

 

最后贴出我的完整的处理 消息 的服务器类:

 

/*  ----------------------------------------------------------
* @名称    :    
* @描述    :    
* @创建人  :    张云飞
* @创建日期:    2011/7/11 14:01:32
* @修改记录:
* ----------------------------------------------------------
*/

using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Text;
using  System.Net;
using  System.Net.Sockets;
using  System.Threading;

namespace  FeiQAPI
{
    
///   <summary>
    
///  消息服务器
    
///   </summary>
     public   class  MsgServer
    {
        
///   <summary>
        
///  当接受的消息时
        
///   </summary>
         public   event  EventHandler < IpMsg_EventArgs >  OnReceiveMsg;

        
int  _Port  =   2425 ;
        
bool  _Running  =   false ;

        
private   string  _MyName;
        
private   string  _MyGroup;

        
///   <summary>
        
///  分组
        
///   </summary>
         public   string  MyGroup
        {
            
get  {  return  _MyGroup; }
            
set  { _MyGroup  =  value; }
        }

        
public   string  MyName
        {
            
get  {  return  _MyName; }
            
set  { _MyName  =  value; }
        }

        
public   bool  Running
        {
            
get  {  return  _Running; }
        }

        Socket _socket;

        UserList _UserList 
=   new  UserList();

        
///   <summary>
        
///  用户列表
        
///   </summary>
         public  UserList UserList
        {
            
get  {  return  _UserList; }
        }

        
// public void Send
         public   void  ProcessUseOnLine(IPMSG msg)
        {
            
string  optionData  =  msg.Option_Data;
            
string [] arr  =  optionData.Split( new   char [ ' / ' ], StringSplitOptions.None);
            
string  displayName  =   "" ;
            
string  group  =   "" ;
            
if  (arr  !=   null   &&  arr.Length  >   0 )
            {
                
if  (arr.Length  >   0 )
                {
                    displayName 
=  arr[ 0 ];
                }
                
if  (arr.Length  >   1 )
                {
                    group 
=  arr[ 1 ];

                }
            }

            _UserList.AddUser(msg.SenderName, msg.SenderHostName, displayName, group);
        }

        
public   void  Start()
        {
            ThreadPool.QueueUserWorkItem(
new  WaitCallback( delegate
            {
                Run();

            }));
        }

        
private   void  Run()
        {
            
if  (_Running  ==   false )
            {
                _socket 
=   new  Socket(AddressFamily.InterNetwork, SocketType.Dgram,
                     ProtocolType.Udp);

                IPEndPoint ip 
=   new  IPEndPoint(IPAddress.Any, _Port);
                _socket.Bind(ip);
                
// socket.Listen(10);

                _Running 
=   true ;
                SendMyOnline();

                
byte [] buf  =   new   byte [ 1024 ];
                
while  (_Running)
                {
                    
// Socket socketClient = socket.Accept();
                    EndPoint remote  =  (EndPoint)( new  IPEndPoint(IPAddress.Any,  0 ));

                    
int  len  =  _socket.ReceiveFrom(buf,  ref  remote);
                    
if  (len  >   0 )
                    {
                        
string  msg  =  Encoding.Default.GetString(buf,  0 , len);
                        Console.WriteLine(msg);
                        IPMSG msg1 
=  IPMSG.Parse(buf,  0 , len);
                        Action
< IPMSG >  act  =   new  Action < IPMSG > (ProcessMessage);
                        
if  (msg  !=   null )
                        {
                            act.BeginInvoke(msg1, 
null null );
                        }
                        
// Println(msg + Environment.NewLine);

                        len 
=   0 ;
                    }
                }
            }
        }

        
private   void  ProcessMessage(IPMSG msg)
        {
            Console.WriteLine(msg.Msg_Command);

            IPMSG_COMMAND mode 
=  msg.Msg_Command;

            
if  ((IPMSG_COMMAND.IPMSG_ANSENTRY  &  mode)  ==  IPMSG_COMMAND.IPMSG_ANSENTRY)
            {
                
// 让用户上线
                ProcessUseOnLine(msg);
                
return ;
            }
            
if  ((IPMSG_COMMAND.IPMSG_BR_ENTRY  &  mode)  ==  IPMSG_COMMAND.IPMSG_BR_ENTRY)
            {
                
// 让用户上线
                
// ReceiveMsg(msg);
                 return ;
            }
            
if  ((IPMSG_COMMAND.IPMSG_SENDMSG  &  mode)  ==  IPMSG_COMMAND.IPMSG_SENDMSG)
            {
                
// 让用户上线
                ReceiveMsg(msg);
                
return ;
            }
            
// if ((IPMSG_COMMAND.IPMSG_RECVMSG & mode) == IPMSG_COMMAND_SENDCHECKOPT)
            
// {
            
//      // 让用户上线
            
//     ReceiveMsg(msg);
            
// }
        }

        
private   void  ReceiveMsg(IPMSG msg)
        {
            
if  (OnReceiveMsg  !=   null )
            {
                OnReceiveMsg(
this new  IpMsg_EventArgs(msg));
            }
        }

        
public   void  SendMyOnline()
        {
            
byte [] byts  =   null ;
            IPMSG msg 
=   new  IPMSG();
            msg.SenderName 
=  MyName;
            msg.SenderHostName 
=  Dns.GetHostName();
            msg.Msg_Command 
=  IPMSG_COMMAND.IPMSG_BR_ENTRY;
            msg.Option_Data 
=   string .Format( " {0}\0{1}\0{1}{2} " , MyName, MyGroup,  "" );
            byts 
=  msg.ToBytes();

            EndPoint ep 
=  (EndPoint) new  IPEndPoint(IPAddress.Parse( " 192.168.3.255 " ), _Port);
            _socket.SendTo(byts, ep);
        }

        
public   void  Stop()
        {
            _Running 
=   false ;
        }

        
public   void  SendMsg(User _CurrentUser,  string  msgText)
        {
            SendMsg(_CurrentUser.HostIp, msgText);
        }

        
public   void  SendMsg( string  targetIP,  string  msgText)
        {
            
byte [] byts  =   null ;
            IPMSG msg 
=   new  IPMSG();
            msg.SenderName 
=  MyName;
            msg.SenderHostName 
=  Dns.GetHostName();
            msg.Msg_Command 
=  IPMSG_COMMAND.IPMSG_SENDMSG; // IPMSG_SENDMSG
            msg.Option_Data  =  msgText;
            byts 
=  msg.ToBytes();

            EndPoint ep 
=  (EndPoint) new  IPEndPoint(IPAddress.Parse(targetIP), _Port);
            _socket.SendTo(byts, ep);
        }
    }
}

 

同时提供完整的源代码下载

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Net; using System.Net.Sockets; using System.Threading; namespace WindowsFormsApplication2 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private int port = 49151; private UdpClient udpclient; delegate void AppendString(sendData sendDatas);//添加用户列表委托 AppendString appendString; delegate void AppendStrings(string text);//添加广播消息委托 AppendStrings appendStrings; delegate void AppendStringGB(sendData text);//添加私人消息委托 AppendStringGB appendStringGB; public sendData sendDatas = new sendData(); IPHostEntry myentry; IPAddress myIp; Thread MyRecData; private void Form1_Load(object sender, EventArgs e) { udpclient = new UdpClient(port); myentry = Dns.GetHostEntry(Dns.GetHostName()); IPselect(); sendDatas.fromIP = myIp; sendDatas.toIP = IPAddress.Parse("255.255.255.255"); sendDatas.content = ""; appendString = new AppendString(AppString); appendStrings = new AppendStrings(AppStrings); appendStringGB = new AppendStringGB(AppStringGB); MyRecData = new Thread(new ThreadStart(RecData)); MyRecData.IsBackground = true; MyRecData.Start(); States(); } /// <summary> /// 广播本机IP /// </summary> public void States() { IPEndPoint iep = new IPEndPoint(IPAddress.Broadcast, port); byte[] sen = Class2.senDataToByte(sendDatas); udpclient.Send(sen, sen.Length, iep); } /// <summary> /// 获取本机IP /// </summary> public void IPselect()//查找本机第一个IP地址 { string strHostName; strHostName = Dns.GetHostName(); Console.WriteLine("本机名:{0}", strHostName); IPHostEntry ipEntry = Dns.GetHostEntry(strHostName); IPAddress[] ipAdd = ipEntry.AddressList; foreach (IPAddress ip in ipAdd) { if (ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) { myIp = ip; return; } } } /// <summary> /// 私聊消息写入消息框 /// </summary> /// <param name="text"></param> private void AppString(sendData sendDatas) { int i = forms(sendDatas.fromIP); if (arryFrom[i].Forms.InvokeRequired == true) { arryFrom[i].Forms.Invoke(appendString, sendDatas); } else { arryFrom[i].Forms.textBox1.AppendText("信息来自"+sendDatas.fromIP+":\r\n"+sendDatas.content + "\r\n"); } } /// <summary> /// 广播消息写入 /// </summary> /// <param name="text"></param> private void AppStringGB(sendData text) { if (textBox1.InvokeRequired == true) { this.Invoke(appendStringGB, text); } else { textBox1.AppendText("消息来自"+text.fromIP+":\r\n"+text.content+"\r\n"); } } /// <summary> /// 判断接收的私聊消息是否有窗体,有就反回是哪个窗体。没有就创建一个窗体反悔集合最后一个索引 /// </summary> /// <param name="sens"></param> /// <returns></returns> public int forms(IPAddress sens) { int f = 0; foreach (ArraysForm af in arryFrom) { if(sens.Equals(af.ToIp)) { return f; } f++; } MethodInvoker mi = new MethodInvoker(this.PerChat);//使用MethodInvoker委托跨线程访问 this.BeginInvoke(mi); Thread.Sleep(100); return arryFrom.Count-1; } /// <summary> /// 添加发言者的IP到用户列表有的将不添加 /// </summary> /// <param name="text"></param> private void AppStrings(string text) { foreach(string str in listBox1.Items) { if(str.Equals(text)) { return; } } if (listBox1.InvokeRequired == true)//使用委托跨线程访问 { this.Invoke(appendStrings, text); } else { listBox1.Items.Add(text); States(); } } sendData sendsa; /// <summary> /// 接收广播消息 /// </summary> private void RecData() { IPEndPoint remote = null; while (true) { try { byte[] bytes = udpclient.Receive(ref remote); sendsa = Class2.byteTosendData(bytes); AppStrings(sendsa.fromIP.ToString()); string str = sendsa.content; string form = sendsa.fromIP.ToString(); if (str != ""&&sendsa;.toIP.Equals(IPAddress.Parse("255.255.255.255"))) { AppStringGB(sendsa); } if(sendsa.toIP.Equals(myIp)) { AppString(sendsa); } } catch(Exception e) { //MessageBox.Show(e.Message); break; } } } private void button1_Click(object sender, EventArgs e) { Sees(); } /// <summary> /// 广播消息 /// </summary> public void Sees() { UdpClient myUdpClient = new UdpClient(); try { sendData sens = new sendData(); IPEndPoint iep = new IPEndPoint(IPAddress.Broadcast, port); sens.fromIP = myIp; sens.toIP = IPAddress.Parse("255.255.255.255"); sens.content = textBox2.Text; byte[] bytes = Class2.senDataToByte(sens); myUdpClient.Send(bytes, bytes.Length, iep); textBox2.Clear(); myUdpClient.Close(); } catch { MessageBox.Show("发送失败!"); } finally { myUdpClient.Close(); } } /// <summary> /// 关闭窗口 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Form1_FormClosing(object sender, FormClosingEventArgs e) { udpclient.Close(); } /// <summary> /// 双击用户可以私聊 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void listBox1_DoubleClick(object sender, EventArgs e) { sendsa.fromIP = IPAddress.Parse(listBox1.Text); PerChat(); } /// <summary> /// 右键点击聊天可以私聊 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void 发送消息ToolStripMenuItem_Click(object sender, EventArgs e) { sendsa.fromIP = IPAddress.Parse(listBox1.Text); PerChat(); } public static List<ArraysForm> arryFrom = new List<ArraysForm>();// 装私聊窗口的集合 PersonalChat[] perChat=new PersonalChat[100];//私聊窗口数组 ArraysForm aForm;//私聊窗体类,包含聊天对象IP和窗体 int index = 0; /// <summary> /// 新建私聊窗口窗口, /// </summary> public void PerChat() { sendData preChatsenData = new sendData(); preChatsenData.fromIP = myIp; preChatsenData.toIP = sendsa.fromIP; preChatsenData.content = ""; perChat[index] = new PersonalChat(preChatsenData); perChat[index].Show(); aForm = new ArraysForm(perChat[index], preChatsenData.fromIP); arryFrom.Add(aForm); index++; } /// <summary> /// Enter发送消息 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void textBox2_KeyDown(object sender, KeyEventArgs e) { //if (e.KeyData == (Keys.Enter | Keys.Control)) //{ // textBox2.AppendText("\r\n"); //} if(e.KeyData==Keys.Enter) { Sees(); } } /// <summary> /// 发送完消息清空textBox /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void textBox2_KeyUp(object sender, KeyEventArgs e) { if (e.KeyData == Keys.Enter) { textBox2.Clear(); } } } }

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值