网络编程

网络分为内网(局域网)和外网,内网可以不需要网络,相同网线相连即可。外网就是上网浏览网页等。对于同一网吧的电脑或连接同一无线网的电脑,他们的内网ip是不同的,但是对外网他们的ip是同一个,如网吧的电脑与外部同一台电脑A聊QQ,A看他们的IP是一样的,而网吧电脑收到信息时会从外网的ip转到内网的ip。IP是指互联网协议地址,,地址是可以自动分配的,MAC地址在每个网卡出场的时候就有一个全球唯一的MAC地址

IP:每个设备在网络中的唯一标识。每台网络终端在网络中都有一个独立的地址,我们在网络中传输数据就是使用这个地址。但一台电脑可以有多个ip地址,ip也是变化的,是随机分配的,
本地回路地址:127.0.0.1
广播地址:255.255.255.255
端口号:每个程序在设备上的唯一标识
每个网络程序都需要绑定一个端口号,传输数据的时候除了确定发到哪台机器上,还要明确发到哪个程序。
端口号范围从0-65535
编写网络应用就需要绑定一个端口号,尽量使用1024以上的,1024以下的基本上都被系统程序占用了。
默认端口号:
DNS 域名解析服务:53
HTTP 超文本传输服务:80
HTTPS 加密的超文本传输服务:433
MYSQL数据库端口:3306
Redis数据库端口:6379
TCP服务端默认端口:8080
Nginx服务器的端口:8888
协议:为计算机网络中进行数据交换而建立的规则、标准或约定的集合。
UDP:面向无连接,数据不安全,速度快。不区分客户端与服务端。
TCP: 面向连接(三次握手),数据安全,速度略低。分为客户端和服务端。
三次握手: 客户端先向服务端发起请求, 服务端响应请求, 传输数据
第一次握手:客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
购买服务器的配置:
购买服务器(使用买的公共ip)–分组配置设置端口号–根据需求可以更改服务器端的电脑配置–在计算机命令里输入mstsc打开与远程的连接–输入服务器地址–也可修改本地资源详细信息里把本地的C盘等传过去–在服务端安装数据库软件–可网上下也可在传过去的本地盘里复制–在把数据备份复制过去–服务端代码里监听的ip是服务器私有ip,客户端监听的是公有ip–右键服务端发布–把发布的服务端复制到服务器–在服务器上打开服务端
在这里插入图片描述

TCP协议

//异步并解决粘包分包服务端代码(同步的可以使用线程解决)

    class Program
    {
        static MsgManager msgMng;
        static void Main(string[] args)
        {
         AsycConnect();
        }
        static void AsycConnect()
        {
            // 1,创建socket
            Socket ServerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //2,绑定ip跟端口号 
            ServerSocket.Bind(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 7788));//向操作系统申请一个可用的ip跟端口号 用来做通信
            //3,开始监听 (等待客户端连接)
            ServerSocket.Listen(100);//参数是最大连接数,只是设定参数的作用,为0时不限制个数                        
            ServerSocket.BeginAccept(AsycAcceptFunc, ServerSocket);//只有连接上才会进入回调函数,异步等待,不会暂停卡住后面的代码
            Console.ReadKey();
        }

        private static void AsycAcceptFunc(IAsyncResult ar)
        {
            Socket socket = ar.AsyncState as Socket;  //这个是传过来的上面那个服务端,如果服务器是全局变量就不需要这行代码了
            Socket clientSocket = socket.EndAccept(ar);//客户端是多个,不能用全局变量
           //连接上向客户端发送信息
            msgMng = new MsgManager();//信息管理类
            string message = "hello 欢迎你";
            byte[] data = Encoding.UTF8.GetBytes(message);
            clientSocket.Send(data);

            clientSocket.BeginReceive(msgMng.MsgByte, msgMng.startIndex, msgMng.RemainSize() , SocketFlags.None, ReceiveFunc, clientSocket);//解决粘包,分包
            // clientSocket.BeginReceive(dataBuffer, 0, 1024, SocketFlags.None, ReceiveFunc, clientSocket);//不解决时正常的代码
            socket.BeginAccept(AsycAcceptFunc, socket);//递归,又给了一次新的异步等待,从而实现无限连接

        }
             private static void ReceiveFunc(IAsyncResult ar)
        {
            Socket socket = ar.AsyncState as Socket;//这个是传过来的客户端
            try//解决客户端直接关闭的异常
            {
                int i = socket.EndReceive(ar);
                 msgMng.ReadMessage(i);//读取信息
                socket.BeginReceive(msgMng.MsgByte, msgMng.startIndex, msgMng.RemainSize(), SocketFlags.None, ReceiveFunc, socket);
                //不解决时的代码
                // string msg = Encoding.UTF8.GetString(dataBuffer, 0, i);
                // Console.WriteLine("我从客户端接收的信息是:" + msg);
                // socket.BeginReceive(dataBuffer, 0, 1024, SocketFlags.None, ReceiveFunc, socket);
            }
            catch(Exception e)
            {
                Console.WriteLine(e);
               if (socket != null)
                socket.Close();
            }
        }

//异步并解决粘包分包服务端拆分收到的信息

 class MsgManager
    {
        
       private  Byte[] msgByte = new Byte[1024];//太小的话会分包
        public Byte[] MsgByte//接收信息的字节数组
        {
            get { return msgByte; }
        }
        public int startIndex=0;//当前数组拥有的字节数

        public int RemainSize()//剩余的数组空间
        {
            return message.Length - startIndex;
        }
        /// <summary>
        /// startIndex在外面获得了收到的字节数,在这个函数中分条获取
        /// </summary>
        public void ReadMessage(int i)
        {
         startIndex += i;
            while (true)
            {
                string msg;
                if (startIndex <= 4) return;
                int count = BitConverter.ToInt32(message, 0);//只会解析前四个字节,因为int是4个字节
                if (startIndex - 4 >= count)
                {
                    msg = Encoding.UTF8.GetString(message, 4, count);
                    Console.WriteLine("解析出一条数据:" + msg);
                    Array.Copy(message, count + 4, message, 0, startIndex - count - 4);
                    startIndex -= count + 4;
                }
                else
                    break;
            }
        }
    }

//异步并解决粘包分包客户端代码

   class Program
    {
        static void Main(string[] args)
        {
            //1,创建socket
            Socket tcpClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //2,发起建立连接的请求
            IPAddress ipaddress = IPAddress.Parse("127.0.0.1");//可以把一个字符串的ip地址转化成一个ipaddress的对象
            IPEndPoint point = new IPEndPoint(ipaddress, 7788);
            tcpClient.Connect(point);

            //接收服务器的消息(可不写)
            byte[] data = new byte[1024];
            int length = tcpClient.Receive(data);//这里传递一个byte数组,实际上这个data数组用来接收数据
            string message = Encoding.UTF8.GetString(data, 0, length);//length返回值表示接收了多少字节的数据
            Console.WriteLine(message);

            while (true)
            {
                //向服务器端发送消息
                string message2 = Console.ReadLine();//读取用户的输入 把输入发送到服务器端
                tcpClient.Send(MessagePack.GetBytes(message2));//把字符串转化成字节数组,然后发送到服务器端 

              //第二种解决服务端异常的办法,在客户端主动关闭连接,但是服务端哪里要加个i>0的条件,不然一直接收空信息
               if (message2 == "c")
                {
                    tcpClient.Close();
                    return;
                }    
            }
            
                

        }
    }

//异步并解决粘包分包客户端包装发送的信息

class MessagePack
    {
        public static byte[] GetBytes(string s)
        {
            byte[] byte_1 = Encoding.UTF8.GetBytes(s);  //要发送的信息
            int count = byte_1.Length;  //这个信息有多少个字节
            byte[] byte_2 = BitConverter.GetBytes(count); //把字节个数存在数组里(因为是int类型,固定四个字节)
            byte[] byte_3 = byte_2.Concat(byte_1).ToArray();//把两个字节数组相连,前面是信息的字节个数,后面是信息内容(注意这两个数组存的内容不一样)
            return byte_3;
        }

    }

1.粘包:服务端与客户端没有约定好要使用的数据结构。Socket Client实际是将数据包发送到一个缓存buffer中,通过buffer刷到数据链路层。因服务端接收数据包时,不能断定数据包1何时结束,就有可能导致服务器读取数据包1和其后面的数据包。这种现象称为粘包
产生原因:如果发送的网络数据包太小,太快,消息没有被及时从缓存区取走,下次在取数据的时候可能就会出现一次取出多个数据包的情况
分包:数据包数据被分开一部分发送出去,服务端一次读取数据时可能读取到完整数据包的一部分,剩余部分被分批读取。
产生原因:可能是IP分片传输导致的,也可能是传输过程中丢失部分包导致出现的半包,还有可能就是一个包可能被分成了两次传输,在取数据的时候,先取到了一部分(还可能与接收的缓冲区大小有关系),总之就是一个数据包被分成了多次接收。
2.服务端:可同步连接Accept可异步连接BeginAccept/EndAccept,可同步接收Receive可异步接收BeginReceive/EndReceive
客户端:只能连接一个Connect,但可同步接收Receive可异步接收Receive可异步接收BeginReceive/EedReceive

UDP协议

//服务端
  class Program
    {
        private static Socket udpServer;
        static void Main(string[] args) {
            //1,创建socket
             udpServer = new Socket(AddressFamily.InterNetwork,SocketType.Dgram,ProtocolType.Udp);
            //2,绑定ip跟端口号
            udpServer.Bind( new IPEndPoint( IPAddress.Parse("127.0.0.1"),7788 ) );

            //3,接收数据
            Thread thread =new Thread(ReceiveMessage);
            thread.IsBackground = true;
            thread.Start();

            Console.ReadKey();
        }

        static void ReceiveMessage()
        {
            while (true)
            {
                EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
                byte[] data = new byte[1024];
                int length = udpServer.ReceiveFrom(data, ref remoteEndPoint);//这个方法会把数据的来源(ip:port)放到第二个参数上
                string message = Encoding.UTF8.GetString(data, 0, length);
                Console.WriteLine("从ip:" + (remoteEndPoint as IPEndPoint).Address.ToString() + ":" + (remoteEndPoint as IPEndPoint).Port + "收到了数据:" + message);
            }

        }
    }
//客户端
 class Program {
        static void Main(string[] args) {
            //创建socket
            Socket udpClient = new Socket(AddressFamily.InterNetwork,SocketType.Dgram, ProtocolType.Udp);

            while (true)
            {
                //发送数据
                EndPoint serverPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 7788);
                string message = Console.ReadLine();
                byte[] data = Encoding.UTF8.GetBytes(message);
                udpClient.SendTo(data, serverPoint);
            }


            udpClient.Close();
            Console.ReadKey();
        }
    }

1.解决方案资源管理器–右键解决方案工程名–属性–生成–选择路径–调试–生成,重新生成解决方案。若生成dll的路径选择的是Unity工程的文件夹中,后面在对代码做修改,直接点重新生成解决方案即可,可同步到Unity的工程中(放到Unity中的dll要改下框架Unity3.5.Net.Subset.base,并且为类库(.NET Framework))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值