UDP聊天穿透(服务器+客户端)源码


//WellKnown公用库
namespace P2P.WellKnown
{
    using System;
    using System.IO;
    using System.Runtime.Serialization.Formatters.Binary;
    using System.Collections;
    using System.Net.Sockets;
    using System.Net;
    /// <summary>
    /// P2PConsts 的摘要说明。
    /// </summary>
    [Serializable]
    public class P2PConsts
    {
        /// <summary>
        /// 服务器侦听端口号
        /// </summary>
        public const int SRV_PORT  = 2280;
    }
    /// <summary>
    /// User 的摘要说明。
    /// </summary>
    [Serializable]
    public class User
    {
       
        protected string userName;
        protected IPEndPoint netPoint;
        public User(string UserName, IPEndPoint NetPoint)
        {
            this.userName = userName;
            this.netPoint = NetPoint;
        }
        public string UserName
        {
            get { return userName; }
        }
        public IPEndPoint NetPoint
        {
            get { return netPoint; }
            set { netPoint = value;}
        }

    }
    /// <summary>
    /// UserCollection 的摘要说明。
    /// </summary>
    [Serializable]
    public class UserCollection : CollectionBase
    {
       
        public void Add(User user)
        {
            InnerList.Add(user);
        }
        public void Remove(User user)
        {
            InnerList.Remove(user);
        }
        public User this[int index]
        {
            get { return (User)InnerList[index]; }
        }
        public User Find(string userName)
        {
            foreach(User user in this)
            {
                if (string.Compare(userName, user.UserName, true) == 0)
                {
                    return user;
                }
            }
            return null;
        }
    }

    /// <summary>
    /// FormatterHelper 序列化,反序列化消息的帮助类
    /// </summary>
    [Serializable]
    public class FormatterHelper
    {
        public static byte[] Serialize(object obj)
        {
            BinaryFormatter binaryF = new BinaryFormatter();
            MemoryStream ms = new MemoryStream(1024*10);
            binaryF.Serialize(ms, obj);
            ms.Seek(0, SeekOrigin.Begin);
            byte[] buffer = new byte[(int)ms.Length];
            ms.Read(buffer, 0, buffer.Length);
            ms.Close();
            return buffer;

        }

        public static object Deserialize(byte[] buffer)
        {

            BinaryFormatter binaryF = new BinaryFormatter();
            MemoryStream ms = new MemoryStream(buffer, 0, buffer.Length, false);
            object obj = binaryF.Deserialize(ms);
            ms.Close();
            return obj;
        }

    }
    /// <summary>
    /// Message base class
    /// </summary>
    public abstract class MessageBase
    {

    }
    // Message from Client to Server
    namespace C2S
    {
        /// <summary>
        /// 客户端发送到服务器的消息基类
        /// </summary>
        public abstract class CSMessage : MessageBase
        {
            private string userName;
            protected CSMessage(string anUserName)
            {
                userName = anUserName;
            }
            public string UserName
            {
                get { return userName; }
            }
        }

        /// <summary>
        /// 用户登录消息
        /// </summary>
        public class LoginMessage : CSMessage
        {

            private string password;
            public LoginMessage(string userName, string password) : base(userName)
            {
                this.password = password;
            }
            public string Password
            {
                get { return password; }
            }
        }
        /// <summary>
        /// 用户登出消息
        /// </summary>
        public class LogoutMessage : CSMessage
        {
            public LogoutMessage(string userName) : base(userName)
            {}
        }
        /// <summary>
        /// 请求用户列表消息
        /// </summary>
        public class GetUsersMessage : CSMessage
        {

            public GetUsersMessage(string userName) : base(userName)
            {}

        }

        /// <summary>
        /// 请求Purch Hole消息
        /// </summary>
        public class TranslateMessage : CSMessage
        {
            protected string toUserName;
            public TranslateMessage(string userName, string toUserName) : base(userName)
            {
                this.toUserName = toUserName;
            }
            public string ToUserName
            {
                get { return this.toUserName; }
            }

        }

    }

 

    // Message from server to the client
    namespace S2C
    {

        /// <summary>
        /// 服务器发送到客户端消息基类
        /// </summary>
        public abstract class SCMessage : MessageBase
        {}
        /// <summary>
        /// 请求用户列表应答消息
        /// </summary>
        public class GetUsersResponseMessage : SCMessage
        {
            private UserCollection userList;
            public GetUsersResponseMessage(UserCollection users)
            {
                this.userList = users;
            }
            public UserCollection UserList
            {
                get { return userList; }
            }

        }

        /// <summary>
        /// 转发请求Purch Hole消息
        /// </summary>
        public class SomeOneCallYouMessage : SCMessage
        {
            protected System.Net.IPEndPoint remotePoint;
            public SomeOneCallYouMessage(System.Net.IPEndPoint point)
            {
                this.remotePoint = point;
            }
            public System.Net.IPEndPoint RemotePoint
            {
                get { return remotePoint; }
            }

        }

    }

 

    // Message from peer to the peer
    namespace P2P
    {
        /// <summary>
        /// 点对点消息基类
        /// </summary>
        public abstract class PPMessage : MessageBase
        {}
        /// <summary>
        /// 测试消息
        /// </summary>
        public class WorkMessage : PPMessage
        {
            private string message;
            public WorkMessage(string msg)
            {
                message = msg;
            }
            public string Message
            {
                get { return message; }
            }

        }

        /// <summary>
        /// 测试应答消息
        /// </summary>
        public class ACKMessage : PPMessage
        {

        }
        /// <summary>
        /// P2P Purch Hole Message
        /// </summary>
        public class TrashMessage : PPMessage
        {}

    } 

}

 

//  P2Pserver
namespace P2P.P2PServer
{
    using System;
    using System.Net;
    using System.Net.Sockets;
    using System.Threading;
    using P2P.WellKnown;
    /// <summary>
    /// AppClass 的摘要说明。
    /// </summary>
    public class AppClass_RunChatServer
    {
        private static Server server;//我添加
        public static void Main_ChatServer_Start()
        {
            server= new Server();
            try
            {
                server.Start();
                Console.ReadLine();
//                server.Stop();
            }
            catch
            {
            }
        }
        public static void Main_ChatServer_Stop()
        {
            //Server server = new Server();
            try
            {
//                server.Start();
//                Console.ReadLine();
                server.Stop();
            }
            catch
            {
            }
        }
    }

    /// <summary>
    /// Server 的摘要说明。
    /// </summary>
    [Serializable]
    public class Server
    {
        private UdpClient server;
        private UserCollection userList;
        private Thread serverThread;
        private IPEndPoint remotePoint;
        //[Serializable]
        public Server()
        {
            userList = new UserCollection();
            remotePoint = new IPEndPoint(IPAddress.Any, 0);
            serverThread = new Thread(new ThreadStart(Run));
        }
        public void Start()
        {
            try
            {
                server = new UdpClient(P2PConsts.SRV_PORT);
                serverThread.Start();
                Console.WriteLine("P2P Server started, waiting client connect...");
            }
            catch(Exception exp)
            {
                Console.WriteLine("Start P2P Server error: " + exp.Message);
                throw exp;
            }

        }

        public void Stop()
        {
            Console.WriteLine("P2P Server stopping...");
            try
            {
                serverThread.Abort();
                server.Close();
                Console.WriteLine("Stop OK.");
            }
            catch(Exception exp)
            {
                Console.WriteLine("Stop error: " + exp.Message);
                throw exp;
            }

        }

        private void Run()
        {
            byte[] buffer = null;
            while (true)
            {
                byte[] msgBuffer = server.Receive(ref remotePoint);
                try
                {
                    object msgObj = FormatterHelper.Deserialize(msgBuffer);
                    Type msgType = msgObj.GetType();
                    if (msgType == typeof(P2P.WellKnown.C2S.LoginMessage))
                    {
                        // 转换接受的消息
                        P2P.WellKnown.C2S.LoginMessage lginMsg = (P2P.WellKnown.C2S.LoginMessage)msgObj;
                        Console.WriteLine("has an user login: {0}", lginMsg.UserName);
                        // 添加用户到列表
                        IPEndPoint userEndPoint = new IPEndPoint(remotePoint.Address, remotePoint.Port);
                        User user = new User(lginMsg.UserName, userEndPoint);
                        userList.Add(user);
                        // 发送应答消息
                        P2P.WellKnown.S2C.GetUsersResponseMessage usersMsg = new P2P.WellKnown.S2C.GetUsersResponseMessage(userList);
                        buffer = FormatterHelper.Serialize(usersMsg);
                        server.Send(buffer, buffer.Length, remotePoint);
                    }
                    else if (msgType == typeof(P2P.WellKnown.C2S.LogoutMessage))
                    {
                        // 转换接受的消息
                        P2P.WellKnown.C2S.LogoutMessage lgoutMsg = (P2P.WellKnown.C2S.LogoutMessage)msgObj;

                        Console.WriteLine("has an user logout: {0}", lgoutMsg.UserName);
                        // 从列表中删除用户
                        User lgoutUser = userList.Find(lgoutMsg.UserName);
                        if (lgoutUser != null)
                        {
                            userList.Remove(lgoutUser);
                        }

                    }
                    else if (msgType == typeof(P2P.WellKnown.C2S.TranslateMessage))
                    {
                        // 转换接受的消息
                        P2P.WellKnown.C2S.TranslateMessage transMsg = (P2P.WellKnown.C2S.TranslateMessage)msgObj;
                        Console.WriteLine("{0}(1) wants to p2p {2}", remotePoint.Address.ToString(), transMsg.UserName, transMsg.ToUserName);
                        // 获取目标用户
                        User toUser = userList.Find(transMsg.ToUserName);
                        // 转发Purch Hole请求消息
                        if (toUser == null)
                        {
                            Console.WriteLine("Remote host {0} cannot be found at index server", transMsg.ToUserName);                         

                        }
                        else
                        {
                            P2P.WellKnown.S2C.SomeOneCallYouMessage transMsg2 = new P2P.WellKnown.S2C.SomeOneCallYouMessage(remotePoint);
                            buffer = FormatterHelper.Serialize(transMsg);
                            server.Send(buffer, buffer.Length, toUser.NetPoint);                        
                        }
                    }
                    else if (msgType == typeof(P2P.WellKnown.C2S.GetUsersMessage))
                    {
                        // 发送当前用户信息到所有登录客户
                        P2P.WellKnown.S2C.GetUsersResponseMessage srvResMsg = new P2P.WellKnown.S2C.GetUsersResponseMessage(userList);                      
                        buffer = FormatterHelper.Serialize(srvResMsg);
                        foreach(User user in userList)
                        {
                            server.Send(buffer, buffer.Length, user.NetPoint);
                        }
                    }
                    Thread.Sleep(500);
                }
                catch{}

            }

        }

    }

}

 

//  P2Pclient

namespace P2P.P2PClient
{
    using System;
    using System.Net;
    using System.Net.Sockets;
    using System.Threading;
    using P2P.WellKnown;
    /// <summary>
    /// AppClass 的摘要说明。
    /// </summary>
    public class AppClass_RunChatClient
    {
        public static void Main_ChatClient(string ServerIP,string myname,string mypassword)
        {
            Client client = new Client(ServerIP);
            client.ConnectToServer(myname, mypassword);
            client.Start();
            Console.WriteLine("test arguments");
            while (true)
            {
                string str = Console.ReadLine();
                client.PaserCommand(str);
            }
        }
    }

    /// <summary>
    /// Client 的摘要说明。
    /// </summary>
    public class Client : IDisposable
    {
        private const int MAXRETRY = 10;
        private UdpClient client;
        private IPEndPoint hostPoint;
        private IPEndPoint remotePoint;
        private UserCollection userList;
        private string myName;
        private bool ReceivedACK;
        private Thread listenThread;

        public Client(string serverIP)
        {
            ReceivedACK = false;
            remotePoint = new IPEndPoint(IPAddress.Any, 0);
            hostPoint = new IPEndPoint(IPAddress.Parse(serverIP), P2PConsts.SRV_PORT);
            client = new UdpClient();
            userList = new UserCollection();
            listenThread = new Thread(new ThreadStart(Run));
        }

        public void Start()
        {
            if (this.listenThread.ThreadState==ThreadState.Unstarted)
            {
                this.listenThread.Start();
                Console.WriteLine("You can input you command:/n");
                Console.WriteLine("Command Type:/"send/",/"exit/",/"getu/"");
                Console.WriteLine("Example : send Username Message");
                Console.WriteLine("          exit");
                Console.WriteLine("          getu");
            }

        }

        public void ConnectToServer(string userName, string password)
        {
            myName = userName;
            // 发送登录消息到服务器

            P2P.WellKnown.C2S.LoginMessage lginMsg = new P2P.WellKnown.C2S.LoginMessage(userName, password);

            byte[] buffer = FormatterHelper.Serialize(lginMsg);
            client.Send(buffer, buffer.Length, hostPoint);
            // 接受服务器的登录应答消息
            buffer = client.Receive(ref remotePoint);
            P2P.WellKnown.S2C.GetUsersResponseMessage srvResMsg = (P2P.WellKnown.S2C.GetUsersResponseMessage)FormatterHelper.Deserialize(buffer);
            // 更新用户列表
            userList.Clear();
            foreach(User user in srvResMsg.UserList)
            {
                userList.Add(user);
            }
            this.DisplayUsers(userList);

        }

 

        /// <summary>
        /// 这是主要的函数:发送一个消息给某个用户(C)
        /// 流程:直接向某个用户的外网IP发送消息,如果此前没有联系过
        /// 那么此消息将无法发送,发送端等待超时。
        /// 超时后,发送端将发送一个请求信息到服务端,要求服务端发送
        /// 给客户C一个请求,请求C给本机发送打洞消息
        /// *以上流程将重复MAXRETRY次
        /// </summary>
        /// <param name="toUserName">对方用户名</param>
        /// <param name="message">待发送的消息</param>
        /// <returns></returns>
        private bool SendMessageTo(string toUserName, string message)
        {

            User toUser = userList.Find(toUserName);
            if (toUser == null)
            {
                return false;
            }
            for (int i=0; i<MAXRETRY; i++)
            {

                P2P.WellKnown.P2P.WorkMessage workMsg = new P2P.WellKnown.P2P.WorkMessage(message);
                byte[] buffer = FormatterHelper.Serialize(workMsg);
                client.Send(buffer, buffer.Length, toUser.NetPoint);
             
                // 等待接收线程将标记修改
                for (int j=0; j<10; j++)
                {
                    if (this.ReceivedACK)
                    {
                        this.ReceivedACK = false;
                        return true;
                    }
                    else
                    {
                        Thread.Sleep(300);
                    }

                }

                // 没有接收到目标主机的回应,认为目标主机的端口映射没有
                // 打开,那么发送请求信息给服务器,要服务器告诉目标主机
                // 打开映射端口(UDP打洞)
                P2P.WellKnown.C2S.TranslateMessage transMsg = new P2P.WellKnown.C2S.TranslateMessage(myName, toUserName);
                buffer = FormatterHelper.Serialize(transMsg);
                client.Send(buffer, buffer.Length, hostPoint);
                // 等待对方先发送信息
                Thread.Sleep(100);
            }
            return false;
        }
 
        public void PaserCommand(string cmdstring)
        {
            cmdstring = cmdstring.Trim();
            string[] args = cmdstring.Split(new char[]{' '});
            if (args.Length > 0)
            {
                if (string.Compare(args[0], "exit", true) == 0)
                {

                    P2P.WellKnown.C2S.LogoutMessage lgoutMsg = new P2P.WellKnown.C2S.LogoutMessage(myName);
                    byte[] buffer = FormatterHelper.Serialize(lgoutMsg);
                    client.Send(buffer, buffer.Length, hostPoint);
                    // do clear something here
                    Dispose();
                    System.Environment.Exit(0);
                }
                else if (string.Compare(args[0], "send", true) == 0)
                {                 
                    if (args.Length > 2)
                    {
                        string toUserName = args[1];
                        string message    = "";
                        for(int i=2; i<args.Length; i++)
                        {
                            if (args[i] == "") message += " ";
                            else message += args[i];
                        }
                        if (this.SendMessageTo(toUserName, message))
                        {
                            Console.WriteLine("Send OK!");
                        }
                        else
                            Console.WriteLine("Send Failed!");
                    }
                }
                else if (string.Compare(args[0], "getu", true) == 0)
                {

                    P2P.WellKnown.C2S.GetUsersMessage getUserMsg = new P2P.WellKnown.C2S.GetUsersMessage(myName);
                    byte[] buffer = FormatterHelper.Serialize(getUserMsg);
                    client.Send(buffer, buffer.Length, hostPoint);
                }
                else
                {
                    Console.WriteLine("Unknown command {0}", cmdstring);
                }

            }

        }
 
        private void DisplayUsers(UserCollection users)
        {
            foreach (User user in users)
            {
                Console.WriteLine("Username: {0}, IP:{1}, Port:{2}", user.UserName, user.NetPoint.Address.ToString(), user.NetPoint.Port);

            }

        }

        private void Run()
        {
            byte[] buffer;
            while (true)
            {
                buffer = client.Receive(ref remotePoint);
                object msgObj = FormatterHelper.Deserialize(buffer);
                Type msgType = msgObj.GetType();
                if (msgType == typeof(P2P.WellKnown.S2C.GetUsersResponseMessage))
                {
                    // 转换消息
                    P2P.WellKnown.S2C.GetUsersResponseMessage usersMsg = (P2P.WellKnown.S2C.GetUsersResponseMessage)msgObj;
                    // 更新用户列表
                    userList.Clear();
                    foreach(User user in usersMsg.UserList)
                    {
                        userList.Add(user);
                    }
                    this.DisplayUsers(userList);

                }
                else if (msgType == typeof(P2P.WellKnown.S2C.SomeOneCallYouMessage))
                {
                    // 转换消息
                    P2P.WellKnown.S2C.SomeOneCallYouMessage purchReqMsg = (P2P.WellKnown.S2C.SomeOneCallYouMessage)msgObj;
                    // 发送打洞消息到远程主机
                    P2P.WellKnown.P2P.TrashMessage trashMsg = new P2P.WellKnown.P2P.TrashMessage();

                    buffer = FormatterHelper.Serialize(trashMsg);
                    client.Send(buffer, buffer.Length, purchReqMsg.RemotePoint);

                }
                else if (msgType == typeof(P2P.WellKnown.P2P.WorkMessage))
                {
                    // 转换消息
                    P2P.WellKnown.P2P.WorkMessage workMsg = (P2P.WellKnown.P2P.WorkMessage)msgObj;
                    Console.WriteLine("Receive a message: {0}", workMsg.Message);
                    // 发送应答消息
                    P2P.WellKnown.P2P.ACKMessage ackMsg = new P2P.WellKnown.P2P.ACKMessage();
                    buffer = FormatterHelper.Serialize(ackMsg);
                    client.Send(buffer, buffer.Length, remotePoint);
                }
                else if (msgType == typeof(P2P.WellKnown.P2P.ACKMessage))
                {
                    this.ReceivedACK = true;
                }
                else if (msgType == typeof(P2P.WellKnown.P2P.TrashMessage))
                {
                    Console.WriteLine("Recieve a trash message");
                }
                Thread.Sleep(100);
            }
        }

        #region IDisposable 成员

        public void Dispose()
        {
            try
            {
                this.listenThread.Abort();
                this.client.Close();
            }
            catch
            {}
        }
 
        #endregion
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Rains卍Soft

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值