基于C#的网络通信(Socket编程,游戏开发中的通信),含同步和异步两种方式

在开始前先明确几个基本概念:

1、什么是网络编程(通信)?

回答:   这个问题的答案非常简单,就是要实现客户端和服务器端数据的交互(简单说就是用户和服务器的通信),而实现这一通信的手段呢比较常用的就是Socket套接字来实现的。

2、Socket实现通信的基本流程?

回答:其实又可以划分为比较常见的TCP和UDP两种,本文主要以TCP为例进行介绍,UDP的方式也是类似的,只是创建Socket和发送接收函数略有不同。基本的流程受篇幅限制,大家可以跳转到之前的一篇博文查看:构建TCP套接字(socket)的概念及具体步骤

3、什么是同步和异步?

回答:这里的同步和异步其实并非是指消息是否能同时到达,而是通信模式的不同,举一个形象的例子:大学体测1000米跑,如果体育老师让全班一个一个跑就是一种同步方式(一个学生跑完下一个学生再跑)。而我们更多采用的是每个学生给一个编号,起点处每隔一定时间起跑一个(实际上就是一定时间会发出一条消息),而在终点处有人专门记录每个人的时间就可以了(其实就是接收消息),这个过程其实就是异步。

 同步异步在网络编程中更多指的是通信模式:

通信的同步,主要是指客户端在发送请求后,必须得在服务端有回应后才发送下一个请求。所以这个时候的所有请求将会在服务端得到同步 。

通信的异步,指客户端在发送请求后,不必等待服务端的回应就可以发送下一个请求,这样对于所有的请求动作来说将会在服务端得到异步,这条请求的链路就象是一个请求队列,所有的动作在这里不会得到同步的。

4、什么是同步阻塞,什么是异步非阻塞?

这里还是需要先明确两个概念,什么是阻塞和非阻塞,其实很好理解,阻塞其实你打电话找一个人,对方不在,你就一直通话等着,很显然,上面的例子中的同步模式显然是阻塞的(one by one)。而非阻塞模式就是,我打电话找一个人,对方不在,你就说我一会再打过来看,很显然,上面的大家同时跑的方式是异步非阻塞的。

而阻塞和非阻塞只是应用在请求的读取和发送。 

5、客户端和服务器端同步or异步有什么要求么?

这个是没有必然要求的,只是不同的方式的效率不同而已,显然客户端和服务器都是同步模式的话那么效率是最低,而客户端和服务器端都是异步模式的话效率是最高的。也可以客户端和服务器端任意一方是同步的而另一方是异步的。

6、异步效率更高,有缺点么?

还是举上面跑步的例子,虽然把很多同学同时安排在跑到上跑步了,但是实际上大家跑步的速度是不一样的,可能会先出发后到达,这个时候还需要去记录和核对学生的姓名,很显然这样的过程非常复杂,这也是异步方式的一个缺点。那有没有又不复杂,效率又高的方法呢?有,一人一个秒表,跑的时候计时,到终点给老师看,显然这样不会出现计时上的错误,效率也很高,但是资源消耗大(需要一人一个表),其实这种方式就是多线程同步模式。

有了上面的例子之后呢,就开始上代码了,主要是两个例子:

1、为了演示方便,我们两个例子的客户端都是一样的功能,定时向服务器发送数据

2、两个例子的服务器端分别是同步的和异步的

首先先放上客户端的代码:

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

class client
{
    //声明socket
    Socket socket;
    //声明一个地址类
    IPEndPoint iep;
    public void connect()
    {
        try
        {
            //设定Socket的模式
            socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //指定服务端的地址和端口
            iep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 30000);
            //建立连接
            socket.Connect(iep);
            Console.WriteLine("服务器连接成功");
            
			//定义向服务器发送的数据
            string content = "你好,我是客户端!";
            //将字符串转换成字符数组
            byte[] bytes_string = Encoding.UTF8.GetBytes(content);
            
            while(true)
            {
				//定时向服务器发送数据
                Thread.Sleep(1000);
                socket.Send(bytes_string);
                Console.WriteLine("向服务器发送了:" + bytes_string.Length + "个字节");
            }
        }
        catch(Exception e)
        {
			//捕获异常
            Console.WriteLine("服务器连接失败");
            Console.WriteLine(e.ToString());
        }
    }
}

然后就是同步模式的服务器端代码:

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

public class demo
{
    //需要添加System.Net.Sockets的支持
    Socket socket;
    private string ip;
    private int port;
    //定义每次接收的最大字节数和接收数组
    private int maxBuffer = 1024;
    private byte[] buffer;
    //定义一个套接字代表客户端
    private Socket client;
    public demo(string _ip,int _port)
    {
        this.ip = _ip;
        this.port = _port;
    }
    //服务器连接函数
    public void StartConnect()
    {
        try
        {
            socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IPEndPoint iep = new IPEndPoint(IPAddress.Parse(ip), port);
            //绑定地址
            socket.Bind(iep);
            //设置客户端连接数量
            socket.Listen(1000);
            Console.WriteLine("服务器开启成功!");

            //监听客户端连接
            Console.WriteLine("监控客户端连接!");
            client = socket.Accept();

            Console.WriteLine("有客户端连接了!");
            Receive();
        }
        catch(Exception e)
        {
			//捕获异常
            Console.WriteLine("服务器开启失败!");
            Console.WriteLine(e.ToString());
        }
        
    }
    //接收客户端信息
    public void Receive()
    {
        //初始化接收的数组
        buffer = new byte[maxBuffer];
        try
        {
            //阻塞式同步接收
            while (true)
            {
                int length = client.Receive(buffer);   //同步模式的核心部分!
                string content = Encoding.UTF8.GetString(buffer, 0, length);
                Console.WriteLine("收到了客户端发来的信息:"+content+" 字节的长度为:" + length); 
            } 
            
        }
        catch (Exception e)
        {
            Console.WriteLine("接收异常!");
            Console.WriteLine(e.ToString());
        }
    }
}

同步的服务器端的核心其实就是接收方式,可以看得到这里用的接收函数时C#中封装的Socket函数,这个函数的方法感兴趣的同学可以在C#的官方文档中具体查看,这里不再赘述了:

接下来实现异步的服务器模式的代码:

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

public class demo
{
    //需要添加System.Net.Sockets的支持
    Socket socket;
    private string ip;
    private int port;
    //定义每次接收的最大字节数和接收数组
    private int maxBuffer = 1024;
    private byte[] buffer;
    //定义一个套接字代表客户端
    private Socket client;
    public demo(string _ip,int _port)
    {
        this.ip = _ip;
        this.port = _port;
    }
    //服务器连接函数
    public void StartConnect()
    {
        try
        {
            socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IPEndPoint iep = new IPEndPoint(IPAddress.Parse(ip), port);
            //绑定地址
            socket.Bind(iep);
            //设置客户端连接数量
            socket.Listen(1000);
            Console.WriteLine("服务器开启成功!");

            //监听客户端连接
            Console.WriteLine("监控客户端连接!");
            client = socket.Accept();

            Console.WriteLine("有客户端连接了!");
            Receive();
        }
        catch(Exception e)
        {
            Console.WriteLine("服务器开启失败!");
            //显示出异常详情
            Console.WriteLine(e.ToString());
        }
        
    }
    //接收客户端信息
    public void Receive()
    {
        //初始化接收的数组
        buffer = new byte[maxBuffer];
        try
        {
            //异步接收,接收的数据放到回调函数BegRece里面去处理,无论客户端是否发送消息都会执行这一步
            client.BeginReceive(buffer, 0, 1024, 0,new AsyncCallback(BegRece),client);    //异步的核心
        }
        catch (Exception e)
        {
            Console.WriteLine("接收异常!");
            Console.WriteLine(e.ToString());
        }
    }
//异步回调函数
    private void BegRece(IAsyncResult ar)
    {
        Socket so = (Socket)ar.AsyncState;
        //接收字节的数据长度
        int length = so.EndReceive(ar);
        string content = Encoding.UTF8.GetString(buffer,0,length);
        Console.WriteLine("收到了客户端发来的消息:" + content);
        //为了实现不停地接收数据,再次调用并对buffer重新赋值即可
        buffer = new byte[maxBuffer];
        client.BeginReceive(buffer, 0, 1024, 0, new AsyncCallback(BegRece), client);

    }
}

 可以发现异步和同步的不同有两点,一个是接收函数不同,异步方法采用的是BeginReceive(),这个函数的详细也可以在文档中查看:

 而关于回调函数的说明也可以在文档中查看,回调函数的本质可以理解为BeginReceive()完成接收,回调函数完成处理。

最后来看一下程序的运行结果:

源程序代码点击这里下载(VS2013编译):点击下载

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章