2013-07-08 17:58:21 mycn027 阅读数 69
  • C# For Unity系列之进阶篇

    整体介绍:       本进阶篇面向的学员不再是完全的编程“小白”,而是具备一定C#编程经验,需要进一步查漏补缺、或者需要进一步全面完善自己C#编程知识体系的广大Unity学员。相信通过本进阶篇的学习,可以使得Unity初中级开发人员对于编程语言的掌握更进一步;对于开发中大型游戏项目,在编程语言这一层级进一步打下坚实的语言基础。 “中级/进阶篇”讲解特点与内容:          本“中级”与“进阶”篇, 是面向初中级游戏研发人员,以及Unity中高级学习者。为了更加深入的刨析各个语法的本质,我们采用反编译解读IL中间语言的方式,来解构语法重点与难点。 中级篇内容主要讲解: .Net 框架、里氏替换原则(LSP)、类的属性极其本质特性、IS ,AS 关键字、字符串的“驻留性” 原理、深入解析Equals() 原理、枚举类型、自定义集合、深入解析动态集合特性与内部原理、泛型集合、泛型约束、初级委托与事件讲解等。         "进阶篇"是在中级篇的基础之上,进一步研究与讲解关于IO操作、序列化、正则表达式、系统委托(Action、Function、Predicate等)、反射原理与特性、Linq查询表达式、多线程、线程池、任务、Socket套接字编程(Tcp与UDP协议),以及最后使用Unity开发具备实战价值的通讯聊天程序等。   C#“进阶篇”教学详细说明如下: 1: IO操作与序列化       学习文件、目录、二进制文件、文本文件的读取与写入底层原理。学习文件序列化与反序列化技能。 2: 正则表达式       学习正则表达式的强大作用与常用原字符的含义与应用场景。 3: 深入委托与事件       学习Action、Func、Predicate 系统内置委托类型,已经适用场合。学习匿名方法、Lambda表达式。深入解析委托与事件的区别。 4: 反射与特性       学习反射的概念与动态调用的重要应用价值,以及Type、Assembley核心类等,最后讲解“特性”技术。   5: Linq 查询表达式      学习Linq 查询表达式对于“对象集合”(支持IEnumberable 或IEnumberable<T>) 以及SQL数据库、XML文档方面的强大查询功能。       6: 多线程      学习多线程以及线程传参、线程取得返回数值技术,前台与后台线程、线程的同步、线程池、任务等技术。      7: Socket套接字通讯      学习Socket套接字通讯中,Tcp与UPD通讯协议的不同应用场景,以及各自的演示示例,最后用Unity开发一款实用性的聊天通讯工具。 温习提示:             本C# for Unity 使用Virtual Studio2012,以及Unity5.2 进行开发与讲解。(学员使用更高版本,对学习没有任何影响)。      一、热更新系列(技术含量:中高级): A:《lua热更新技术中级篇》 https://edu.csdn.net/course/detail/27087 B:《热更新框架设计之Xlua基础视频课程》 https://edu.csdn.net/course/detail/27110 C:《热更新框架设计之热更流程与热补丁技术》 https://edu.csdn.net/course/detail/27118 D:《热更新框架设计之客户端热更框架(上)》 https://edu.csdn.net/course/detail/27132 E:《热更新框架设计之客户端热更框架(中)》 https://edu.csdn.net/course/detail/27135 F:《热更新框架设计之客户端热更框架(下)》 https://edu.csdn.net/course/detail/27136 二:框架设计系列(技术含量:中级):  A:《游戏UI界面框架设计系列视频课程》 https://edu.csdn.net/course/detail/27142 B:《Unity客户端框架设计PureMVC篇视频课程(上)》 https://edu.csdn.net/course/detail/27172 C:《Unity客户端框架设计PureMVC篇视频课程(下)》 https://edu.csdn.net/course/detail/27173 D:《AssetBundle框架设计_框架篇视频课程》 https://edu.csdn.net/course/detail/27169 三、Unity脚本从入门到精通(技术含量:初级) A:《C# For Unity系列之入门篇》 https://edu.csdn.net/course/detail/4560 B:《C# For Unity系列之基础篇》 https://edu.csdn.net/course/detail/4595 C: 《C# For Unity系列之中级篇》 https://edu.csdn.net/course/detail/24422 D:《C# For Unity系列之进阶篇》 https://edu.csdn.net/course/detail/24465 四、虚拟现实(VR)与增强现实(AR):(技术含量:初级) A:《虚拟现实之汽车仿真模拟系统 》 https://edu.csdn.net/course/detail/26618 五、Unity基础课程系列(技术含量:初级)  A:《台球游戏与FlappyBirds—Unity快速入门系列视频课程(第1部)》   https://edu.csdn.net/course/detail/24643 B:《太空射击与移动端发布技术-Unity快速入门系列视频课程(第2部)》 https://edu.csdn.net/course/detail/24645  C:《Unity ECS(二) 小试牛刀》 https://edu.csdn.net/course/detail/27096 六、Unity ARPG课程(技术含量:初中级): A:《MMOARPG地下守护神_单机版实战视频课程(上部)》 https://edu.csdn.net/course/detail/24965 B:《MMOARPG地下守护神_单机版实战视频课程(中部)》 https://edu.csdn.net/course/detail/24968 C:《MMOARPG地下守护神_单机版实战视频课程(下部)》 https://edu.csdn.net/course/detail/24979

    1320 人正在学习 去看看 刘国柱
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace AsyncUdpSever
{
    public class UdpServer
    {
        private int _udpPort;

        private Socket _udpSocket;

        public UdpServer(int port)
        {
            this._udpPort = port;
            Console.WriteLine("Server Start...@" + port.ToString());
        }

        /// <summary>
        /// ysl 线程处理
        /// </summary>
        public void Start()
        {
            //本地IP与指定端口
            IPEndPoint ipep = new IPEndPoint(IPAddress.Any, _udpPort);

            //服务器Udp Socket
            _udpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            _udpSocket.Bind(ipep);

            //异步接收
            AsyncBeginReceive();
        }

        protected void Stop()
        {
            try
            {
                _udpSocket.Close();
                Thread.Sleep(100);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        private void AsyncBeginReceive()
        {
            while (true)
            {
                try
                {
                    UdpState state = new UdpState();

                    state._receiveDone.Reset();

                    _udpSocket.BeginReceiveFrom(
                        state._data,
                        0,
                        UdpState._bufferSize,
                        SocketFlags.None,
                        ref state._remoteEndPoint,
                        new AsyncCallback(AsyncReceive),
                        state);

                    Thread.Sleep(100);

                    state._receiveDone.WaitOne();
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }
        }

        /// <summary>
        /// ysl 异步接收
        /// </summary>
        /// <param name="ar"></param>
        private void AsyncReceive(IAsyncResult ar)
        {
            if (ar.IsCompleted)
            {
                try
                {
                    Console.WriteLine(1 + "line");
                    UdpState state = (UdpState)ar.AsyncState;
                    //远程发送过来的数据
                    state._dataLength = _udpSocket.EndReceiveFrom(ar, ref state._remoteEndPoint);
                    Console.WriteLine(2 + "line");
                    //完成接收
                    _udpSocket.BeginReceiveFrom(
                        state._data,
                        0,
                        UdpState._bufferSize,
                        SocketFlags.None,
                        ref state._remoteEndPoint,
                        new AsyncCallback(AsyncReceive),
                        state);
                    Console.WriteLine(3 + "line");
                    //获取到的报文
                    string receiveString = Encoding.ASCII.GetString(state._data, 0, state._dataLength);
                    //当前处理完毕
                    Console.WriteLine(4 + "line");
                    Console.WriteLine(receiveString);
                    string str = "OK";
                    byte[] bytes = Encoding.ASCII.GetBytes(str.ToCharArray());
                    _udpSocket.SendTo(bytes, state._remoteEndPoint);

                    state._receiveDone.Set();
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }
        }
    }
}

 

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

namespace AsyncUdpSever
{
    public class UdpState
{
    public const int _bufferSize = 4096;

    public byte[] _data;

    public int _dataLength;

    public EndPoint _remoteEndPoint;

    public UdpState()
    {
        this._data = new byte[_bufferSize];

        _remoteEndPoint = (EndPoint)new IPEndPoint(IPAddress.Any, 0);
    }

    public UdpState(byte[] data, EndPoint remoteEndPoint)
    {
        this._data = data;
        this._dataLength = data.Length;
        this._remoteEndPoint = remoteEndPoint;
    }

    public ManualResetEvent _receiveDone = new ManualResetEvent(false);

}
}

 

2013-07-01 22:41:17 mycn027 阅读数 122
  • C# For Unity系列之进阶篇

    整体介绍:       本进阶篇面向的学员不再是完全的编程“小白”,而是具备一定C#编程经验,需要进一步查漏补缺、或者需要进一步全面完善自己C#编程知识体系的广大Unity学员。相信通过本进阶篇的学习,可以使得Unity初中级开发人员对于编程语言的掌握更进一步;对于开发中大型游戏项目,在编程语言这一层级进一步打下坚实的语言基础。 “中级/进阶篇”讲解特点与内容:          本“中级”与“进阶”篇, 是面向初中级游戏研发人员,以及Unity中高级学习者。为了更加深入的刨析各个语法的本质,我们采用反编译解读IL中间语言的方式,来解构语法重点与难点。 中级篇内容主要讲解: .Net 框架、里氏替换原则(LSP)、类的属性极其本质特性、IS ,AS 关键字、字符串的“驻留性” 原理、深入解析Equals() 原理、枚举类型、自定义集合、深入解析动态集合特性与内部原理、泛型集合、泛型约束、初级委托与事件讲解等。         "进阶篇"是在中级篇的基础之上,进一步研究与讲解关于IO操作、序列化、正则表达式、系统委托(Action、Function、Predicate等)、反射原理与特性、Linq查询表达式、多线程、线程池、任务、Socket套接字编程(Tcp与UDP协议),以及最后使用Unity开发具备实战价值的通讯聊天程序等。   C#“进阶篇”教学详细说明如下: 1: IO操作与序列化       学习文件、目录、二进制文件、文本文件的读取与写入底层原理。学习文件序列化与反序列化技能。 2: 正则表达式       学习正则表达式的强大作用与常用原字符的含义与应用场景。 3: 深入委托与事件       学习Action、Func、Predicate 系统内置委托类型,已经适用场合。学习匿名方法、Lambda表达式。深入解析委托与事件的区别。 4: 反射与特性       学习反射的概念与动态调用的重要应用价值,以及Type、Assembley核心类等,最后讲解“特性”技术。   5: Linq 查询表达式      学习Linq 查询表达式对于“对象集合”(支持IEnumberable 或IEnumberable<T>) 以及SQL数据库、XML文档方面的强大查询功能。       6: 多线程      学习多线程以及线程传参、线程取得返回数值技术,前台与后台线程、线程的同步、线程池、任务等技术。      7: Socket套接字通讯      学习Socket套接字通讯中,Tcp与UPD通讯协议的不同应用场景,以及各自的演示示例,最后用Unity开发一款实用性的聊天通讯工具。 温习提示:             本C# for Unity 使用Virtual Studio2012,以及Unity5.2 进行开发与讲解。(学员使用更高版本,对学习没有任何影响)。      一、热更新系列(技术含量:中高级): A:《lua热更新技术中级篇》 https://edu.csdn.net/course/detail/27087 B:《热更新框架设计之Xlua基础视频课程》 https://edu.csdn.net/course/detail/27110 C:《热更新框架设计之热更流程与热补丁技术》 https://edu.csdn.net/course/detail/27118 D:《热更新框架设计之客户端热更框架(上)》 https://edu.csdn.net/course/detail/27132 E:《热更新框架设计之客户端热更框架(中)》 https://edu.csdn.net/course/detail/27135 F:《热更新框架设计之客户端热更框架(下)》 https://edu.csdn.net/course/detail/27136 二:框架设计系列(技术含量:中级):  A:《游戏UI界面框架设计系列视频课程》 https://edu.csdn.net/course/detail/27142 B:《Unity客户端框架设计PureMVC篇视频课程(上)》 https://edu.csdn.net/course/detail/27172 C:《Unity客户端框架设计PureMVC篇视频课程(下)》 https://edu.csdn.net/course/detail/27173 D:《AssetBundle框架设计_框架篇视频课程》 https://edu.csdn.net/course/detail/27169 三、Unity脚本从入门到精通(技术含量:初级) A:《C# For Unity系列之入门篇》 https://edu.csdn.net/course/detail/4560 B:《C# For Unity系列之基础篇》 https://edu.csdn.net/course/detail/4595 C: 《C# For Unity系列之中级篇》 https://edu.csdn.net/course/detail/24422 D:《C# For Unity系列之进阶篇》 https://edu.csdn.net/course/detail/24465 四、虚拟现实(VR)与增强现实(AR):(技术含量:初级) A:《虚拟现实之汽车仿真模拟系统 》 https://edu.csdn.net/course/detail/26618 五、Unity基础课程系列(技术含量:初级)  A:《台球游戏与FlappyBirds—Unity快速入门系列视频课程(第1部)》   https://edu.csdn.net/course/detail/24643 B:《太空射击与移动端发布技术-Unity快速入门系列视频课程(第2部)》 https://edu.csdn.net/course/detail/24645  C:《Unity ECS(二) 小试牛刀》 https://edu.csdn.net/course/detail/27096 六、Unity ARPG课程(技术含量:初中级): A:《MMOARPG地下守护神_单机版实战视频课程(上部)》 https://edu.csdn.net/course/detail/24965 B:《MMOARPG地下守护神_单机版实战视频课程(中部)》 https://edu.csdn.net/course/detail/24968 C:《MMOARPG地下守护神_单机版实战视频课程(下部)》 https://edu.csdn.net/course/detail/24979

    1320 人正在学习 去看看 刘国柱

服务器端:

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

namespace UDPServer
{
    class Program
    {
        static void Main(string[] args)
        {
            int recv;
            byte[] data = new byte[1024]; 

            //构建TCP 服务器

            //得到本机IP,设置TCP端口号         
            IPEndPoint ipep = new IPEndPoint(IPAddress.Any , 8001);
            Socket newsock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram , ProtocolType.Udp);

            //绑定网络地址
            newsock.Bind(ipep);

            Console.WriteLine("This is a Server, host name is {0}",Dns.GetHostName());

            //等待客户机连接
            Console.WriteLine("Waiting for a client");

            //得到客户机IP
            IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
            EndPoint Remote = (EndPoint)(sender);
            recv = newsock.ReceiveFrom(data, ref Remote);
            Console .WriteLine ("Message received from {0}: ", Remote.ToString ());
            Console .WriteLine (Encoding .ASCII .GetString (data ,0,recv ));

            //客户机连接成功后,发送欢迎信息
            string welcome = "Welcome ! ";

            //字符串与字节数组相互转换
            data  = Encoding .ASCII .GetBytes (welcome );

            //发送信息
            newsock .SendTo (data ,data.Length ,SocketFlags .None ,Remote );
            while (true )
            {
                data =new byte [1024];
                //发送接受信息
                recv =newsock.ReceiveFrom(data ,ref Remote);
                Console .WriteLine (Encoding .ASCII .GetString (data ,0,recv));
                newsock .SendTo (data ,recv ,SocketFlags .None ,Remote );
            }
        }

    }
}

 客户端:

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

namespace UDPClient
{
    class Program
    {
        static void Main(string[] args)
        {
            byte[] data = new byte[1024];
            string input ,stringData;

            //构建TCP 服务器

            Console.WriteLine("This is a Client, host name is {0}", Dns.GetHostName());

            //设置服务IP,设置TCP端口号
            IPEndPoint ipep = new IPEndPoint(IPAddress .Parse ("127.0.0.1") , 8001);

            //定义网络类型,数据连接类型和网络协议UDP
            Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

            string welcome = "Hello! ";
            data = Encoding.ASCII.GetBytes(welcome);
            server.SendTo(data, data.Length, SocketFlags.None, ipep);
            IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
            EndPoint Remote = (EndPoint)sender;

            data = new byte[1024];
            //对于不存在的IP地址,加入此行代码后,可以在指定时间内解除阻塞模式限制
            //server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 100);
            int recv = server.ReceiveFrom(data, ref Remote);
            Console.WriteLine("Message received from {0}: ", Remote.ToString());
            Console.WriteLine(Encoding .ASCII .GetString (data,0,recv));
            while (true)
            {
                input = Console .ReadLine ();
                if (input =="exit")
                    break ;
                server .SendTo (Encoding .ASCII .GetBytes (input ),Remote );
                data = new byte [1024];
                recv = server.ReceiveFrom(data, ref Remote);
                stringData = Encoding.ASCII.GetString(data, 0, recv);
                Console.WriteLine(stringData);
            }
            Console .WriteLine ("Stopping Client.");
            server .Close ();            
        }

    }
}

 

2011-09-07 13:25:02 iteye_13419 阅读数 129
  • C# For Unity系列之进阶篇

    整体介绍:       本进阶篇面向的学员不再是完全的编程“小白”,而是具备一定C#编程经验,需要进一步查漏补缺、或者需要进一步全面完善自己C#编程知识体系的广大Unity学员。相信通过本进阶篇的学习,可以使得Unity初中级开发人员对于编程语言的掌握更进一步;对于开发中大型游戏项目,在编程语言这一层级进一步打下坚实的语言基础。 “中级/进阶篇”讲解特点与内容:          本“中级”与“进阶”篇, 是面向初中级游戏研发人员,以及Unity中高级学习者。为了更加深入的刨析各个语法的本质,我们采用反编译解读IL中间语言的方式,来解构语法重点与难点。 中级篇内容主要讲解: .Net 框架、里氏替换原则(LSP)、类的属性极其本质特性、IS ,AS 关键字、字符串的“驻留性” 原理、深入解析Equals() 原理、枚举类型、自定义集合、深入解析动态集合特性与内部原理、泛型集合、泛型约束、初级委托与事件讲解等。         "进阶篇"是在中级篇的基础之上,进一步研究与讲解关于IO操作、序列化、正则表达式、系统委托(Action、Function、Predicate等)、反射原理与特性、Linq查询表达式、多线程、线程池、任务、Socket套接字编程(Tcp与UDP协议),以及最后使用Unity开发具备实战价值的通讯聊天程序等。   C#“进阶篇”教学详细说明如下: 1: IO操作与序列化       学习文件、目录、二进制文件、文本文件的读取与写入底层原理。学习文件序列化与反序列化技能。 2: 正则表达式       学习正则表达式的强大作用与常用原字符的含义与应用场景。 3: 深入委托与事件       学习Action、Func、Predicate 系统内置委托类型,已经适用场合。学习匿名方法、Lambda表达式。深入解析委托与事件的区别。 4: 反射与特性       学习反射的概念与动态调用的重要应用价值,以及Type、Assembley核心类等,最后讲解“特性”技术。   5: Linq 查询表达式      学习Linq 查询表达式对于“对象集合”(支持IEnumberable 或IEnumberable<T>) 以及SQL数据库、XML文档方面的强大查询功能。       6: 多线程      学习多线程以及线程传参、线程取得返回数值技术,前台与后台线程、线程的同步、线程池、任务等技术。      7: Socket套接字通讯      学习Socket套接字通讯中,Tcp与UPD通讯协议的不同应用场景,以及各自的演示示例,最后用Unity开发一款实用性的聊天通讯工具。 温习提示:             本C# for Unity 使用Virtual Studio2012,以及Unity5.2 进行开发与讲解。(学员使用更高版本,对学习没有任何影响)。      一、热更新系列(技术含量:中高级): A:《lua热更新技术中级篇》 https://edu.csdn.net/course/detail/27087 B:《热更新框架设计之Xlua基础视频课程》 https://edu.csdn.net/course/detail/27110 C:《热更新框架设计之热更流程与热补丁技术》 https://edu.csdn.net/course/detail/27118 D:《热更新框架设计之客户端热更框架(上)》 https://edu.csdn.net/course/detail/27132 E:《热更新框架设计之客户端热更框架(中)》 https://edu.csdn.net/course/detail/27135 F:《热更新框架设计之客户端热更框架(下)》 https://edu.csdn.net/course/detail/27136 二:框架设计系列(技术含量:中级):  A:《游戏UI界面框架设计系列视频课程》 https://edu.csdn.net/course/detail/27142 B:《Unity客户端框架设计PureMVC篇视频课程(上)》 https://edu.csdn.net/course/detail/27172 C:《Unity客户端框架设计PureMVC篇视频课程(下)》 https://edu.csdn.net/course/detail/27173 D:《AssetBundle框架设计_框架篇视频课程》 https://edu.csdn.net/course/detail/27169 三、Unity脚本从入门到精通(技术含量:初级) A:《C# For Unity系列之入门篇》 https://edu.csdn.net/course/detail/4560 B:《C# For Unity系列之基础篇》 https://edu.csdn.net/course/detail/4595 C: 《C# For Unity系列之中级篇》 https://edu.csdn.net/course/detail/24422 D:《C# For Unity系列之进阶篇》 https://edu.csdn.net/course/detail/24465 四、虚拟现实(VR)与增强现实(AR):(技术含量:初级) A:《虚拟现实之汽车仿真模拟系统 》 https://edu.csdn.net/course/detail/26618 五、Unity基础课程系列(技术含量:初级)  A:《台球游戏与FlappyBirds—Unity快速入门系列视频课程(第1部)》   https://edu.csdn.net/course/detail/24643 B:《太空射击与移动端发布技术-Unity快速入门系列视频课程(第2部)》 https://edu.csdn.net/course/detail/24645  C:《Unity ECS(二) 小试牛刀》 https://edu.csdn.net/course/detail/27096 六、Unity ARPG课程(技术含量:初中级): A:《MMOARPG地下守护神_单机版实战视频课程(上部)》 https://edu.csdn.net/course/detail/24965 B:《MMOARPG地下守护神_单机版实战视频课程(中部)》 https://edu.csdn.net/course/detail/24968 C:《MMOARPG地下守护神_单机版实战视频课程(下部)》 https://edu.csdn.net/course/detail/24979

    1320 人正在学习 去看看 刘国柱
class Program
{
//This variable determines the number of
//SocketAsyncEventArg objects put in the pool of objects for receive/send.
//The value of this variable also affects the Semaphore.
//This app uses a Semaphore to ensure that the max # of connections
//value does not get exceeded.
//Max # of connections to a socket can be limited by the Windows Operating System
//also.
public const Int32 maxNumberOfConnections = 3000;

//If this port # will not work for you, it's okay to change it.
public const Int32 port = 4444;

//You would want a buffer size larger than 25 probably, unless you know the
//data will almost always be less than 25. It is just 25 in our test app.
public const Int32 testBufferSize = 25;

//This is the maximum number of asynchronous accept operations that can be
//posted simultaneously. This determines the size of the pool of
//SocketAsyncEventArgs objects that do accept operations. Note that this
//is NOT the same as the maximum # of connections.
public const Int32 maxSimultaneousAcceptOps = 10;

//The size of the queue of incoming connections for the listen socket.
public const Int32 backlog = 100;

//For the BufferManager
public const Int32 opsToPreAlloc = 2; // 1 for receive, 1 for send

//allows excess SAEA objects in pool.
public const Int32 excessSaeaObjectsInPool = 1;

//This number must be the same as the value on the client.
//Tells what size the message prefix will be. Don't change this unless
//you change the code, because 4 is the length of 32 bit integer, which
//is what we are using as prefix.
public const Int32 receivePrefixLength = 4;
public const Int32 sendPrefixLength = 4;

public static Int32 mainTransMissionId = 10000;
public static Int32 startingTid; //
public static Int32 mainSessionId = 1000000000;

public static List listOfDataHolders;

// To keep a record of maximum number of simultaneous connections
// that occur while the server is running. This can be limited by operating
// system and hardware. It will not be higher than the value that you set
// for maxNumberOfConnections.
public static Int32 maxSimultaneousClientsThatWereConnected = 0;

static void Main(String[] args)
{
try
{
// Get endpoint for the listener.
IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, port);

WriteInfoToConsole(localEndPoint);

//This object holds a lot of settings that we pass from Main method
//to the SocketListener. In a real app, you might want to read
//these settings from a database or windows registry settings that
//you would create.
SocketListenerSettings theSocketListenerSettings =
new SocketListenerSettings(maxNumberOfConnections,
excessSaeaObjectsInPool, backlog, maxSimultaneousAcceptOps,
receivePrefixLength, testBufferSize, sendPrefixLength, opsToPreAlloc,
localEndPoint);

//instantiate the SocketListener.
SocketListener socketListener = new SocketListener(theSocketListenerSettings);
}
catch (Exception ex)
{
Console.WriteLine("Error: " + ex.Message);
}
}
class SocketListener
{
//Buffers for sockets are unmanaged by .NET.
//So memory used for buffers gets "pinned", which makes the
//.NET garbage collector work around it, fragmenting the memory.
//Circumvent this problem by putting all buffers together
//in one block in memory. Then we will assign a part of that space
//to each SocketAsyncEventArgs object, and
//reuse that buffer space each time we reuse the SocketAsyncEventArgs object.
//Create a large reusable set of buffers for all socket operations.
BufferManager theBufferManager;

// the socket used to listen for incoming connection requests
Socket listenSocket;

//A Semaphore has two parameters, the initial number of available slots
// and the maximum number of slots. We'll make them the same.
//This Semaphore is used to keep from going over max connection #.
//(It is not about controlling threading really here.)
Semaphore theMaxConnectionsEnforcer;

//an object that we pass in and which has all the settings the listener needs
SocketListenerSettings socketListenerSettings;

PrefixHandler prefixHandler;
MessageHandler messageHandler;

// pool of reusable SocketAsyncEventArgs objects for accept operations
SocketAsyncEventArgsPool poolOfAcceptEventArgs;
// pool of reusable SocketAsyncEventArgs objects for
//receive and send socket operations
SocketAsyncEventArgsPool poolOfRecSendEventArgs;

//_______________________________________________________________________________
// Constructor.
public SocketListener(SocketListenerSettings theSocketListenerSettings)
{
this.socketListenerSettings = theSocketListenerSettings;
this.prefixHandler = new PrefixHandler();
this.messageHandler = new MessageHandler();

//Allocate memory for buffers. We are using a separate buffer space for
//receive and send, instead of sharing the buffer space, like the Microsoft
//example does.
this.theBufferManager = new BufferManager(this.socketListenerSettings.BufferSize
* this.socketListenerSettings.NumberOfSaeaForRecSend
* this.socketListenerSettings.OpsToPreAllocate,
this.socketListenerSettings.BufferSize
* this.socketListenerSettings.OpsToPreAllocate);

this.poolOfRecSendEventArgs = new
SocketAsyncEventArgsPool(this.socketListenerSettings.NumberOfSaeaForRecSend);

this.poolOfAcceptEventArgs = new
SocketAsyncEventArgsPool(this.socketListenerSettings.MaxAcceptOps);

// Create connections count enforcer
this.theMaxConnectionsEnforcer = new
Semaphore(this.socketListenerSettings.MaxConnections,
this.socketListenerSettings.MaxConnections);

//Microsoft's example called these from Main method, which you
//can easily do if you wish.
Init();
StartListen();
}

//____________________________________________________________________________
// initializes the server by preallocating reusable buffers and
// context objects (SocketAsyncEventArgs objects).
//It is NOT mandatory that you preallocate them or reuse them. But, but it is
//done this way to illustrate how the API can
// easily be used to create reusable objects to increase server performance.

internal void Init()
{
// Allocate one large byte buffer block, which all I/O operations will
//use a piece of. This guards against memory fragmentation.
this.theBufferManager.InitBuffer();

// preallocate pool of SocketAsyncEventArgs objects for accept operations
for (Int32 i = 0; i < this.socketListenerSettings.MaxAcceptOps; i++)
{
// add SocketAsyncEventArg to the pool
this.poolOfAcceptEventArgs.Push(
CreateNewSaeaForAccept(poolOfAcceptEventArgs));
}

//The pool that we built ABOVE is for SocketAsyncEventArgs objects that do
// accept operations.
//Now we will build a separate pool for SAEAs objects
//that do receive/send operations. One reason to separate them is that accept
//operations do NOT need a buffer, but receive/send operations do.
//ReceiveAsync and SendAsync require
//a parameter for buffer size in SocketAsyncEventArgs.Buffer.
// So, create pool of SAEA objects for receive/send operations.
SocketAsyncEventArgs eventArgObjectForPool;

Int32 tokenId;

for (Int32 i = 0; i < this.socketListenerSettings.NumberOfSaeaForRecSend; i++)
{
//Allocate the SocketAsyncEventArgs object for this loop,
//to go in its place in the stack which will be the pool
//for receive/send operation context objects.
eventArgObjectForPool = new SocketAsyncEventArgs();

// assign a byte buffer from the buffer block to
//this particular SocketAsyncEventArg object
this.theBufferManager.SetBuffer(eventArgObjectForPool);

tokenId = poolOfRecSendEventArgs.AssignTokenId() + 1000000;

//Attach the SocketAsyncEventArgs object
//to its event handler. Since this SocketAsyncEventArgs object is
//used for both receive and send operations, whenever either of those
//completes, the IO_Completed method will be called.
eventArgObjectForPool.Completed += new
EventHandler(IO_Completed);

//We can store data in the UserToken property of SAEA object.
DataHoldingUserToken theTempReceiveSendUserToken = new
DataHoldingUserToken(eventArgObjectForPool, eventArgObjectForPool.Offset,
eventArgObjectForPool.Offset + this.socketListenerSettings.BufferSize,
this.socketListenerSettings.ReceivePrefixLength,
this.socketListenerSettings.SendPrefixLength, tokenId);

//We'll have an object that we call DataHolder, that we can remove from
//the UserToken when we are finished with it. So, we can hang on to the
//DataHolder, pass it to an app, serialize it, or whatever.
theTempReceiveSendUserToken.CreateNewDataHolder();

eventArgObjectForPool.UserToken = theTempReceiveSendUserToken;

// add this SocketAsyncEventArg object to the pool.
this.poolOfRecSendEventArgs.Push(eventArgObjectForPool);
}

//____________________________________________________________________________
// This method is called when we need to create a new SAEA object to do
//accept operations. The reason to put it in a separate method is so that
//we can easily add more objects to the pool if we need to.
//You can do that if you do NOT use a buffer in the SAEA object that does
//the accept operations.
internal SocketAsyncEventArgs CreateNewSaeaForAccept(SocketAsyncEventArgsPool pool)
{
//Allocate the SocketAsyncEventArgs object.
SocketAsyncEventArgs acceptEventArg = new SocketAsyncEventArgs();

//SocketAsyncEventArgs.Completed is an event, (the only event,)
//declared in the SocketAsyncEventArgs class.
//See http://msdn.microsoft.com/en-us/library/
// system.net.sockets.socketasynceventargs.completed.aspx.
//An event handler should be attached to the event within
//a SocketAsyncEventArgs instance when an asynchronous socket
//operation is initiated, otherwise the application will not be able
//to determine when the operation completes.
//Attach the event handler, which causes the calling of the
//AcceptEventArg_Completed object when the accept op completes.
acceptEventArg.Completed +=
new EventHandler<SocketAsyncEventArgs>(AcceptEventArg_Completed);

AcceptOpUserToken theAcceptOpToken = new
AcceptOpUserToken(pool.AssignTokenId() + 10000);

acceptEventArg.UserToken = theAcceptOpToken;

return acceptEventArg;

// accept operations do NOT need a buffer.
//You can see that is true by looking at the
//methods in the .NET Socket class on the Microsoft website. AcceptAsync does
//not require a parameter for buffer size.
}

//____________________________________________________________________________
// This method starts the socket server such that it is listening for
// incoming connection requests.
internal void StartListen()
{
// create the socket which listens for incoming connections
listenSocket = new
Socket(this.socketListenerSettings.LocalEndPoint.AddressFamily,
SocketType.Stream, ProtocolType.Tcp);

//bind it to the port
listenSocket.Bind(this.socketListenerSettings.LocalEndPoint);

// Start the listener with a backlog of however many connections.
//"backlog" means pending connections.
//The backlog number is the number of clients that can wait for a
//SocketAsyncEventArg object that will do an accept operation.
//The listening socket keeps the backlog as a queue. The backlog allows
//for a certain # of excess clients waiting to be connected.
//If the backlog is maxed out, then the client will receive an error when
//trying to connect.
//max # for backlog can be limited by the operating system.
listenSocket.Listen(this.socketListenerSettings.Backlog);

//Server is listening now****

// Calls the method which will post accepts on the listening socket.
//This call just occurs one time from this StartListen method.
//After that the StartAccept method will be called in a loop.
StartAccept();
}

//____________________________________________________________________________
// Begins an operation to accept a connection request from the client
internal void StartAccept()
{
//Get a SocketAsyncEventArgs object to accept the connection.
SocketAsyncEventArgs acceptEventArg;
//Get it from the pool if there is more than one in the pool.
//We could use zero as bottom, but one is a little safer.
if (this.poolOfAcceptEventArgs.Count > 1)
{
try
{
acceptEventArg = this.poolOfAcceptEventArgs.Pop();
}
//or make a new one.
catch
{
acceptEventArg = CreateNewSaeaForAccept(poolOfAcceptEventArgs);
}
}
//or make a new one.
else
{
acceptEventArg = CreateNewSaeaForAccept(poolOfAcceptEventArgs);
}

// Semaphore class is used to control access to a resource or pool of
// resources. Enter the semaphore by calling the WaitOne method, which is
// inherited from the WaitHandle class, and release the semaphore
// by calling the Release method. This is a mechanism to prevent exceeding
// the max # of connections we specified. We'll do this before
// doing AcceptAsync. If maxConnections value has been reached,
// then the thread will pause here until the Semaphore gets released,
// which happens in the CloseClientSocket method.
this.theMaxConnectionsEnforcer.WaitOne();

// Socket.AcceptAsync begins asynchronous operation to accept the connection.
// Note the listening socket will pass info to the SocketAsyncEventArgs
// object that has the Socket that does the accept operation.
// If you do not create a Socket object and put it in the SAEA object
// before calling AcceptAsync and use the AcceptSocket property to get it,
// then a new Socket object will be created for you by .NET.
bool willRaiseEvent = listenSocket.AcceptAsync(acceptEventArg);

// Socket.AcceptAsync returns true if the I/O operation is pending, i.e. is
// working asynchronously. The
// SocketAsyncEventArgs.Completed event on the acceptEventArg parameter
// will be raised upon completion of accept op.
// AcceptAsync will call the AcceptEventArg_Completed
// method when it completes, because when we created this SocketAsyncEventArgs
// object before putting it in the pool, we set the event handler to do it.
// AcceptAsync returns false if the I/O operation completed synchronously.
// The SocketAsyncEventArgs.Completed event on the acceptEventArg parameter
// will NOT be raised when AcceptAsync returns false.
if (!willRaiseEvent)
{
// The code in this if (!willRaiseEvent) statement only runs
// when the operation was completed synchronously. It is needed because
// when Socket.AcceptAsync returns false,
// it does NOT raise the SocketAsyncEventArgs.Completed event.
// And we need to call ProcessAccept and pass it the SAEA object.
// This is only when a new connection is being accepted.
// Probably only relevant in the case of a socket error.
ProcessAccept(acceptEventArg);
}
}

//____________________________________________________________________________
// This method is the callback method associated with Socket.AcceptAsync
// operations and is invoked when an async accept operation completes.
//This is only when a new connection is being accepted.
//Notice that Socket.AcceptAsync is returning a value of true, and
//raising the Completed event when the AcceptAsync method completes.
private void AcceptEventArg_Completed(object sender, SocketAsyncEventArgs e)
{
//Any code that you put in this method will NOT be called if
//the operation completes synchronously, which will probably happen when
//there is some kind of socket error. It might be better to put the code
//in the ProcessAccept method.
ProcessAccept(e);
}

//____________________________________________________________________________
//The e parameter passed from the AcceptEventArg_Completed method
//represents the SocketAsyncEventArgs object that did
//the accept operation. in this method we'll do the handoff from it to the
//SocketAsyncEventArgs object that will do receive/send.
private void ProcessAccept(SocketAsyncEventArgs acceptEventArgs)
{
// This is when there was an error with the accept op. That should NOT
// be happening often. It could indicate that there is a problem with
// that socket. If there is a problem, then we would have an infinite
// loop here, if we tried to reuse that same socket.
if (acceptEventArgs.SocketError != SocketError.Success)
{
// Loop back to post another accept op. Notice that we are NOT
// passing the SAEA object here.
LoopToStartAccept();

AcceptOpUserToken theAcceptOpToken =
(AcceptOpUserToken)acceptEventArgs.UserToken;

//Let's destroy this socket, since it could be bad.
HandleBadAccept(acceptEventArgs);

//Jump out of the method.
return;
}

//Now that the accept operation completed, we can start another
//accept operation, which will do the same. Notice that we are NOT
//passing the SAEA object here.
LoopToStartAccept();

// Get a SocketAsyncEventArgs object from the pool of receive/send op
//SocketAsyncEventArgs objects
SocketAsyncEventArgs receiveSendEventArgs = this.poolOfRecSendEventArgs.Pop();

//Create sessionId in UserToken.
((DataHoldingUserToken)receiveSendEventArgs.UserToken).CreateSessionId();

//A new socket was created by the AcceptAsync method. The
//SocketAsyncEventArgs object which did the accept operation has that
//socket info in its AcceptSocket property. Now we will give
//a reference for that socket to the SocketAsyncEventArgs
//object which will do receive/send.
receiveSendEventArgs.AcceptSocket = acceptEventArgs.AcceptSocket;

//We have handed off the connection info from the
//accepting socket to the receiving socket. So, now we can
//put the SocketAsyncEventArgs object that did the accept operation
//back in the pool for them. But first we will clear
//the socket info from that object, so it will be
//ready for a new socket when it comes out of the pool.
acceptEventArgs.AcceptSocket = null;
this.poolOfAcceptEventArgs.Push(acceptEventArgs);

StartReceive(receiveSendEventArgs);
}

//____________________________________________________________________________
//LoopToStartAccept method just sends us back to the beginning of the
//StartAccept method, to start the next accept operation on the next
//connection request that this listening socket will pass of to an
//accepting socket. We do NOT actually need this method. You could
//just call StartAccept() in ProcessAccept() where we called LoopToStartAccept().
//This method is just here to help you visualize the program flow.
private void LoopToStartAccept()
{
StartAccept();
}

//____________________________________________________________________________
// Set the receive buffer and post a receive op.
private void StartReceive(SocketAsyncEventArgs receiveSendEventArgs)
{
//Set the buffer for the receive operation.
receiveSendEventArgs.SetBuffer(receiveSendToken.bufferOffsetReceive,
this.socketListenerSettings.BufferSize);

// Post async receive operation on the socket.
bool willRaiseEvent =
receiveSendEventArgs.AcceptSocket.ReceiveAsync(receiveSendEventArgs);

//Socket.ReceiveAsync returns true if the I/O operation is pending. The
//SocketAsyncEventArgs.Completed event on the e parameter will be raised
//upon completion of the operation. So, true will cause the IO_Completed
//method to be called when the receive operation completes.
//That's because of the event handler we created when building
//the pool of SocketAsyncEventArgs objects that perform receive/send.
//It was the line that said
//eventArgObjectForPool.Completed +=
// new EventHandler<SocketAsyncEventArgs>(IO_Completed);

//Socket.ReceiveAsync returns false if I/O operation completed synchronously.
//In that case, the SocketAsyncEventArgs.Completed event on the e parameter
//will not be raised and the e object passed as a parameter may be
//examined immediately after the method call
//returns to retrieve the result of the operation.
// It may be false in the case of a socket error.
if (!willRaiseEvent)
{
//If the op completed synchronously, we need to call ProcessReceive
//method directly. This will probably be used rarely, as you will
//see in testing.
ProcessReceive(receiveSendEventArgs);
}
}

//____________________________________________________________________________
// This method is called whenever a receive or send operation completes.
// Here "e" represents the SocketAsyncEventArgs object associated
//with the completed receive or send operation
void IO_Completed(object sender, SocketAsyncEventArgs e)
{
//Any code that you put in this method will NOT be called if
//the operation completes synchronously, which will probably happen when
//there is some kind of socket error.

// determine which type of operation just
// completed and call the associated handler
switch (e.LastOperation)
{
case SocketAsyncOperation.Receive:
ProcessReceive(e);
break;

case SocketAsyncOperation.Send:
ProcessSend(e);
break;

default:
//This exception will occur if you code the Completed event of some
//operation to come to this method, by mistake.
throw new ArgumentException("The last operation completed on
the socket was not a receive or send");
}
}

//____________________________________________________________________________
// This method is invoked by the IO_Completed method
// when an asynchronous receive operation completes.
// If the remote host closed the connection, then the socket is closed.
// Otherwise, we process the received data. And if a complete message was
// received, then we do some additional processing, to
// respond to the client.
private void ProcessReceive(SocketAsyncEventArgs receiveSendEventArgs)
{
DataHoldingUserToken receiveSendToken =
(DataHoldingUserToken)receiveSendEventArgs.UserToken;

// If there was a socket error, close the connection. This is NOT a normal
// situation, if you get an error here.
// In the Microsoft example code they had this error situation handled
// at the end of ProcessReceive. Putting it here improves readability
// by reducing nesting some.
if (receiveSendEventArgs.SocketError != SocketError.Success)
{
receiveSendToken.Reset();
CloseClientSocket(receiveSendEventArgs);

//Jump out of the ProcessReceive method.
return;
}

// If no data was received, close the connection. This is a NORMAL
// situation that shows when the client has finished sending data.
if (receiveSendEventArgs.BytesTransferred == 0)
{
receiveSendToken.Reset();
CloseClientSocket(receiveSendEventArgs);
return;
}

//The BytesTransferred property tells us how many bytes
//we need to process.
Int32 remainingBytesToProcess = receiveSendEventArgs.BytesTransferred;

//If we have not got all of the prefix already,
//then we need to work on it here.
if (receiveSendToken.receivedPrefixBytesDoneCount <
this.socketListenerSettings.ReceivePrefixLength)
{
remainingBytesToProcess = prefixHandler.HandlePrefix(receiveSendEventArgs,
receiveSendToken, remainingBytesToProcess);

if (remainingBytesToProcess == 0)
{
// We need to do another receive op, since we do not have
// the message yet, but remainingBytesToProcess == 0.
StartReceive(receiveSendEventArgs);
//Jump out of the method.
return;
}
}

// If we have processed the prefix, we can work on the message now.
// We'll arrive here when we have received enough bytes to read
// the first byte after the prefix.
bool incomingTcpMessageIsReady = messageHandler
.HandleMessage(receiveSendEventArgs,
receiveSendToken, remainingBytesToProcess);

if (incomingTcpMessageIsReady == true)
{
// Pass the DataHolder object to the Mediator here. The data in
// this DataHolder can be used for all kinds of things that an
// intelligent and creative person like you might think of.
receiveSendToken.theMediator.HandleData(receiveSendToken.theDataHolder);

// Create a new DataHolder for next message.
receiveSendToken.CreateNewDataHolder();

//Reset the variables in the UserToken, to be ready for the
//next message that will be received on the socket in this
//SAEA object.
receiveSendToken.Reset();

receiveSendToken.theMediator.PrepareOutgoingData();
StartSend(receiveSendToken.theMediator.GiveBack());
}
else
{
// Since we have NOT gotten enough bytes for the whole message,
// we need to do another receive op. Reset some variables first.

// All of the data that we receive in the next receive op will be
// message. None of it will be prefix. So, we need to move the
// receiveSendToken.receiveMessageOffset to the beginning of the
// receive buffer space for this SAEA.
receiveSendToken.receiveMessageOffset = receiveSendToken.bufferOffsetReceive;

// Do NOT reset receiveSendToken.receivedPrefixBytesDoneCount here.
// Just reset recPrefixBytesDoneThisOp.
receiveSendToken.recPrefixBytesDoneThisOp = 0;

// Since we have not gotten enough bytes for the whole message,
// we need to do another receive op.
StartReceive(receiveSendEventArgs);
}
}

//____________________________________________________________________________
//Post a send op.
private void StartSend(SocketAsyncEventArgs receiveSendEventArgs)
{
DataHoldingUserToken receiveSendToken =
(DataHoldingUserToken)receiveSendEventArgs.UserToken;

//Set the buffer. You can see on Microsoft's page at
//http://msdn.microsoft.com/en-us/library/
// system.net.sockets.socketasynceventargs.setbuffer.aspx
//that there are two overloads. One of the overloads has 3 parameters.
//When setting the buffer, you need 3 parameters the first time you set it,
//which we did in the Init method. The first of the three parameters
//tells what byte array to use as the buffer. After we tell what byte array
//to use we do not need to use the overload with 3 parameters any more.
//(That is the whole reason for using the buffer block. You keep the same
//byte array as buffer always, and keep it all in one block.)
//Now we use the overload with two parameters. We tell
// (1) the offset and
// (2) the number of bytes to use, starting at the offset.

//The number of bytes to send depends on whether the message is larger than
//the buffer or not. If it is larger than the buffer, then we will have
//to post more than one send operation. If it is less than or equal to the
//size of the send buffer, then we can accomplish it in one send op.
if (receiveSendToken.sendBytesRemainingCount
<= this.socketListenerSettings.BufferSize)
{
receiveSendEventArgs.SetBuffer(receiveSendToken.bufferOffsetSend,
receiveSendToken.sendBytesRemainingCount);
//Copy the bytes to the buffer associated with this SAEA object.
Buffer.BlockCopy(receiveSendToken.dataToSend,
receiveSendToken.bytesSentAlreadyCount,
receiveSendEventArgs.Buffer, receiveSendToken.bufferOffsetSend,
receiveSendToken.sendBytesRemainingCount);
}
else
{
//We cannot try to set the buffer any larger than its size.
//So since receiveSendToken.sendBytesRemainingCount > BufferSize, we just
//set it to the maximum size, to send the most data possible.
receiveSendEventArgs.SetBuffer(receiveSendToken.bufferOffsetSend,
this.socketListenerSettings.BufferSize);
//Copy the bytes to the buffer associated with this SAEA object.
Buffer.BlockCopy(receiveSendToken.dataToSend,
receiveSendToken.bytesSentAlreadyCount,
receiveSendEventArgs.Buffer, receiveSendToken.bufferOffsetSend,
this.socketListenerSettings.BufferSize);

//We'll change the value of sendUserToken.sendBytesRemainingCount
//in the ProcessSend method.
}

//post asynchronous send operation
bool willRaiseEvent =
receiveSendEventArgs.AcceptSocket.SendAsync(receiveSendEventArgs);

if (!willRaiseEvent)
{
ProcessSend(receiveSendEventArgs);
}
}

//____________________________________________________________________________
// This method is called by I/O Completed() when an asynchronous send completes.
// If all of the data has been sent, then this method calls StartReceive
//to start another receive op on the socket to read any additional
// data sent from the client. If all of the data has NOT been sent, then it
//calls StartSend to send more data.
private void ProcessSend(SocketAsyncEventArgs receiveSendEventArgs)
{
DataHoldingUserToken receiveSendToken =
(DataHoldingUserToken)receiveSendEventArgs.UserToken;

receiveSendToken.sendBytesRemainingCount =
receiveSendToken.sendBytesRemainingCount
- receiveSendEventArgs.BytesTransferred;
receiveSendToken.bytesSentAlreadyCount +=
receiveSendEventArgs.BytesTransferred;

if (receiveSendEventArgs.SocketError == SocketError.Success)
{
if (receiveSendToken.sendBytesRemainingCount == 0)
{
StartReceive(receiveSendEventArgs);
}
else
{
//If some of the bytes in the message have NOT been sent,
//then we will need to post another send operation.
//So let's loop back to StartSend().
StartSend(receiveSendEventArgs);
}
}
else
{
//If we are in this else-statement, there was a socket error.
//In this example we'll just close the socket if there was a socket error
//when receiving data from the client.
receiveSendToken.Reset();
CloseClientSocket(receiveSendEventArgs);
}
}

//_______________________________________________________________________
// Does the normal destroying of sockets after
// we finish receiving and sending on a connection.
private void CloseClientSocket(SocketAsyncEventArgs e)
{
var receiveSendToken = (e.UserToken as DataHoldingUserToken);

// do a shutdown before you close the socket
try
{
e.AcceptSocket.Shutdown(SocketShutdown.Both);
}
// throws if socket was already closed
catch (Exception)
{
}

//This method closes the socket and releases all resources, both
//managed and unmanaged. It internally calls Dispose.
e.AcceptSocket.Close();

//Make sure the new DataHolder has been created for the next connection.
//If it has, then dataMessageReceived should be null.
if (receiveSendToken.theDataHolder.dataMessageReceived != null)
{
receiveSendToken.CreateNewDataHolder();
}

// Put the SocketAsyncEventArg back into the pool,
// to be used by another client. This
this.poolOfRecSendEventArgs.Push(e);

// decrement the counter keeping track of the total number of clients
//connected to the server, for testing
Interlocked.Decrement(ref this.numberOfAcceptedSockets);

//Release Semaphore so that its connection counter will be decremented.
//This must be done AFTER putting the SocketAsyncEventArg back into the pool,
//or you can run into problems.
this.theMaxConnectionsEnforcer.Release();
}

//____________________________________________________________________________
private void HandleBadAccept(SocketAsyncEventArgs acceptEventArgs)
{
var acceptOpToken = (acceptEventArgs.UserToken as AcceptOpUserToken);

//This method closes the socket and releases all resources, both
//managed and unmanaged. It internally calls Dispose.
acceptEventArgs.AcceptSocket.Close();

//Put the SAEA back in the pool.
poolOfAcceptEventArgs.Push(acceptEventArgs);
}
}

class PrefixHandler
{
public Int32 HandlePrefix(SocketAsyncEventArgs e,
DataHoldingUserToken receiveSendToken,
Int32 remainingBytesToProcess)
{
//receivedPrefixBytesDoneCount tells us how many prefix bytes were
//processed during previous receive ops which contained data for
//this message. Usually there will NOT have been any previous
//receive ops here. So in that case,
//receiveSendToken.receivedPrefixBytesDoneCount would equal 0.
//Create a byte array to put the new prefix in, if we have not
//already done it in a previous loop.
if (receiveSendToken.receivedPrefixBytesDoneCount == 0)
{
receiveSendToken.byteArrayForPrefix = new
Byte[receiveSendToken.receivePrefixLength];
}

//If this next if-statement is true, then we have received at
//least enough bytes to have the prefix. So we can determine the
//length of the message that we are working on.
if (remainingBytesToProcess >= receiveSendToken.receivePrefixLength
- receiveSendToken.receivedPrefixBytesDoneCount)
{
//Now copy that many bytes to byteArrayForPrefix.
//We can use the variable receiveMessageOffset as our main
//index to show which index to get data from in the TCP
//buffer.
Buffer.BlockCopy(e.Buffer, receiveSendToken.receiveMessageOffset
- receiveSendToken.receivePrefixLength
+ receiveSendToken.receivedPrefixBytesDoneCount,
receiveSendToken.byteArrayForPrefix,
receiveSendToken.receivedPrefixBytesDoneCount,
receiveSendToken.receivePrefixLength
- receiveSendToken.receivedPrefixBytesDoneCount);

remainingBytesToProcess = remainingBytesToProcess
- receiveSendToken.receivePrefixLength
+ receiveSendToken.receivedPrefixBytesDoneCount;

receiveSendToken.recPrefixBytesDoneThisOp =
receiveSendToken.receivePrefixLength
- receiveSendToken.receivedPrefixBytesDoneCount;

receiveSendToken.receivedPrefixBytesDoneCount =
receiveSendToken.receivePrefixLength;

receiveSendToken.lengthOfCurrentIncomingMessage =
BitConverter.ToInt32(receiveSendToken.byteArrayForPrefix, 0);

return remainingBytesToProcess;
}

//This next else-statement deals with the situation
//where we have some bytes
//of this prefix in this receive operation, but not all.
else
{
//Write the bytes to the array where we are putting the
//prefix data, to save for the next loop.
Buffer.BlockCopy(e.Buffer, receiveSendToken.receiveMessageOffset
- receiveSendToken.receivePrefixLength
+ receiveSendToken.receivedPrefixBytesDoneCount,
receiveSendToken.byteArrayForPrefix,
receiveSendToken.receivedPrefixBytesDoneCount,
remainingBytesToProcess);

receiveSendToken.recPrefixBytesDoneThisOp = remainingBytesToProcess;
receiveSendToken.receivedPrefixBytesDoneCount += remainingBytesToProcess;
remainingBytesToProcess = 0;
}

// This section is needed when we have received
// an amount of data exactly equal to the amount needed for the prefix,
// but no more. And also needed with the situation where we have received
// less than the amount of data needed for prefix.
if (remainingBytesToProcess == 0)
{
receiveSendToken.receiveMessageOffset =
receiveSendToken.receiveMessageOffset -
receiveSendToken.recPrefixBytesDoneThisOp;
receiveSendToken.recPrefixBytesDoneThisOp = 0;
}
return remainingBytesToProcess;
}
}

class MessageHandler
{
public bool HandleMessage(SocketAsyncEventArgs receiveSendEventArgs,
DataHoldingUserToken receiveSendToken,
Int32 remainingBytesToProcess)
{
bool incomingTcpMessageIsReady = false;

//Create the array where we'll store the complete message,
//if it has not been created on a previous receive op.
if (receiveSendToken.receivedMessageBytesDoneCount == 0)
{
receiveSendToken.theDataHolder.dataMessageReceived =
new Byte[receiveSendToken.lengthOfCurrentIncomingMessage];
}

// Remember there is a receiveSendToken.receivedPrefixBytesDoneCount
// variable, which allowed us to handle the prefix even when it
// requires multiple receive ops. In the same way, we have a
// receiveSendToken.receivedMessageBytesDoneCount variable, which
// helps us handle message data, whether it requires one receive
// operation or many.
if (remainingBytesToProcess + receiveSendToken.receivedMessageBytesDoneCount
== receiveSendToken.lengthOfCurrentIncomingMessage)
{
// If we are inside this if-statement, then we got
// the end of the message. In other words,
// the total number of bytes we received for this message matched the
// message length value that we got from the prefix.

// Write/append the bytes received to the byte array in the
// DataHolder object that we are using to store our data.
Buffer.BlockCopy(receiveSendEventArgs.Buffer,
receiveSendToken.receiveMessageOffset,
receiveSendToken.theDataHolder.dataMessageReceived,
receiveSendToken.receivedMessageBytesDoneCount,
remainingBytesToProcess);

incomingTcpMessageIsReady = true;
}
else
{
// If we are inside this else-statement, then that means that we
// need another receive op. We still haven't got the whole message,
// even though we have examined all the data that was received.
// Not a problem. In SocketListener.ProcessReceive we will just call
// StartReceive to do another receive op to receive more data.

Buffer.BlockCopy(receiveSendEventArgs.Buffer,
receiveSendToken.receiveMessageOffset,
receiveSendToken.theDataHolder.dataMessageReceived,
receiveSendToken.receivedMessageBytesDoneCount,
remainingBytesToProcess);

receiveSendToken.receiveMessageOffset =
receiveSendToken.receiveMessageOffset -
receiveSendToken.recPrefixBytesDoneThisOp;

receiveSendToken.receivedMessageBytesDoneCount += remainingBytesToProcess;
}
return incomingTcpMessageIsReady;
}
}

class BufferManager
{
// This class creates a single large buffer which can be divided up
// and assigned to SocketAsyncEventArgs objects for use with each
// socket I/O operation.
// This enables buffers to be easily reused and guards against
// fragmenting heap memory.
//
//This buffer is a byte array which the Windows TCP buffer can copy its data to.

// the total number of bytes controlled by the buffer pool
Int32 totalBytesInBufferBlock;

// Byte array maintained by the Buffer Manager.
byte[] bufferBlock;
Stack<int> freeIndexPool;
Int32 currentIndex;
Int32 bufferBytesAllocatedForEachSaea;

public BufferManager(Int32 totalBytes, Int32 totalBufferBytesInEachSaeaObject)
{
totalBytesInBufferBlock = totalBytes;
this.currentIndex = 0;
this.bufferBytesAllocatedForEachSaea = totalBufferBytesInEachSaeaObject;
this.freeIndexPool = new Stack<int>();
}

// Allocates buffer space used by the buffer pool
internal void InitBuffer()
{
// Create one large buffer block.
this.bufferBlock = new byte[totalBytesInBufferBlock];
}

// Divide that one large buffer block out to each SocketAsyncEventArg object.
// Assign a buffer space from the buffer block to the
// specified SocketAsyncEventArgs object.
//
// returns true if the buffer was successfully set, else false
internal bool SetBuffer(SocketAsyncEventArgs args)
{
if (this.freeIndexPool.Count > 0)
{
//This if-statement is only true if you have called the FreeBuffer
//method previously, which would put an offset for a buffer space
//back into this stack.
args.SetBuffer(this.bufferBlock, this.freeIndexPool.Pop(),
this.bufferBytesAllocatedForEachSaea);
}
else
{
//Inside this else-statement is the code that is used to set the
//buffer for each SAEA object when the pool of SAEA objects is built
//in the Init method.
if ((totalBytesInBufferBlock - this.bufferBytesAllocatedForEachSaea) <
this.currentIndex)
{
return false;
}
args.SetBuffer(this.bufferBlock, this.currentIndex,
this.bufferBytesAllocatedForEachSaea);
this.currentIndex += this.bufferBytesAllocatedForEachSaea;
}
return true;
}

// Removes the buffer from a SocketAsyncEventArg object. This frees the
// buffer back to the buffer pool. Try NOT to use the FreeBuffer method,
// unless you need to destroy the SAEA object, or maybe in the case
// of some exception handling. Instead, on the server
// keep the same buffer space assigned to one SAEA object for the duration of
// this app's running.
internal void FreeBuffer(SocketAsyncEventArgs args)
{
this.freeIndexPool.Push(args.Offset);
args.SetBuffer(null, 0, 0);
}
}

class DataHoldingUserToken
{
internal Mediator theMediator;
internal DataHolder theDataHolder;
internal readonly Int32 bufferOffsetReceive;
internal readonly Int32 permanentReceiveMessageOffset;
internal readonly Int32 bufferOffsetSend;
private Int32 idOfThisObject;

internal Int32 lengthOfCurrentIncomingMessage;

//receiveMessageOffset is used to mark the byte position where the message
//begins in the receive buffer. This value can sometimes be out of
//bounds for the data stream just received. But, if it is out of bounds, the
//code will not access it.
internal Int32 receiveMessageOffset;
internal Byte[] byteArrayForPrefix;
internal readonly Int32 receivePrefixLength;
internal Int32 receivedPrefixBytesDoneCount = 0;
internal Int32 receivedMessageBytesDoneCount = 0;
//This variable will be needed to calculate the value of the
//receiveMessageOffset variable in one situation. Notice that the
//name is similar but the usage is different from the variable
//receiveSendToken.receivePrefixBytesDone.
internal Int32 recPrefixBytesDoneThisOp = 0;

internal Int32 sendBytesRemainingCount;
internal readonly Int32 sendPrefixLength;
internal Byte[] dataToSend;
internal Int32 bytesSentAlreadyCount;

//The session ID correlates with all the data sent in a connected session.
//It is different from the transmission ID in the DataHolder, which relates
//to one TCP message. A connected session could have many messages, if you
//set up your app to allow it.
private Int32 sessionId;

public DataHoldingUserToken(SocketAsyncEventArgs e, Int32 rOffset, Int32 sOffset,
Int32 receivePrefixLength, Int32 sendPrefixLength, Int32 identifier)
{
this.idOfThisObject = identifier;

//Create a Mediator that has a reference to the SAEA object.
this.theMediator = new Mediator(e);
this.bufferOffsetReceive = rOffset;
this.bufferOffsetSend = sOffset;
this.receivePrefixLength = receivePrefixLength;
this.sendPrefixLength = sendPrefixLength;
this.receiveMessageOffset = rOffset + receivePrefixLength;
this.permanentReceiveMessageOffset = this.receiveMessageOffset;
}

//Let's use an ID for this object during testing, just so we can see what
//is happening better if we want to.
public Int32 TokenId
{
get
{
return this.idOfThisObject;
}
}

internal void CreateNewDataHolder()
{
theDataHolder = new DataHolder();
}

//Used to create sessionId variable in DataHoldingUserToken.
//Called in ProcessAccept().
internal void CreateSessionId()
{
sessionId = Interlocked.Increment(ref Program.mainSessionId);
}

public Int32 SessionId
{
get
{
return this.sessionId;
}
}

public void Reset()
{
this.receivedPrefixBytesDoneCount = 0;
this.receivedMessageBytesDoneCount = 0;
this.recPrefixBytesDoneThisOp = 0;
this.receiveMessageOffset = this.permanentReceiveMessageOffset;
}
}

class Mediator
{
private IncomingDataPreparer theIncomingDataPreparer;
private OutgoingDataPreparer theOutgoingDataPreparer;
private DataHolder theDataHolder;
private SocketAsyncEventArgs saeaObject;

public Mediator(SocketAsyncEventArgs e)
{
this.saeaObject = e;
this.theIncomingDataPreparer = new IncomingDataPreparer(saeaObject);
this.theOutgoingDataPreparer = new OutgoingDataPreparer();
}

internal void HandleData(DataHolder incomingDataHolder)
{
theDataHolder = theIncomingDataPreparer.HandleReceivedData
(incomingDataHolder, this.saeaObject);
}

internal void PrepareOutgoingData()
{
theOutgoingDataPreparer.PrepareOutgoingData(saeaObject, theDataHolder);
}

internal SocketAsyncEventArgs GiveBack()
{
return saeaObject;
}
}

class IncomingDataPreparer
{
private DataHolder theDataHolder;
private SocketAsyncEventArgs theSaeaObject;

public IncomingDataPreparer(SocketAsyncEventArgs e)
{
this.theSaeaObject = e;
}

private Int32 ReceivedTransMissionIdGetter()
{
Int32 receivedTransMissionId =
Interlocked.Increment(ref Program.mainTransMissionId);
return receivedTransMissionId;
}

private EndPoint GetRemoteEndpoint()
{
return this.theSaeaObject.AcceptSocket.RemoteEndPoint;
}

internal DataHolder HandleReceivedData(DataHolder incomingDataHolder,
SocketAsyncEventArgs theSaeaObject)
{
DataHoldingUserToken receiveToken =
(DataHoldingUserToken)theSaeaObject.UserToken;
theDataHolder = incomingDataHolder;
theDataHolder.sessionId = receiveToken.SessionId;
theDataHolder.receivedTransMissionId =
this.ReceivedTransMissionIdGetter();
theDataHolder.remoteEndpoint = this.GetRemoteEndpoint();
this.AddDataHolder();
return theDataHolder;
}

private void AddDataHolder()
{
lock (Program.lockerForList)
{
Program.listOfDataHolders.Add(theDataHolder);
}
}
}

class OutgoingDataPreparer
{
private DataHolder theDataHolder;

internal void PrepareOutgoingData(SocketAsyncEventArgs e,
DataHolder handledDataHolder)
{
DataHoldingUserToken theUserToken = (DataHoldingUserToken)e.UserToken;
theDataHolder = handledDataHolder;

//In this example code, we will send back the receivedTransMissionId,
// followed by the
//message that the client sent to the server. And we must
//prefix it with the length of the message. So we put 3
//things into the array.
// 1) prefix,
// 2) receivedTransMissionId,
// 3) the message that we received from the client, which
// we stored in our DataHolder until we needed it.
//That is our communication protocol. The client must know the protocol.

//Convert the receivedTransMissionId to byte array.
Byte[] idByteArray = BitConverter.GetBytes
(theDataHolder.receivedTransMissionId);

//Determine the length of all the data that we will send back.
Int32 lengthOfCurrentOutgoingMessage = idByteArray.Length
+ theDataHolder.dataMessageReceived.Length;

//So, now we convert the length integer into a byte array.
//Aren't byte arrays wonderful? Maybe you'll dream about byte arrays tonight!
Byte[] arrayOfBytesInPrefix = BitConverter.GetBytes
(lengthOfCurrentOutgoingMessage);

//Create the byte array to send.
theUserToken.dataToSend = new Byte[theUserToken.sendPrefixLength
+ lengthOfCurrentOutgoingMessage];

//Now copy the 3 things to the theUserToken.dataToSend.
Buffer.BlockCopy(arrayOfBytesInPrefix, 0, theUserToken.dataToSend,
0, theUserToken.sendPrefixLength);
Buffer.BlockCopy(idByteArray, 0, theUserToken.dataToSend,
theUserToken.sendPrefixLength, idByteArray.Length);
//The message that the client sent is already in a byte array, in DataHolder.
Buffer.BlockCopy(theDataHolder.dataMessageReceived, 0,
theUserToken.dataToSend, theUserToken.sendPrefixLength
+ idByteArray.Length, theDataHolder.dataMessageReceived.Length);

theUserToken.sendBytesRemainingCount =
theUserToken.sendPrefixLength + lengthOfCurrentOutgoingMessage;
theUserToken.bytesSentAlreadyCount = 0;
}
}

class DataHolder
{
//Remember, if a socket uses a byte array for its buffer, that byte array is
//unmanaged in .NET and can cause memory fragmentation. So, first write to the
//buffer block used by the SAEA object. Then, you can copy that data to another
//byte array, if you need to keep it or work on it, and want to be able to put
//the SAEA object back in the pool quickly, or continue with the data
//transmission quickly.
//DataHolder has this byte array to which you can copy the data.
internal Byte[] dataMessageReceived;

internal Int32 receivedTransMissionId;

internal Int32 sessionId;

//for testing. With a packet analyzer this can help you see specific connections.
internal EndPoint remoteEndpoint;
}

internal sealed class SocketAsyncEventArgsPool
{
//just for assigning an ID so we can watch our objects while testing.
private Int32 nextTokenId = 0;

// Pool of reusable SocketAsyncEventArgs objects.
Stack pool;

// initializes the object pool to the specified size.
// "capacity" = Maximum number of SocketAsyncEventArgs objects
internal SocketAsyncEventArgsPool(Int32 capacity)
{
this.pool = new Stack(capacity);
}

// The number of SocketAsyncEventArgs instances in the pool.
internal Int32 Count
{
get { return this.pool.Count; }
}

internal Int32 AssignTokenId()
{
Int32 tokenId = Interlocked.Increment(ref nextTokenId);
return tokenId;
}

// Removes a SocketAsyncEventArgs instance from the pool.
// returns SocketAsyncEventArgs removed from the pool.
internal SocketAsyncEventArgs Pop()
{
lock (this.pool)
{
return this.pool.Pop();
}
}

// Add a SocketAsyncEventArg instance to the pool.
// "item" = SocketAsyncEventArgs instance to add to the pool.
internal void Push(SocketAsyncEventArgs item)
{
if (item == null)
{
throw new ArgumentNullException("Items added to a
SocketAsyncEventArgsPool cannot be null");
}
lock (this.pool)
{
this.pool.Push(item);
}
}
}

class SocketListenerSettings
{
// the maximum number of connections the sample is designed to handle simultaneously
private Int32 maxConnections;

// this variable allows us to create some extra SAEA objects for the pool,
// if we wish.
private Int32 numberOfSaeaForRecSend;

// max # of pending connections the listener can hold in queue
private Int32 backlog;

// tells us how many objects to put in pool for accept operations
private Int32 maxSimultaneousAcceptOps;

// buffer size to use for each socket receive operation
private Int32 receiveBufferSize;

// length of message prefix for receive ops
private Int32 receivePrefixLength;

// length of message prefix for send ops
private Int32 sendPrefixLength;

// See comments in buffer manager.
private Int32 opsToPreAllocate;

// Endpoint for the listener.
private IPEndPoint localEndPoint;

public SocketListenerSettings(Int32 maxConnections,
Int32 excessSaeaObjectsInPool, Int32 backlog, Int32 maxSimultaneousAcceptOps,
Int32 receivePrefixLength, Int32 receiveBufferSize, Int32 sendPrefixLength,
Int32 opsToPreAlloc, IPEndPoint theLocalEndPoint)
{
this.maxConnections = maxConnections;
this.numberOfSaeaForRecSend = maxConnections + excessSaeaObjectsInPool;
this.backlog = backlog;
this.maxSimultaneousAcceptOps = maxSimultaneousAcceptOps;
this.receivePrefixLength = receivePrefixLength;
this.receiveBufferSize = receiveBufferSize;
this.sendPrefixLength = sendPrefixLength;
this.opsToPreAllocate = opsToPreAlloc;
this.localEndPoint = theLocalEndPoint;
}

public Int32 MaxConnections
{
get
{
return this.maxConnections;
}
}
public Int32 NumberOfSaeaForRecSend
{
get
{
return this.numberOfSaeaForRecSend;
}
}
public Int32 Backlog
{
get
{
return this.backlog;
}
}
public Int32 MaxAcceptOps
{
get
{
return this.maxSimultaneousAcceptOps;
}
}
public Int32 ReceivePrefixLength
{
get
{
return this.receivePrefixLength;
}
}
public Int32 BufferSize
{
get
{
return this.receiveBufferSize;
}
}
public Int32 SendPrefixLength
{
get
{
return this.sendPrefixLength;
}
}
public Int32 OpsToPreAllocate
{
get
{
return this.opsToPreAllocate;
}
}
public IPEndPoint LocalEndPoint
{
get
{
return this.localEndPoint;
}
}
}
2018-12-15 09:33:18 qq_27695659 阅读数 973
  • C# For Unity系列之进阶篇

    整体介绍:       本进阶篇面向的学员不再是完全的编程“小白”,而是具备一定C#编程经验,需要进一步查漏补缺、或者需要进一步全面完善自己C#编程知识体系的广大Unity学员。相信通过本进阶篇的学习,可以使得Unity初中级开发人员对于编程语言的掌握更进一步;对于开发中大型游戏项目,在编程语言这一层级进一步打下坚实的语言基础。 “中级/进阶篇”讲解特点与内容:          本“中级”与“进阶”篇, 是面向初中级游戏研发人员,以及Unity中高级学习者。为了更加深入的刨析各个语法的本质,我们采用反编译解读IL中间语言的方式,来解构语法重点与难点。 中级篇内容主要讲解: .Net 框架、里氏替换原则(LSP)、类的属性极其本质特性、IS ,AS 关键字、字符串的“驻留性” 原理、深入解析Equals() 原理、枚举类型、自定义集合、深入解析动态集合特性与内部原理、泛型集合、泛型约束、初级委托与事件讲解等。         "进阶篇"是在中级篇的基础之上,进一步研究与讲解关于IO操作、序列化、正则表达式、系统委托(Action、Function、Predicate等)、反射原理与特性、Linq查询表达式、多线程、线程池、任务、Socket套接字编程(Tcp与UDP协议),以及最后使用Unity开发具备实战价值的通讯聊天程序等。   C#“进阶篇”教学详细说明如下: 1: IO操作与序列化       学习文件、目录、二进制文件、文本文件的读取与写入底层原理。学习文件序列化与反序列化技能。 2: 正则表达式       学习正则表达式的强大作用与常用原字符的含义与应用场景。 3: 深入委托与事件       学习Action、Func、Predicate 系统内置委托类型,已经适用场合。学习匿名方法、Lambda表达式。深入解析委托与事件的区别。 4: 反射与特性       学习反射的概念与动态调用的重要应用价值,以及Type、Assembley核心类等,最后讲解“特性”技术。   5: Linq 查询表达式      学习Linq 查询表达式对于“对象集合”(支持IEnumberable 或IEnumberable<T>) 以及SQL数据库、XML文档方面的强大查询功能。       6: 多线程      学习多线程以及线程传参、线程取得返回数值技术,前台与后台线程、线程的同步、线程池、任务等技术。      7: Socket套接字通讯      学习Socket套接字通讯中,Tcp与UPD通讯协议的不同应用场景,以及各自的演示示例,最后用Unity开发一款实用性的聊天通讯工具。 温习提示:             本C# for Unity 使用Virtual Studio2012,以及Unity5.2 进行开发与讲解。(学员使用更高版本,对学习没有任何影响)。      一、热更新系列(技术含量:中高级): A:《lua热更新技术中级篇》 https://edu.csdn.net/course/detail/27087 B:《热更新框架设计之Xlua基础视频课程》 https://edu.csdn.net/course/detail/27110 C:《热更新框架设计之热更流程与热补丁技术》 https://edu.csdn.net/course/detail/27118 D:《热更新框架设计之客户端热更框架(上)》 https://edu.csdn.net/course/detail/27132 E:《热更新框架设计之客户端热更框架(中)》 https://edu.csdn.net/course/detail/27135 F:《热更新框架设计之客户端热更框架(下)》 https://edu.csdn.net/course/detail/27136 二:框架设计系列(技术含量:中级):  A:《游戏UI界面框架设计系列视频课程》 https://edu.csdn.net/course/detail/27142 B:《Unity客户端框架设计PureMVC篇视频课程(上)》 https://edu.csdn.net/course/detail/27172 C:《Unity客户端框架设计PureMVC篇视频课程(下)》 https://edu.csdn.net/course/detail/27173 D:《AssetBundle框架设计_框架篇视频课程》 https://edu.csdn.net/course/detail/27169 三、Unity脚本从入门到精通(技术含量:初级) A:《C# For Unity系列之入门篇》 https://edu.csdn.net/course/detail/4560 B:《C# For Unity系列之基础篇》 https://edu.csdn.net/course/detail/4595 C: 《C# For Unity系列之中级篇》 https://edu.csdn.net/course/detail/24422 D:《C# For Unity系列之进阶篇》 https://edu.csdn.net/course/detail/24465 四、虚拟现实(VR)与增强现实(AR):(技术含量:初级) A:《虚拟现实之汽车仿真模拟系统 》 https://edu.csdn.net/course/detail/26618 五、Unity基础课程系列(技术含量:初级)  A:《台球游戏与FlappyBirds—Unity快速入门系列视频课程(第1部)》   https://edu.csdn.net/course/detail/24643 B:《太空射击与移动端发布技术-Unity快速入门系列视频课程(第2部)》 https://edu.csdn.net/course/detail/24645  C:《Unity ECS(二) 小试牛刀》 https://edu.csdn.net/course/detail/27096 六、Unity ARPG课程(技术含量:初中级): A:《MMOARPG地下守护神_单机版实战视频课程(上部)》 https://edu.csdn.net/course/detail/24965 B:《MMOARPG地下守护神_单机版实战视频课程(中部)》 https://edu.csdn.net/course/detail/24968 C:《MMOARPG地下守护神_单机版实战视频课程(下部)》 https://edu.csdn.net/course/detail/24979

    1320 人正在学习 去看看 刘国柱
# python基础
# 网络编程 _
    # 什么是网络 网络的基础概念 *****
    # socket
        # TCP
        # UDP
# 并发编程 _ 概念


# qq 微信   _基于应用的网络程序
# 百度 微博 知乎    _基于浏览器的网络程序

# 网络编程中的 - C/S架构
    # c client  客户端
    # s server  服务端
# 网络编程中的 - B/S架构
    # b broser  浏览器
    # s server  服务端
    # 不需要额外的安装客户端了,只需要一个网址就可以访问
    # 轻量级  - 使用成本低
# B/S架构是C/S架构的一种特殊形式
# 手机上 : 浏览器 app

# 两个py程序想要通信
    # 写文件
# 在不同机器上的两个py程序之间想要通信
    # 网络

# 网络的发展史
    # 网卡\网口
    # 两台机器之间 插上网线就可以通信
    # 网卡上 - mac地址
    # ip地址
        # 是4个点分十进制  - ipv4协议
            # 0.0.0.0 - 255.255.255.255
            # 127.0.0.1 本机
            # 内网字段 192.168.****
                    #  10.****
                    #  172.***
        # 6个点分十进制  - ipv6协议
            # 0.0.0.0.0.0  - 255.255.255.255.255.255
    # 交换机
        # 广播
        # 单播
        # 组播
    # arp协议 : 通过IP地址获取某一台机器的mac地址
    # 子网掩码
        # 子网掩码 和 ip地址进行 按位 与 运算 就能得出一个机器所在的网段
        # 192.168.21.36
        # 11000000.10101000.00010101.00100100
        # 255.255.255.0   255.255.0.0
        # 11111111.11111111.11111111.00000000
        # 11000000.10101000.00010101.00000000
        # 192.168.21.0 网段
    # 网关地址 : 整个局域网中的机器能沟通过网关ip与外界通信
    # 网段 : 子网掩码 和 ip地址进行 按位 与 运算
    # 端口 : 0-65535
        # 8000-酷狗音乐  22-ssh  3306-mysql
        # python 网络应用  8000
        # ip地址+端口号 : 在全网找到唯一的一台机器+唯一的应用
        # 我们选择端口 : 8000之后
    # tcp协议
        # 全双工的通信协议
            # 一旦连接建立起来,那么连接两端的机器能够随意互相通信
            # 面向连接的通信方式
            # 数据安全不容易丢失
            # 建立连接的 三次握手 ******
            # 断开连接的 四次挥手 ******
2018-03-11 21:27:24 sinat_34828933 阅读数 1000
  • C# For Unity系列之进阶篇

    整体介绍:       本进阶篇面向的学员不再是完全的编程“小白”,而是具备一定C#编程经验,需要进一步查漏补缺、或者需要进一步全面完善自己C#编程知识体系的广大Unity学员。相信通过本进阶篇的学习,可以使得Unity初中级开发人员对于编程语言的掌握更进一步;对于开发中大型游戏项目,在编程语言这一层级进一步打下坚实的语言基础。 “中级/进阶篇”讲解特点与内容:          本“中级”与“进阶”篇, 是面向初中级游戏研发人员,以及Unity中高级学习者。为了更加深入的刨析各个语法的本质,我们采用反编译解读IL中间语言的方式,来解构语法重点与难点。 中级篇内容主要讲解: .Net 框架、里氏替换原则(LSP)、类的属性极其本质特性、IS ,AS 关键字、字符串的“驻留性” 原理、深入解析Equals() 原理、枚举类型、自定义集合、深入解析动态集合特性与内部原理、泛型集合、泛型约束、初级委托与事件讲解等。         "进阶篇"是在中级篇的基础之上,进一步研究与讲解关于IO操作、序列化、正则表达式、系统委托(Action、Function、Predicate等)、反射原理与特性、Linq查询表达式、多线程、线程池、任务、Socket套接字编程(Tcp与UDP协议),以及最后使用Unity开发具备实战价值的通讯聊天程序等。   C#“进阶篇”教学详细说明如下: 1: IO操作与序列化       学习文件、目录、二进制文件、文本文件的读取与写入底层原理。学习文件序列化与反序列化技能。 2: 正则表达式       学习正则表达式的强大作用与常用原字符的含义与应用场景。 3: 深入委托与事件       学习Action、Func、Predicate 系统内置委托类型,已经适用场合。学习匿名方法、Lambda表达式。深入解析委托与事件的区别。 4: 反射与特性       学习反射的概念与动态调用的重要应用价值,以及Type、Assembley核心类等,最后讲解“特性”技术。   5: Linq 查询表达式      学习Linq 查询表达式对于“对象集合”(支持IEnumberable 或IEnumberable<T>) 以及SQL数据库、XML文档方面的强大查询功能。       6: 多线程      学习多线程以及线程传参、线程取得返回数值技术,前台与后台线程、线程的同步、线程池、任务等技术。      7: Socket套接字通讯      学习Socket套接字通讯中,Tcp与UPD通讯协议的不同应用场景,以及各自的演示示例,最后用Unity开发一款实用性的聊天通讯工具。 温习提示:             本C# for Unity 使用Virtual Studio2012,以及Unity5.2 进行开发与讲解。(学员使用更高版本,对学习没有任何影响)。      一、热更新系列(技术含量:中高级): A:《lua热更新技术中级篇》 https://edu.csdn.net/course/detail/27087 B:《热更新框架设计之Xlua基础视频课程》 https://edu.csdn.net/course/detail/27110 C:《热更新框架设计之热更流程与热补丁技术》 https://edu.csdn.net/course/detail/27118 D:《热更新框架设计之客户端热更框架(上)》 https://edu.csdn.net/course/detail/27132 E:《热更新框架设计之客户端热更框架(中)》 https://edu.csdn.net/course/detail/27135 F:《热更新框架设计之客户端热更框架(下)》 https://edu.csdn.net/course/detail/27136 二:框架设计系列(技术含量:中级):  A:《游戏UI界面框架设计系列视频课程》 https://edu.csdn.net/course/detail/27142 B:《Unity客户端框架设计PureMVC篇视频课程(上)》 https://edu.csdn.net/course/detail/27172 C:《Unity客户端框架设计PureMVC篇视频课程(下)》 https://edu.csdn.net/course/detail/27173 D:《AssetBundle框架设计_框架篇视频课程》 https://edu.csdn.net/course/detail/27169 三、Unity脚本从入门到精通(技术含量:初级) A:《C# For Unity系列之入门篇》 https://edu.csdn.net/course/detail/4560 B:《C# For Unity系列之基础篇》 https://edu.csdn.net/course/detail/4595 C: 《C# For Unity系列之中级篇》 https://edu.csdn.net/course/detail/24422 D:《C# For Unity系列之进阶篇》 https://edu.csdn.net/course/detail/24465 四、虚拟现实(VR)与增强现实(AR):(技术含量:初级) A:《虚拟现实之汽车仿真模拟系统 》 https://edu.csdn.net/course/detail/26618 五、Unity基础课程系列(技术含量:初级)  A:《台球游戏与FlappyBirds—Unity快速入门系列视频课程(第1部)》   https://edu.csdn.net/course/detail/24643 B:《太空射击与移动端发布技术-Unity快速入门系列视频课程(第2部)》 https://edu.csdn.net/course/detail/24645  C:《Unity ECS(二) 小试牛刀》 https://edu.csdn.net/course/detail/27096 六、Unity ARPG课程(技术含量:初中级): A:《MMOARPG地下守护神_单机版实战视频课程(上部)》 https://edu.csdn.net/course/detail/24965 B:《MMOARPG地下守护神_单机版实战视频课程(中部)》 https://edu.csdn.net/course/detail/24968 C:《MMOARPG地下守护神_单机版实战视频课程(下部)》 https://edu.csdn.net/course/detail/24979

    1320 人正在学习 去看看 刘国柱

下面为大家梳理一下高并发处理的思路,从几个方面进行解决

1、流量处理

  • 防盗链处理(可以在nginx.conf配置,做加密处理,或者请求域名限制)

2、前端优化

  • 减少HTTP请求,减少css,js,image等组件的请求数(例如设置地图图片请求)
  • 添加异步请求(ajax的异步请求等)
  • 启用浏览器缓存和文件压缩
  • CDN加速(cdn的工作原理要明确,可以选用LVS做4层负载均衡,nginx或者squid做反向代理)
  • 建立独立的图片服务器

3、服务端优化

  • 页面静态化
  • 并发处理
  • 队列处理

4、数据库优化

  • 数据库缓存(redis)
  • 分库分表,分区操作
  • 读写分离
  • 主从复制
  • 双主热备
  • 负载均衡

5、web服务器优化

  • nginx负载均衡
  • F5(价格高昂)
  • LVS负载均衡

 

UDP编程实验

阅读数 2976

没有更多推荐了,返回首页