TCP 学习笔记

Win + R   打开控制台输入CMD  打开小黑窗, 输入ipconfig  查询本机地址

 

TCP 学习笔记_第1张图片

“外网IP是全世界唯一的IP地址,仅分配给一个网络设备。而内网IP是由路由器分配给每一部内部使用的IP地址,而内网的所有用户都是通过同一个外网IP地址进行上网的,而内网的IP地址每个人的都不一样,Internet上的用户也无法直接访问到内网用户。简单来说呢,外网IP就是标示了您在整个互联网上的地址,就相当于小区的地址,而内网IP呢,就是标识着您在局域网里面的地址,也就是小区内的几栋几楼几号房子。

内网IP地址一般是192.168开头 ,连接到wifi或网线的每个设备都有自己的一个内网ip

外网可以理解为公司的总网,也就是牵的网线里的网络

而内网是由路由器为每个设备分配的ip

TCP 学习笔记_第2张图片

端口号就是用来决定将消息传给同一内网IP下的某个程序 

三次握手,四次挥手

TCP 协议比较稳定,需要与接收方建立连接,可以确保数据不会丢失,可以保证数据发送的是否正确,需得到接收方的返回,如果在一定时间内没有收到回应则会重新发送。

UDP 速度快,但是不稳定安全,不用建立连接 (只管发,不管接,可能会被拦截,或断掉)

三次握手: 建立连接

四次挥手:断开连接

 IPv6的出现是因为IPv4不够用了 

 TCP 学习笔记_第3张图片

 TCP 用Stream 流通信比较稳定,  创建服务端代码如下

using System.Net.Sockets;
using System.Net;
namespace TCP学习
{
    class Program
    {
        static void Main(string[] args)
        {
            StartServerAsync();
            Console.ReadKey();
        }
        static void StartServerAsync()
        {
            Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //本机IP: 192.168.1.195 /127.0.0.1(127永远代表本机IP)  //196不是固定的,重启后或一定时间后路由器会重新分配IP
            //IPAddress xxx.xxx.xxx.xxx  IPEndPoint xxx.xxx.xxx.xxx:port
            //IPAddress iPAddress = new IPAddress(new byte[] { 192, 168, 1, 195 });
            IPAddress iPAddress = IPAddress.Parse("192.168.1.195");//推荐
            IPEndPoint iPEndPoint = new IPEndPoint(iPAddress, 88);
            serverSocket.Bind(iPEndPoint); //绑定IP和端口号
            serverSocket.Listen(0);   //开始监听端口号,参数为监听队列长度,
                                      //Listen(10)指超过10个后的消息不接收,设置为0不限制

            //同步接收客户端的连接(旧方法)
            //Socket clientSocket = serverSocket.Accept(); // 接收一个客户端链接 程序在这里暂停,直到有客户端连接
            //向链接的客户端发送消息
            //string msg = "Hello你好";
            //byte[] msgB = System.Text.Encoding.UTF8.GetBytes(msg); //把字符串转为字节数组
            //clientSocket.Send(msgB);
            //dataBuffer = new byte[10240];

            //异步接收客户端的消息,不影响下面的代码执行//当接收到客户端消息时才调用回调方法,最后一个参数是回调方法的参数
            //clientSocket.BeginReceive(dataBuffer, 0, 1024, SocketFlags.None, ReceiveCallBack, clientSocket);


            //同步接收客户端的消息(旧方法)
            //byte[] msgC = new byte[1024];
            //int count = clientSocket.Receive(msgC); //用msgC储存接收的数据.//程序在这里暂停,等待客户端发送消息//返回值是接收到的数据的长度
            //string msgD = System.Text.Encoding.UTF8.GetString(msgC, 0, count); //把前count个的字节数组数据转为字符串                                                           //从0开始读取count个数据
            //Console.WriteLine(msgD);

            //Console.ReadKey();
            //clientSocket.Close(); //关闭的和客户端的一个连接
            //serverSocket.Close(); //关闭自身的连接,停服

            serverSocket.BeginAccept(AcceptCallBack, serverSocket);

            //到目前为止 服务器端已经可以处理多个客户端的连接,也可以处理多个来自同一个客户端的消息 最终处理来自多个客户端的多个消息
        }
        static byte[] dataBuffer = new byte[1024];

        static void AcceptCallBack(IAsyncResult ar)
        {
            Socket serverSocket = ar.AsyncState as Socket;
            Socket clientSocket = serverSocket.EndAccept(ar);
            string msg = "Hello你好";
            byte[] msgB = System.Text.Encoding.UTF8.GetBytes(msg);
            clientSocket.Send(msgB);
            clientSocket.BeginReceive(dataBuffer, 0, 1024, SocketFlags.None, ReceiveCallBack, clientSocket);
            serverSocket.BeginAccept(AcceptCallBack, serverSocket);
        }
        static void ReceiveCallBack(IAsyncResult ar)
        {
            Socket clientSocket = null;
            try    //try 异常捕捉是为了当客户端异常关闭(非正常关闭)时,让所持有的客户端被释放且无法用该客户端继续接收消息
            {
                clientSocket = (Socket)ar.AsyncState;
                int count = clientSocket.EndReceive(ar);  //客户端如果发送"" 是接收不到的,当客户端Close时,会收到客户端的空消息,个数为0
                //所以当客户端的消息个数为0时,客户端主动断开(正常关闭)了
                if (count == 0)
                {
                    clientSocket.Close();
                    return;
                }
                string msg = System.Text.Encoding.UTF8.GetString(dataBuffer, 0, count);
                Console.WriteLine("从客户端接收到数据:" + msg);
                clientSocket.BeginReceive(dataBuffer, 0, 1024, SocketFlags.None, ReceiveCallBack, clientSocket);
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                if (clientSocket != null)
                {
                    clientSocket.Close();
                }
            }
            finally
            {
               
            }
        }
    }
    
}

创建客户端代码如下

using System.Net.Sockets;
using System.Net;
namespace TCP客户端
{
    internal class Program
    {
        static void Main(string[] args)
        {
            //客户端只需要和服务端建立连接 ,不需要绑定Bind
            Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            clientSocket.Connect(new IPEndPoint(IPAddress.Parse("192.168.1.195"),88));
            //接收这部分和服务器端一样
            byte[] msgC = new byte[1024];
            int count = clientSocket.Receive(msgC); //接收服务端的消息,用msgC储存接收的数据.
                                                    //返回值是接收到的数据的长度
                                                    // 执行到Receive程序会暂停,直到接收到服务端的消息 
            string msgD = System.Text.Encoding.UTF8.GetString(msgC, 0, count); //把前count个的字节数组数据转为字符串
                                                                               //从0开始读取count个数据
            Console.WriteLine(msgD);

            while (true)
            {
                string str = Console.ReadLine();
                if (str == "Close")   //客户端发送Close 主动要求断开连接
                { 
                    clientSocket.Close();
                    return;
                }
                clientSocket.Send(System.Text.Encoding.UTF8.GetBytes(str));
            }
            Console.ReadKey();
            clientSocket.Close();
        }
    }
}

同步方式:Accept Connect   /   Receive

异步方式:BeginAccept  EndAccept  /    BeginReceive    EndReceive  

效果如下,多客户端连接一个服务器TCP 学习笔记_第4张图片

粘包和分包

粘包 :当发送数据比较频繁,且数据量小,TCP  在你点发送时不会立马把数据发出去,而是等很多条数据达到一定量整合到一起发出去,服务器端或客户端执行一次Receive就可以收到多条消息的整合,如果点一次发一次会造成数据传输性能开销,

TCP 学习笔记_第5张图片

 粘包虽然会粘在一起,但是到达的先后顺序是不会变的

分包: 当数据量很大,上千上万的数据,TCP会分几次发送,大包占用网速,运送慢, 占用时间长,发送失败还要重新发送,服务器端或客户端执行一次Receive可能收到的不是一个完整的消息,而是被分割的消息段

我们定义的数组是1024字节,所以一次消息只能发1024字节数据,数据超过了就要分包发送,

可以加大每次发送的字节容量大小来避免分包(前提是异步,如果是同步,即使容量大也会分包),不过一般不考虑,很少有一次会发送那么大的数据,应该考虑的是粘包问题,因为游戏交互每次发送数据的量很小。次数也多

TCP 学习笔记_第6张图片

 处理粘包问题:

一个字符占一个字节 ,一个空格占用一个字节 ,一个汉字占3个字节

  int count = 15686;
            byte[] data = BitConverter.GetBytes(count);//支持值类型参数
            foreach (byte b in data)
            {
                Console.Write(b + ":");  //可以确保数据头只占用int32  4个字节
            }

TCP 学习笔记_第7张图片

你可能感兴趣的:(学习,笔记)