接口和协议组成 个人对 Socket 和协议的理解 以及 使用 System.Net.Sockets 命名空间编写简单的 TCP 通讯程序

sindynasty · 发布于 2017年02月25日 · 最后由 sindynasty 回复于 2017年05月26日 · 2555 次阅读
本帖已被设为精华帖!

Socket

我们要想实现两个程序在不同主机上进行相互通讯,我们就必须准确得标识这两个程序。我们知道对于一个程序来说其都有一个PID(即进程控制符),虽然对于同一台主机上来说PID是唯一的,但是在不同主机之间,两个程序的PID那就不一定是唯一的了,其极有可能会发生重复,因此我们无法使用PID来标识不同主机上的程序。

于是Socket变应运而生,其使用IP地址标识了主机后,再使用端口标识了程序,也许你会问:既然IP标识了主机,那为什么不继续使用PID标识程序,那是因为当我们开启一个程序,但系统分配给这个程序的PID是一个随机的值,这样就导致了我们本地的主机程序 无法得知 我们所需连接的 远程主机程序 的PID,从而无法完成连接。而端口却是固定的且是唯一的,一个端口只能被一个进程所占用,因此在网络通信中我们使用的端口标识程序。

综上所述,Socket是指 本地IP地址 和 远程IP地址 以及 本地端口号 和 远程端口号 的组合,其作用是标识不同主机间的程序,在计算机专业术语中它的意思是套接字,但我们光靠一个套接字显然不可能实现网络通讯,我们需要一些方法,这些方法最初起源于1983年加利福尼亚大学伯克利分校发布的Berkeley Sockets API,后经微软、英特尔等大型公司的完善及规范,形成了一套标准,并在Windows上推出了WinSock API,因此在网络编程中,其也被常常称为套接字接口,简称套接口。在.Net Framework的基础类库中,微软对WinSock API进行了进一步的包装,并为我们提供了强大的System.Net.Sockets命名空间,我们可以利用该命名空间轻松地完成网络编程。

协议

最近发现有很多人认为Socket是指协议,在这儿我想很明确地告诉你们,那是错误的,记住Socket不是协议
协议为计算机网络中进行数据交换而建立的规则、标准或约定的集合(百度百科上是这么写的)。其中它有三个要素:语义、语法、时序,以下是我个人对这三个要素的理解:

语义:是指发送的每个数据包中的数据所代表的含义,例如第一个数据包发送的数据表示的是协议编号,第二个数据包发送的数据表示的是真正所需传递的数据1,第三个数据包发送的数据表示的是真正所需传递的数据2,这就是语义。其也可以看做是发送数据包的顺序。

语法:是指单个数据包的格式,例如每个数据包的前8个字节表示的是数据包的大小,而后面的N个字节表示的是该数据包所传递的数据,这就是语法,当然这语法也包括数据在转换成字节时所用到的编码类型等。

时序:是指整个服务器和客户端交互的流程,其中包括长连接与短连接、同步与异步等。

协议指的是通过语义+语法+时序所建立起来的规则、标准或约定的集合,而Socket仅仅是实现网络通讯的工具而已。

Socket不是协议!Socket不是协议!Socket不是协议!

重要的事说三遍

相关知识补充

套接字的分类:System.Net.Sockets命名空间中,微软为我们提供了一个名为SocketType的枚举,其表示的是套接字的类型。SocketType枚举中共有6个成员,其中常用的有流式套接字(Stream)、数据报套接字(Dgram)、原始套接字(Raw)。流式套接字(Stream)必须被用于TCP协议中,这已经被微软写死,如果该套接字类型与协议类型不对应会报错;数据报套接字(Dgram)也已经被微软写死,只能用于UDP协议中;原始套接字(Raw)可以操纵网络层和传输层,一般被用于自定义协议中。

TCP是一种面向连接的协议:由于TCP是一种面向连接的协议,因此对于TCP服务端来说,其必须创建用于监听的Socket用于连接的Socket才能完成通讯,而由于UDP是一种面向无连接的协议,因此对于UDP服务端来说,其不需要监听连接。

网络通讯中所有数据都是以字节的形式进行传输:由于网络通讯中所有数据都是以字节的形式进行传输的,因此我们必须把相关数据都转化为byte[]的类型。其中System命名空间中的BitConverter类,为我们提供了一系列基础数据类型与字节数组相互转换的方法;另外对于字符串与字节数组的相互转换,我们可以使用System.Text命名空间中的Encoding类,并根据相应的编码方式来实现。

服务端和客户端通讯的基本流程(TCP)

服务端:创建用于监听的Socket ListenSocket

方法一:

Socket ListenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

方法二:

Socket ListenSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);

AddressFamily:是一个枚举,指的是服务端地址的类型,对于TCP来说,我们提供的服务端地址一般是IPV4地址或IPV6地址,他们分别对应的是InterNetwork和InterNetworkV6。
SocketType:是一个枚举,指的是套接字的类型,一般常用的有Stream、Dgram和Raw,TCP是使用流式套接字来实现传输的,因此我们在这里使用的是Stream,如果我们使用了SocketType.Stream那ProtocolType必须使用ProtocolType.Tcp,另外Dgram是指数据报套接字,其被用于UDP,如果我们使用了SocketType.Dgram那ProtocolType必须使用ProtocolType.Udp
ProtocolType:是一个枚举,指的是协议类型,这里我们要用的是Tcp,当然这个枚举里是没有HTTP的,由于HTTP是基于TCP的,因此如果要发HTTP,我们需要使用的还是Tcp。

服务端:将Socket ListenSocket与 服务端的IP及端口 进行绑定

IPAddress ServerIPAddress = IPAddress.Parse(ServerIP);
IPEndPoint ServerIPEndPoint = new IPEndPoint(ServerIPAddress, ServerPort);
ListenSocket.Bind(ServerIPEndPoint);

IPAddress:表示的是一个IP地址,我们可以使用IPAddress.Parse(String)的方法,将一个字符串形式的IP地址转换为一个IPAddress类。
IPEndPoint:表示的是一个由IP及端口组成的一个网络终结点,我们可以使用构造函数IPEndPoint(IPAddress, Int32)来进行创建,其所需的第二个参数指的是端口号。
Socket.Bind(EndPoint):该方法可以将Socket ListenSocket与 服务端的IP及端口 进行绑定,其中EndPoint表示的是一个网络地址,由于IPEndPoint继承自EndPoint,所以我们我们可以直接将IPEndPoint带入方法中来完成该步骤。

服务端:将Socket ListenSocket设为监听状态,开始监听

ListenSocket.Listen(MaxListenNumber);

Socket.Listen(Int32):该方法可以将服务端的Socket ListenSocket设为监听状态,其所需的参数Int32表示的是连接队列的最大连接数。

客户端:创建连接Socket ConnectSocket

方法一:

Socket ConnectSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

方法二:

Socket ConnectSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);

该方法与之前说的创建服务端的Socket ListenSocket相同因此不再叙述。

客户端:向服务器发起连接请求,并完成连接

设定服务端的IP及端口号

IPAddress ServerIPAddress = IPAddress.Parse(ServerIP);
IPEndPoint ServerIPEndPoint = new IPEndPoint(ServerIPAddress, ServerPort);

IPAddress和IPEndPoint:在发起连接前,我们需要设定服务端的地址,而设定服务端地址的方法也是利用IPAddress和IPEndPoint这两个类,由于该方法与之前讲的相同,我便不再叙说。
同步:

ConnectSocket.Connect(ServerIPEndPoint);

Socket.Connect(EndPoint):该方法可以使客户端向服务端以同步的方式发起一个连接请求。其所需的参数EndPoint表示的是服务端的地址,我们可以将IPEndPoint来带入。
异步:

    object[] ConnectState = { ConnectSocket };
    ConnectSocket.BeginConnect(ServerIPEndPoint, ConnectCallBack, ConnectState);

private static void ConnectCallBack(IAsyncResult AsyncResult)
{
    object[] ConnectState = (object[])AsyncResult.AsyncState;
    Socket ConnectSocket = (Socket)ConnectState[0];
    ConnectSocket.EndConnect(AsyncResult);
}//void ConnectCallBack(IAsyncResult AsyncResult)

Socket.BeginConnect(EndPoint, AsyncCallback, Object):该方法可以使客户端向服务器以异步的方式发起一个连接请求。该方法需要3个参数:第一个参数EndPoint,表示的是服务端的地址,我们可以将IPEndPoint来带入;第二个参数AsyncCallback表示的是一个异步委托,其原型是 delegate void AsyncCallback(IAsyncResult ar),因此回调函数应是 一个无返回值void类型 且 所需参数是一个IAsyncResult的函数,由于该参数是一个委托,因此我们可以直接将回调函数名直接带入即可;第三个参数Object表示所需传递至回调函数的参数,由于这里只能带入一个参数,我们需要将多个参数打包成一个object[],然后带入。
IAsyncResult和IAsyncResult.AsyncState:IAsyncResult是一个接口,表示的是一个异步操作的状态,我们可以使用IAsyncResult的AsyncState属性以及强制转换类型的方法来获取 在BeginConnect(EndPoint, AsyncCallback, Object)函数中所带入的第三个参数object的 值。
Socket.EndConnect(IAsyncResult):该方法可以结束一个客户端向服务端发起的异步连接请求,其所需的参数是一个IAsyncResult

服务端:接收客户端发来的请求并创建一个用于连接该客户端的Socket ConnectSocket

同步:

Socket ConnectSocket = ListenSocket.Accept();

Socket.Accept():该方法可以使服务端接受客户端的连接请求,并生成一个服务端用于连接客户端的Socket ConnectSocket。
异步:

    object[] AcceptState = { ListenSocket };
    ListenSocket.BeginAccept(AcceptCallBack, AcceptState);

private static void AcceptCallBack(IAsyncResult AsyncResult)
{
    object[] AcceptState = (object[])AsyncResult.AsyncState;
    Socket ListenSocket = (Socket)AcceptState[0];
    Socket ConnectSocket = ListenSocket.EndAccept(AsyncResult);
}//void AcceptCallBack(IAsyncResult AsyncResult)

Socket.BeginAccept(AsyncCallback, Object):该方法可以 以异步的方式 使服务端接收来自客户端发来的连接请求,并生成一个服务端用于连接客户端的Socket ConnectSocket。该方法需要2个参数:第一个参数AsyncCallback和之前说的一样,表示是的一个异步委托,我们只需带入回调函数名即可;第二个参数Object表示所需传递至回调函数的参数。
Socket.EndAccept(IAsyncResult):该方法可以终止 使服务端接受客户端的连接请求 的异步操作,其所需的参数是一个IAsyncResult,该方法具有一个返回值,返回值即为Socket ConnectSocket。

客户端:开始向服务端发送数据(服务端向客户端发送数据也是同理)

同步:

ConnectSocket.Send(ClientData, 0, ClientData.Length, SocketFlags.None);

Socket.Send(Byte[], Int32, Int32, SocketFlags):该方法可以将要发送的byte[]形式的数据写入连接套接字中,从而实现数据的发送。该方法需要4个参数:第一个参数Byte[]表示的是所需发送的数据对象;第二个参数Int32表示的是发送数据的起始位置;第三个参数Int32表示的是发送数据的大小;第四个参数SocketFlags是一个枚举,其指的是套接字的收发行为,一般我们只需使用None即可。
异步:

    object[] SendState = { ConnectSocket, ClientData };
    ConnectSocket.BeginSend(ClientData, 0, ClientData.Length, SocketFlags.None, SendCallBack, SendState);

private static void SendCallBack(IAsyncResult AsyncResult)
{
    object[] SendState = (object[])AsyncResult.AsyncState;
    Socket ConnectSocket = (Socket)SendState[0];
    byte[] ClientData = (byte[])SendState[1];
    ConnectSocket.EndSend(AsyncResult);
}//void SendCallBack(IAsyncResult AsyncResult)

Socket.BeginSend(Byte[], Int32, Int32, SocketFlags, AsyncCallback, Object):该方法可以 以异步的方式 将要发送的数据写入连接套接字中。该方法需要6个参数::第一个参数Byte[]表示的是所需发送的数据对象;第二个参数Int32表示的是发送数据的起始位置;第三个参数Int32表示的是发送数据的大小;第四个参数SocketFlags是一个枚举,其指的是套接字的收发行为,一般我们只需使用None即可;第五个参数AsyncCallback表示的是一个异步委托,我们只需带入回调函数名即可;第六个参数Object表示的是所需传递给回调函数的数据。
Socket.EndSend(IAsyncResult):该方法可以终止 将数据byte[]写入套接字 的异步操作,其所需的参数是一个IAsyncResult,该方法具有一个int类型的返回值,表示的是已经写入套接字的数据大小。

服务端:开始接收客户端发来的数据(客户端接收服务端发来的数据也是同理)

同步:

ConnectSocket.Receive(ClientData, 0, ClientData.Length, SocketFlags.None);

Socket.Receive(Byte[], Int32, Int32, SocketFlags):该方法可以从连接套接字中取出远程发来的数据byte[]。该方法需要4个参数:第一个参数Byte[]表示的是接收数据的数据对象;第二个参数Int32表示的是接收数据的起始存放位置;第三个参数Int32表示的是所需接收数据的大小;第四个参数SocketFlags是一个枚举,其指的是套接字的收发行为,一般我们只需使用None即可。
异步:

    object[] ReceiveState = { ConnectSocket, ClientData };
    ConnectSocket.BeginReceive(ClientData, 0, ClientData.Length, SocketFlags.None, ReceiveCallBack, ReceiveState);

private static void ReceiveCallBack(IAsyncResult AsyncResult)
{
    object[] ReceiveState = (object[])AsyncResult.AsyncState;
    Socket ConnectSocket = (Socket)ReceiveState[0];
    byte[] ClientData = (byte[])ReceiveState[1];
    ConnectSocket.EndReceive(AsyncResult);
}//void ReceiveCallBack(IAsyncResult AsyncResult)

Socket.BeginReceive(Byte[], Int32, Int32, SocketFlags, AsyncCallback, Object):该方法可以 以异步的方式 从连接套接字中取出远程发来的数据byte[]。该方法需要6个参数:第一个参数Byte[]表示的是接收数据的数据对象;第二个参数Int32表示的是接收数据的起始存放位置;第三个参数Int32表示的是所需接收数据的大小、第四个参数SocketFlags是一个枚举,其指的是套接字的收发行为,一般我们只需使用None即可;第五个参数AsyncCallback表示的是一个异步委托,我们只需带入回调函数名即可;第六个参数Object表示的是所需传递给回调函数的数据。
Socket.EndReceive(IAsyncResult):该方法可以终止 从连接套接字中取出远程发来的数据 的异步操作,其所需的参数是一个IAsyncResult,该方法具有一个int类型的返回值,表示的是从连接套接字中已经取出的数据大小。

客户端:关闭客户端用于连接服务端的Socket ConnectSocket(服务端关闭Socket ConnectSocket也是同理)

同步:

ConnectSocket.Disconnect(true);

Socket.Disconnect(Boolean):该方法可以关闭套接字连接,并允许设定是否可以重用套接字,其所需的参数Boolean是一个布尔值,为true则表示可以重用套接字,为false则表示不可重用套接字。
异步:

    object[] DisconnectState = { ConnectSocket };
    ConnectSocket.BeginDisconnect(true, DisconnectCallBack, DisconnectState);

private static void DisconnectCallBack(IAsyncResult AsyncResult)
{
    object[] DisconnectState = (object[])AsyncResult.AsyncState;
    Socket ConnectSocket = (Socket)DisconnectState[0];
    ConnectSocket.EndReceive(AsyncResult);
}//void DisconnectCallBack(IAsyncResult AsyncResult)

Socket.BeginDisconnect(Boolean, AsyncCallback, Object):该方法可以 以异步的方式 关闭套接字连接。该方法有3个参数:第一个参数Boolean是一个布尔值,为true则表示可以重用套接字,为false则表示不可重用套接字;;第二个参数AsyncCallback表示的是一个异步委托,我们只需带入回调函数名即可;第三个参数Object表示的是所需传递给回调函数的数据。
Socket.EndDisconnect(IAsyncResult):该方法可以终止 关闭套接字连接 的异步操作,其所需的参数是一个IAsyncResult

System.Net.Sockets命名空间的API文档

在System.Net.Sockets命名空间还有其他许多重要的类、方法等,例如能增强异步通讯的SocketAsyncEventArgs类等,其相关API文档都在MSDN里,网址是https://msdn.microsoft.com/zh-cn/library/system.net.sockets(v=vs.110).aspx

短连接与长连接

短连接:短连接是指服务端与客户端每次完成通讯后,就断开连接套接字Socket ConnectSocket的连接,同时每次需要服务端与客户端产生通讯的时候,都要重新创建连接套接字Socket ConnectSocket,最典型的短连接应该就是HTTP了吧(当然从HTTP/1.1起,HTTP默认使用长连接)
长连接:用于对于客户端来说,其只需要一个连接套接字Socket ConnectSocket就能完成与服务端所有的通讯,因此对于长连接来说,客户端只需在最开始创建一个连接套接字Socket ConnectSocket,便可以和服务端反复通讯多次。而当一个连接套接字Socket ConnectSocket长时间存在,其便会出现两个问题:1、当Socket ConnectSocket的连接如果被中断后,我们再去使用这个Socket ConnectSocket进行通讯时,其便会出错;2、如果服务端不断地接收客户端的连接并创建相应的连接套接字Socket ConnectSocket,却又不去关闭已经失效的Socket ConnectSocket,那么服务端迟早将会挂掉。由于会出现以上这两个问题,为此便引申出了KeepAlive机制。
KeepAlive机制:简单地来讲就是让一台主机每隔一段时间不停地向另一台远程主机发送连接请求(心跳包),以确认对方是否仍处于连接状态,如果发现对方长时间不应答,便关闭与对方连接。理论上来说服务端和客户端都可以向对方发送心跳包,但一般来说都是由客户端向服务端发送心跳包。在TCP中,KeepAlive机制默认是如果对方2小时不应答,则会断开连接,但是由于2小时时间过长,因此一般我们都要重写该机制。

共收到 15 条回复
10611

C#:使用System.Net.Sockets命名空间编写的简单的TCP通讯示例(包含同步服务端、同步客户端、异步服务端、异步客户端)

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

namespace SinDynasty
{
    public class SyncServer
    {
        private class SyncTCP
        {
            private static Socket ListenSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);
            private Socket ConnectSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);
            private List<byte[]> ClientDataList = new List<byte[]>();
            private List<byte[]> ServerDataList = new List<byte[]>();

            public static void Listen(string ServerIP, int ServerPort, int MaxListenNumber)
            {
                IPAddress ServerIPAddress = IPAddress.Parse(ServerIP);
                IPEndPoint ServerIPEndPoint = new IPEndPoint(ServerIPAddress, ServerPort);
                ListenSocket.Bind(ServerIPEndPoint);
                ListenSocket.Listen(MaxListenNumber);
            }//void Listen(string ServerIP, int ServerPort, int MaxListenNumber)

            public void Accept()
            {
                ConnectSocket = ListenSocket.Accept();
            }//void Accept()

            public void Receive()
            {
                byte[] ClientDataQuantity = new byte[4];
                ConnectSocket.Receive(ClientDataQuantity, 0, ClientDataQuantity.Length, SocketFlags.None);

                long CycleIndex = 0;
                while (CycleIndex < BitConverter.ToInt32(ClientDataQuantity, 0))
                {
                    byte[] ClientDataLength = new byte[4];
                    ConnectSocket.Receive(ClientDataLength, 0, ClientDataLength.Length, SocketFlags.None);

                    byte[] ClientData = new byte[0];
                    if (BitConverter.ToInt32(ClientDataLength, 0) > 0)
                    {
                        ClientData = new byte[BitConverter.ToInt32(ClientDataLength, 0)];
                        ConnectSocket.Receive(ClientData, 0, ClientData.Length, SocketFlags.None);
                    }

                    ClientDataList.Add(ClientData);

                    CycleIndex = CycleIndex + 1;
                }
            }//void Receive()

            public void ProtocolHandler()
            {
                byte[] ProtocolNumber = ClientDataList[0];
                if (ProtocolNumber.Length != 4)
                {
                    ProtocolNumber = BitConverter.GetBytes(0);
                }
                string ProtocolName = "Protocol_" + BitConverter.ToInt32(ProtocolNumber, 0);
                Protocol P = new Protocol();
                Type T = P.GetType();
                MethodInfo ProtocolMethodInfo = T.GetMethod(ProtocolName);
                if (ProtocolMethodInfo == null)
                {
                    ProtocolMethodInfo = T.GetMethod("Protocol_0");
                }
                object[] ProtocolParameter = { ClientDataList };
                ServerDataList = (List<byte[]>)ProtocolMethodInfo.Invoke(null, ProtocolParameter);
            }//void ProtocolHandler()

            public void Send()
            {
                byte[] ServerDataQuantity = BitConverter.GetBytes(ServerDataList.Count);
                ConnectSocket.Send(ServerDataQuantity, 0, ServerDataQuantity.Length, SocketFlags.None);

                foreach (byte[] ServerData in ServerDataList)
                {
                    byte[] ServerDataLength = BitConverter.GetBytes(ServerData.Length);
                    ConnectSocket.Send(ServerDataLength, 0, ServerDataLength.Length, SocketFlags.None);

                    ConnectSocket.Send(ServerData, 0, ServerData.Length, SocketFlags.None);
                }
            }//void Send()

            public void Disconnect()
            {
                if (ConnectSocket != null)
                {
                    ConnectSocket.Disconnect(true);
                }
            }//void DisconnectConnect()

            private class Protocol
            {
                public static List<byte[]> Protocol_0(List<byte[]> ClientDataList)
                {
                    List<byte[]> ServerDataList = new List<byte[]>();
                    ServerDataList.Add(Encoding.Default.GetBytes("未知的协议"));

                    return ServerDataList;
                }//List<byte[]> Protocol_0(List<byte[]> ClientDataList)

                public static List<byte[]> Protocol_1(List<byte[]> ClientDataList)
                {
                    List<byte[]> ServerDataList = ClientDataList;

                    return ServerDataList;
                }//List<byte[]> Protocol_1(List<byte[]> ClientDataList)

            }//class Protocol

        }//class SyncTCP

        public static void TCP_Start(string ServerIP, int ServerPort, int MaxListenNumber)
        {
            SyncTCP.Listen(ServerIP, ServerPort, MaxListenNumber);
        }//void TCP_Start(string ServerIP, int ServerPort, int MaxListenNumber)

        public static void TCP()
        {
            SyncTCP Server = new SyncTCP();
            while (true)
            {
                Server.Accept();
                Server.Receive();
                Server.ProtocolHandler();
                Server.Send();
                Server.Disconnect();
            }
        }//void TCP()

    }//class SyncServer

    public class SyncClient
    {
        private class SyncTCP
        {
            private static Socket ConnectSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);
            public List<byte[]> ClientDataList = new List<byte[]>();
            public List<byte[]> ServerDataList = new List<byte[]>();

            public static void Connect(string ServerIP, int ServerPort)
            {
                IPAddress ServerIPAddress = IPAddress.Parse(ServerIP);
                IPEndPoint ServerIPEndPoint = new IPEndPoint(ServerIPAddress, ServerPort);
                ConnectSocket.Connect(ServerIPEndPoint);
            }//void Connect(string ServerIP, int ServerPort)

            public void Send()
            {
                byte[] ClientDataQuantity = BitConverter.GetBytes(ClientDataList.Count);
                ConnectSocket.Send(ClientDataQuantity, 0, ClientDataQuantity.Length, SocketFlags.None);

                foreach (byte[] ClientData in ClientDataList)
                {
                    byte[] ClientDataLength = BitConverter.GetBytes(ClientData.Length);
                    ConnectSocket.Send(ClientDataLength, 0, ClientDataLength.Length, SocketFlags.None);

                    ConnectSocket.Send(ClientData, 0, ClientData.Length, SocketFlags.None);
                }
            }//void Send()

            public void Receive()
            {
                byte[] ServerDataQuantity = new byte[4];
                ConnectSocket.Receive(ServerDataQuantity, 0, ServerDataQuantity.Length, SocketFlags.None);

                long CycleIndex = 0;
                while (CycleIndex < BitConverter.ToInt32(ServerDataQuantity, 0))
                {
                    byte[] ServerDataLength = new byte[4];
                    ConnectSocket.Receive(ServerDataLength, 0, ServerDataLength.Length, SocketFlags.None);

                    byte[] ServerData = new byte[0];
                    if (BitConverter.ToInt32(ServerDataLength, 0) > 0)
                    {
                        ServerData = new byte[BitConverter.ToInt32(ServerDataLength, 0)];
                        ConnectSocket.Receive(ServerData, 0, ServerData.Length, SocketFlags.None);
                    }

                    ServerDataList.Add(ServerData);

                    CycleIndex = CycleIndex + 1;
                }
            }//void Receive()

            public static void Disconnect()
            {
                if (ConnectSocket != null)
                {
                    ConnectSocket.Disconnect(true);
                }
            }//void Disconnect()

        }//class SyncTCP

        public static void TCP_Start(string ServerIP, int ServerPort)
        {
            SyncTCP.Connect(ServerIP, ServerPort);
        }//void TCP_Start(string ServerIP, int ServerPort)

        public static List<byte[]> TCP(List<byte[]> ClientDataList)
        {
            SyncTCP TCP = new SyncTCP();
            TCP.ClientDataList = ClientDataList;
            TCP.Send();
            TCP.Receive();
            List<byte[]> ServerDataList = TCP.ServerDataList;

            return ServerDataList;
        }//List<byte[]> TCP(List<byte[]> ClientDataList)

        public static void TCP_End()
        {
            SyncTCP.Disconnect();
        }//void TCP_End()

    }//class SyncClient

    public class AsyncServer
    {
        private class AsyncTCP
        {
            private static Socket ListenSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);
            private Socket ConnectSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);
            private List<byte[]> ClientDataList = new List<byte[]>();
            private List<byte[]> ServerDataList = new List<byte[]>();

            public static void Listen(string ServerIP, int ServerPort, int MaxListenNumber)
            {
                IPAddress ServerIPAddress = IPAddress.Parse(ServerIP);
                IPEndPoint ServerIPEndPoint = new IPEndPoint(ServerIPAddress, ServerPort);
                ListenSocket.Bind(ServerIPEndPoint);
                ListenSocket.Listen(MaxListenNumber);
            }//void Listen(string ServerIP, int ServerPort, int MaxListenNumber)

            private ManualResetEvent MRE_Accept = new ManualResetEvent(false);
            public void Accept()
            {
                MRE_Accept.Reset();

                ListenSocket.BeginAccept(AcceptCallBack, null);

                MRE_Accept.WaitOne();
            }//void Accept()

            private void AcceptCallBack(IAsyncResult AsyncResult)
            {
                ConnectSocket = ListenSocket.EndAccept(AsyncResult);

                MRE_Accept.Set();
            }//void AcceptCallBack(IAsyncResult AsyncResult)

            private ManualResetEvent MRE_Receive = new ManualResetEvent(false);
            private ManualResetEvent MRE_ReceiveClientDataQuantity = new ManualResetEvent(false);
            private ManualResetEvent MRE_ReceiveClientDataLength = new ManualResetEvent(false);
            private ManualResetEvent MRE_ReceiveClientData = new ManualResetEvent(false);

            public void Receive()
            {
                MRE_Receive.Reset();

                MRE_ReceiveClientDataQuantity.Reset();

                byte[] ClientDataQuantity = new byte[4];
                object[] ReceiveClientDataQuantityState = { ClientDataQuantity };
                ConnectSocket.BeginReceive(ClientDataQuantity, 0, ClientDataQuantity.Length, SocketFlags.None, ReceiveClientDataQuantityCallBack, ReceiveClientDataQuantityState);

                MRE_ReceiveClientDataQuantity.WaitOne();

                MRE_Receive.WaitOne();
            }//void Receive()

            private void ReceiveClientDataQuantityCallBack(IAsyncResult AsyncResult)
            {
                object[] ReceiveClientDataQuantityState = (object[])AsyncResult.AsyncState;
                byte[] ClientDataQuantity = (byte[])ReceiveClientDataQuantityState[0];
                ConnectSocket.EndReceive(AsyncResult);

                MRE_ReceiveClientDataQuantity.Set();

                long CycleIndex = 0;
                while (CycleIndex < BitConverter.ToInt32(ClientDataQuantity, 0))
                {
                    MRE_ReceiveClientDataLength.Reset();

                    byte[] ClientDataLength = new byte[4];
                    ConnectSocket.BeginReceive(ClientDataLength, 0, ClientDataLength.Length, SocketFlags.None, ReceiveClientDataLengthCallBack, null);

                    MRE_ReceiveClientDataLength.WaitOne();

                    byte[] ClientData = new byte[0];
                    if (BitConverter.ToInt32(ClientDataLength, 0) > 0)
                    {
                        MRE_ReceiveClientData.Reset();

                        ClientData = new byte[BitConverter.ToInt32(ClientDataLength, 0)];
                        ConnectSocket.BeginReceive(ClientData, 0, ClientData.Length, SocketFlags.None, ReceiveClientDataCallBack, null);

                        MRE_ReceiveClientData.WaitOne();
                    }

                    ClientDataList.Add(ClientData);

                    CycleIndex = CycleIndex + 1;
                }

                MRE_Receive.Set();
            }//void ReceiveClientDataQuantityCallBack(IAsyncResult AsyncResult)

            private void ReceiveClientDataLengthCallBack(IAsyncResult AsyncResult)
            {
                ConnectSocket.EndReceive(AsyncResult);

                MRE_ReceiveClientDataLength.Set();
            }//void ReceiveClientDataLengthCallBack(IAsyncResult AsyncResult)

            private void ReceiveClientDataCallBack(IAsyncResult AsyncResult)
            {
                ConnectSocket.EndReceive(AsyncResult);

                MRE_ReceiveClientData.Set();
            }//void ReceiveClientDataCallBack(IAsyncResult AsyncResult)

            public void ProtocolHandler()
            {
                byte[] ProtocolNumber = ClientDataList[0];
                if (ProtocolNumber.Length != 4)
                {
                    ProtocolNumber = BitConverter.GetBytes(0);
                }
                string ProtocolName = "Protocol_" + BitConverter.ToInt32(ProtocolNumber, 0);
                Protocol P = new Protocol();
                Type T = P.GetType();
                MethodInfo ProtocolMethodInfo = T.GetMethod(ProtocolName);
                if (ProtocolMethodInfo == null)
                {
                    ProtocolMethodInfo = T.GetMethod("Protocol_0");
                }
                object[] ProtocolParameter = { ClientDataList };
                ServerDataList = (List<byte[]>)ProtocolMethodInfo.Invoke(null, ProtocolParameter);
            }//void ProtocolHandler()

            private ManualResetEvent MRE_Send = new ManualResetEvent(false);
            private ManualResetEvent MRE_SendServerDataQuantity = new ManualResetEvent(false);
            private ManualResetEvent MRE_SendServerDataLength = new ManualResetEvent(false);
            private ManualResetEvent MRE_SendServerData = new ManualResetEvent(false);
            public void Send()
            {
                MRE_Send.Reset();

                MRE_SendServerDataQuantity.Reset();

                byte[] ServerDataQuantity = BitConverter.GetBytes(ServerDataList.Count);
                ConnectSocket.BeginSend(ServerDataQuantity, 0, ServerDataQuantity.Length, SocketFlags.None, SendServerDataQuantityCallBack, null);

                MRE_SendServerDataQuantity.WaitOne();

                MRE_Send.WaitOne();
            }//void Send()

            private void SendServerDataQuantityCallBack(IAsyncResult AsyncResult)
            {
                ConnectSocket.EndSend(AsyncResult);

                MRE_SendServerDataQuantity.Set();

                foreach (byte[] ServerData in ServerDataList)
                {
                    MRE_SendServerDataLength.Reset();

                    byte[] ServerDataLength = BitConverter.GetBytes(ServerData.Length);
                    ConnectSocket.BeginSend(ServerDataLength, 0, ServerDataLength.Length, SocketFlags.None, SendServerDataLengthCallBack, null);

                    MRE_SendServerDataLength.WaitOne();

                    MRE_SendServerData.Reset();

                    ConnectSocket.BeginSend(ServerData, 0, ServerData.Length, SocketFlags.None, SendServerDataCallBack, null);

                    MRE_SendServerData.WaitOne();
                }

                MRE_Send.Set();
            }//void SendServerDataQuantityCallBack(IAsyncResult AsyncResult)

            private void SendServerDataLengthCallBack(IAsyncResult AsyncResult)
            {
                ConnectSocket.EndSend(AsyncResult);

                MRE_SendServerDataLength.Set();
            }//void SendServerDataLengthCallBack(IAsyncResult AsyncResult)

            private void SendServerDataCallBack(IAsyncResult AsyncResult)
            {
                ConnectSocket.EndSend(AsyncResult);

                MRE_SendServerData.Set();
            }//void SendServerDataCallBack(IAsyncResult AsyncResult)

            private ManualResetEvent MRE_Disconnect = new ManualResetEvent(false);
            public void Disconnect()
            {
                if (ConnectSocket != null)
                {
                    MRE_Disconnect.Reset();

                    ConnectSocket.BeginDisconnect(true, DisconnectCallBack, null);

                    MRE_Disconnect.WaitOne();
                }
            }//void DisconnectConnect()

            private void DisconnectCallBack(IAsyncResult AsyncResult)
            {
                ConnectSocket.EndDisconnect(AsyncResult);

                MRE_Disconnect.Set();
            }//void DisconnectCallBack(IAsyncResult AsyncResult)

            private class Protocol
            {
                public static List<byte[]> Protocol_0(List<byte[]> ClientDataList)
                {
                    List<byte[]> ServerDataList = new List<byte[]>();
                    ServerDataList.Add(Encoding.Default.GetBytes("未知的协议"));

                    return ServerDataList;
                }//List<byte[]> Protocol_0(List<byte[]> ClientDataList)

                public static List<byte[]> Protocol_1(List<byte[]> ClientDataList)
                {
                    List<byte[]> ServerDataList = ClientDataList;

                    return ServerDataList;
                }//List<byte[]> Protocol_1(List<byte[]> ClientDataList)

            }//class Protocol

        }//class AsyncTCP

        public static void TCP_Start(string ServerIP, int ServerPort, int MaxListenNumber)
        {
            AsyncTCP.Listen(ServerIP, ServerPort, MaxListenNumber);
        }//void TCP_Start(string ServerIP, int ServerPort, int MaxListenNumber)

        public static void TCP()
        {
            AsyncTCP Server = new AsyncTCP();
            while (true)
            {
                Server.Accept();
                Server.Receive();
                Server.ProtocolHandler();
                Server.Send();
                Server.Disconnect();
            }
        }//void TCP()

    }//class AsyncServer

    public class AsyncClient
    {
        private class AsyncTCP
        {
            private static Socket ConnectSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);
            public List<byte[]> ClientDataList = new List<byte[]>();
            public List<byte[]> ServerDataList = new List<byte[]>();

            private static ManualResetEvent MRE_Connect = new ManualResetEvent(false);
            public static void Connect(string ServerIP, int ServerPort)
            {
                MRE_Connect.Reset();

                IPAddress ServerIPAddress = IPAddress.Parse(ServerIP);
                IPEndPoint ServerIPEndPoint = new IPEndPoint(ServerIPAddress, ServerPort);
                ConnectSocket.BeginConnect(ServerIPEndPoint, ConnectCallBack, null);

                MRE_Connect.WaitOne();
            }//Socket Connect(string ServerIP, int ServerPort)

            private static void ConnectCallBack(IAsyncResult AsyncResult)
            {
                ConnectSocket.EndConnect(AsyncResult);

                MRE_Connect.Set();
            }//void ConnectCallBack(IAsyncResult AsyncResult)

            private ManualResetEvent MRE_Send = new ManualResetEvent(false);
            private ManualResetEvent MRE_SendClientDataQuantity = new ManualResetEvent(false);
            private ManualResetEvent MRE_SendClientDataLength = new ManualResetEvent(false);
            private ManualResetEvent MRE_SendClientData = new ManualResetEvent(false);
            public void Send()
            {
                MRE_Send.Reset();

                MRE_SendClientDataQuantity.Reset();

                byte[] ClientDataQuantity = BitConverter.GetBytes(ClientDataList.Count);
                ConnectSocket.BeginSend(ClientDataQuantity, 0, ClientDataQuantity.Length, SocketFlags.None, SendClientDataQuantityCallBack, null);

                MRE_SendClientDataQuantity.WaitOne();

                MRE_Send.WaitOne();
            }//void Send()

            private void SendClientDataQuantityCallBack(IAsyncResult AsyncResult)
            {
                ConnectSocket.EndSend(AsyncResult);

                MRE_SendClientDataQuantity.Set();

                foreach (byte[] ClientData in ClientDataList)
                {
                    MRE_SendClientDataLength.Reset();

                    byte[] ClientDataLength = BitConverter.GetBytes(ClientData.Length);
                    ConnectSocket.BeginSend(ClientDataLength, 0, ClientDataLength.Length, SocketFlags.None, SendClientDataLengthCallBack, null);

                    MRE_SendClientDataLength.WaitOne();

                    MRE_SendClientData.Reset();

                    ConnectSocket.BeginSend(ClientData, 0, ClientData.Length, SocketFlags.None, SendClientDataCallBack, null);

                    MRE_SendClientData.WaitOne();
                }

                MRE_Send.Set();
            }//void SendClientDataQuantityCallBack(IAsyncResult AsyncResult)

            private void SendClientDataLengthCallBack(IAsyncResult AsyncResult)
            {
                ConnectSocket.EndSend(AsyncResult);

                MRE_SendClientDataLength.Set();
            }//void SendClientDataLengthCallBack(IAsyncResult AsyncResult)

            private void SendClientDataCallBack(IAsyncResult AsyncResult)
            {
                ConnectSocket.EndSend(AsyncResult);

                MRE_SendClientData.Set();
            }//void SendClientDataCallBack(IAsyncResult AsyncResult)

            private ManualResetEvent MRE_Receive = new ManualResetEvent(false);
            private ManualResetEvent MRE_ReceiveServerDataQuantity = new ManualResetEvent(false);
            private ManualResetEvent MRE_ReceiveServerDataLength = new ManualResetEvent(false);
            private ManualResetEvent MRE_ReceiveServerData = new ManualResetEvent(false);
            public void Receive()
            {
                MRE_Receive.Reset();

                MRE_ReceiveServerDataQuantity.Reset();

                byte[] ServerDataQuantity = new byte[4];
                object[] ReceiveServerDataQuantityState = { ServerDataQuantity };
                ConnectSocket.BeginReceive(ServerDataQuantity, 0, ServerDataQuantity.Length, SocketFlags.None, ReceiveServerDataQuantityCallBack, ReceiveServerDataQuantityState);

                MRE_ReceiveServerDataQuantity.WaitOne();

                MRE_Receive.WaitOne();
            }//void Receive()

            private void ReceiveServerDataQuantityCallBack(IAsyncResult AsyncResult)
            {
                object[] ReceiveServerDataQuantityState = (object[])AsyncResult.AsyncState;
                byte[] ServerDataQuantity = (byte[])ReceiveServerDataQuantityState[0];
                ConnectSocket.EndReceive(AsyncResult);

                MRE_ReceiveServerDataQuantity.Set();

                long CycleIndex = 0;
                while (CycleIndex < BitConverter.ToInt32(ServerDataQuantity, 0))
                {
                    MRE_ReceiveServerDataLength.Reset();

                    byte[] ServerDataLength = new byte[4];
                    ConnectSocket.BeginReceive(ServerDataLength, 0, ServerDataLength.Length, SocketFlags.None, ReceiveServerDataLengthCallBack, null);

                    MRE_ReceiveServerDataLength.WaitOne();

                    byte[] ServerData = new byte[0];
                    if (BitConverter.ToInt32(ServerDataLength, 0) > 0)
                    {
                        MRE_ReceiveServerData.Reset();

                        ServerData = new byte[BitConverter.ToInt32(ServerDataLength, 0)];
                        ConnectSocket.BeginReceive(ServerData, 0, ServerData.Length, SocketFlags.None, ReceiveServerDataCallBack, null);

                        MRE_ReceiveServerData.WaitOne();
                    }

                    ServerDataList.Add(ServerData);

                    CycleIndex = CycleIndex + 1;
                }

                MRE_Receive.Set();
            }//void ReceiveClientDataQuantityCallBack(IAsyncResult AsyncResult)

            private void ReceiveServerDataLengthCallBack(IAsyncResult AsyncResult)
            {
                ConnectSocket.EndReceive(AsyncResult);

                MRE_ReceiveServerDataLength.Set();
            }//void ReceiveClientDataLengthCallBack(IAsyncResult AsyncResult)

            private void ReceiveServerDataCallBack(IAsyncResult AsyncResult)
            {
                ConnectSocket.EndReceive(AsyncResult);

                MRE_ReceiveServerData.Set();
            }//void ReceiveClientDataCallBack(IAsyncResult AsyncResult)

            private static ManualResetEvent MRE_Disconnect = new ManualResetEvent(false);
            public static void Disconnect()
            {
                if (ConnectSocket != null)
                {
                    MRE_Disconnect.Reset();

                    ConnectSocket.BeginDisconnect(true, DisconnectCallBack, null);

                    MRE_Disconnect.WaitOne();
                }
            }//void DisconnectConnect()

            private static void DisconnectCallBack(IAsyncResult AsyncResult)
            {
                ConnectSocket.EndDisconnect(AsyncResult);

                MRE_Disconnect.Set();
            }//void DisconnectCallBack(IAsyncResult AsyncResult)

        }//class AsyncTCP

        public static void TCP_Start(string ServerIP, int ServerPort)
        {
            AsyncTCP.Connect(ServerIP, ServerPort);
        }//void TCP_Start(string ServerIP, int ServerPort)

        public static List<byte[]> TCP(List<byte[]> ClientDataList)
        {
            AsyncTCP TCP = new AsyncTCP();
            TCP.ClientDataList = ClientDataList;
            TCP.Send();
            TCP.Receive();
            List<byte[]> ServerDataList = TCP.ServerDataList;

            return ServerDataList;
        }//List<byte[]> TCP(List<byte[]> ClientDataList)

        public static void TCP_End()
        {
            AsyncTCP.Disconnect();
        }//void TCP_End()

    }//class AsyncClient

}//namespace SinDynasty
10611

示例声明:1、表示示例中的代码为了便于理解,对于异步的代码个人感觉写得有点不太好,但还是就那样吧;2、无论是List ClientDataList还是List ServerDataList,其第一个元素表示的是一个byte[]类型的协议编号,否则会出错
示例流程:(对于如何建立服务端与客户端的连接,这里先暂且略过,这儿先讲数据传输)客户端获取List ClientDataList中所含元素的数量ClientDataQuantity,并发送给服务端---->服务端接收ClientDataQuantity,让服务端知道接下来要接收多少个数据---->客户端遍历List ClientDataList中的元素,并循环打包成 前4字节表示数据大小+后面的字节表示数据 的格式,并发送---->服务端根据之前客户端发来的ClientDataQuantity,循环接收客户端发来的数据包,接收单个数据包的方法是先接收4字节的数据获得接下来所需接收的数据大小,然后接收数据---->服务端每接收一个byte[]的数据,便将该数据添加到List ClientDataList---->当服务端获得完整的List ClientDataList后,便解析List ClientDataList中的第一个元素,获得协议编号---->服务端根据协议编号,利用反射的方法,获得所需调用的协议方法,并使用List ClientDataList和MethodInfo.Invoke()的方法获得List ServerDataList---->服务端安装之前客户端发送数据的方式,发送List ServerDataList---->客户端也按照之前服务端接收数据的方式,接收List ServerDataList

示例语义:其就是List ClientDataList和List ServerDataList中所含的数据及其排序,外加上在发送数据时最开始发送的ClientDataQuantity及ServerDataQuantity
示例语法:其就是在打包List ClientDataList和List ServerDataList时所采用的格式,前4字节表示数据大小+后面的字节表示数据
示例时序:短连接,服务端先调用TCP_Start的方法,建立用于监听的Socket ListenSocket,并绑定端口,及开始监听---->客户端创建用于连接的Socket ConnectSocket,并请求连接---->服务端开始接受客户端的连接请求---->服务端与客户端相互传输数据---->服务端调用Disconnect()的方法关闭与该客户端的连接---->客户端调用TCP_End的方法关闭与服务端的连接

110 Lihuazhang 将本帖设为了精华贴 02月26日 09:22
110

解惑,继续再接再厉,还能给大家答疑

3165

感谢科普!期待更新!

899

萌兽加油~~,棒棒的

899

accept函数返回-1 和执行成功的,可以单独讲下哦。

10611
899jiazurongyu 回复

6335

很实用的协议知识,对服务端协议测试起到了很好的科普作用,感谢!

9320

正准备入坑服务器测试~解惑

6469

感谢作者用心的扫盲,朴实的技术分享。
补充一点我的理解:
Socket就是客户端与服务器之间通信用的套接字。
主流编程语言都为网络编程提供了Socket API,基于此可以进行协议开发。

104

记得更新打赏二维码,你已经错过本轮的社区打赏

10611
sindynasty · #13 · 2017年03月20日 作者
104seveniruby 回复

我没想过要打赏啊╮(╯▽╰)╭

10611
sindynasty · #14 · 2017年03月20日 作者
104seveniruby 回复

这是之前在TesterHome游戏测试群里,jiazurongyu说要搞个手册,然后用了我https://testerhome.com/topics/6956这文章,我感觉那文章不好,就重新写了这篇文章

6907

厉害了,同时会C#和Java的啊。

10611
sindynasty · #16 · 2017年05月26日 作者

最近发现我这篇文章里对Socket的理解有误,具体的不说了,那是关于底层的东西,关键知道 Socket不是协议 就行了

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册