• 【光学相机】关于网络通信的方法及函数


    一.TcpClient与TcpServe。

    首先我们需要知道服务器的IP地址,在服务器端建立监听,当监听到客户端的连接请求后,连接到客户端。

    而客户端则需要连接到指定的IP服务器地址,建立网络流,则可以实现通信。

    接下来给出一个服务器端与客户端的实例:

    服务器端:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Net;
    using System.Net.Sockets;
    
    namespace fuwuqi
    {
        class Program
        {
            static void Main(string[] args)
            {
                try
                {
                    IPAddress Ip = IPAddress.Parse("127.0.0.1");
                    //IPAddress提供网际协议地址的类,Parse将IP地址字符串转换为IPAddress的实例   
                    TcpListener TcpList = new TcpListener(Ip, 8888);
                    //TcpListener网络侦听类,从TCP网络客户端侦听连接,TcpListener (参数1,参数2),参数1表示本地IP地址,参数2表示用来侦听传入的端口连接   
                    TcpList.Start();
                    //启动对挂起连接数的传入链接请求的侦听   
                    Console.WriteLine("Server start!");
                    Console.WriteLine("Ip address:" +TcpList.LocalEndpoint);
                    //LocalEndpoint获取服务端(即本地)地址与端口等信息   
                    Console.WriteLine("Wait");
                    Socket Soc = TcpList.AcceptSocket();//相当于启动客户端后,Socket被挂起
                    //AcceptSocket表示接受挂起的连接请求,Socket为套接字接口的类   
                    Console.WriteLine("Received Connection:" + Soc.RemoteEndPoint);
                    //RemoteEndPoint获取客户端地址与端口等信息   
                    byte[] b = new byte[100];
                    int k = Soc.Receive(b);
                    //Soc.Receive(b)从socket接收数据,将数据存入接收缓冲区列表中,k的值为该数据的长度   
                    Console.WriteLine("Received data from client:");
                    for (int i = 0; i < k; i++)
                        Console.Write(Convert.ToChar(b[i]));
                    //Convert.ToChar(b[i])将数组b转换基本数据类型为char的类型并输出   
                    ASCIIEncoding AS = new ASCIIEncoding();
                    //ASCIIEncoding表示Unicode字符的ASCII字符编码类   
                    Soc.Send(AS.GetBytes("Received data!"));
                    //Soc.Send向客户端发送数据,AS.GetBytes()获得括号中字符串的bytes值   
                    Soc.Close();
                    //关闭连接并释放所有关联的资源   
                    TcpList.Stop();
                    //关闭侦听   
                    Console.ReadLine();
                    //等待输入,起到暂停的作用   
                }
                catch (Exception e)
                {
                    Console.WriteLine("Error!" + e.StackTrace);
                    //获取当前异常发生时调用堆栈上的帧的字符串   
                }
            }
        }
    }

    此时服务器端应用的是Socket类发送数据流,Socket类用于客户端和服务器都可,其发送机制是以一种包的形式发送,在做项目时,发现Socket接收到的数据包与发送到的数据包长度不一定吻合,有阻塞,所以使用Socket数据包发送时,接收和发送的数据组要指定长度,太长也会丢数。

    客户端:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.IO;
    using System.Net.Sockets;
    namespace kehuduan
    {
        class Program
        {
            
                static void Main(string[] args)  
            {
                    try
                    {
                        TcpClient TcpClient = new TcpClient();
                        //TcpClient为TCP网络服务提供客户端连接的类   
                        TcpClient.Connect("127.0.0.1", 8888);
                        //Connect方法使用指定的IP地址和端口号将客户端连接到远程TCP主机   
                        Console.WriteLine("Connect Complete");
    
                        Console.WriteLine("Input string:");
                        String str = Console.ReadLine();
                        //定义字符串str变量,保存输入的字符   
                        Stream stm = TcpClient.GetStream();
                        //定义数据流,用于发送和接收数据   
                        ASCIIEncoding AS = new ASCIIEncoding();
                        byte[] b = AS.GetBytes(str);
                        //将字符串转换为byte类型   
                        stm.Write(b, 0, b.Length);
                        //Write(参数1,参数2,参数3)表示向服务端发送字符串,参数1指将此数组复制到当前流,参数2指从零开始的字节偏移量,参数3指要写入当前流的字节数(即字符串长度)   
                        Console.WriteLine("Send Complete");
                        byte[] bb = new byte[100];
                        int k = stm.Read(bb, 0, 100);
                        //stm.Read在当前流中读入服务端发来的响应信息,其参数与Write方法参数一致,k值为读入字符串的长度   
                        for (int i = 0; i < k; i++)
                            Console.Write(Convert.ToChar(bb[i]));
                        TcpClient.Close();
                        //释放TcpClient实例,并不关闭基础连接   
                        Console.ReadLine();
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine("Error!" + e.StackTrace);
                    }
                }
            }
        }

    上面展示了客户端与服务器端的简单通信。作为例子程序学习,接下来对自己做的项目中连接摄像头服务器做一个记录。

    首先,把摄像头作为服务器端,定义客户端:

       TcpClient client = new TcpClient("IP",8080);

    获取网络流:

      NetworkStream networkstream = client.GetStream();//获取网络流

    此时可以建立与服务器端的网络流连接,如果需要从网络流中读取数据或者写入数据,使用了BinaryReader/BinaryWriter或者StreamReader/StreamWriter,实例化后,就可实现对流的读写

    BinaryReaderbr = new BinaryReader(networkstream);
    BinaryWriter bw = new BinaryWriter(networkstream);
    我对这两个的理解是StreamReder读取函数ReadToEnd,ReadLine等,读取出来是字符串类型。而BinaryReader可以按字节读取。根据需求选取。

    (笔记StreamReader的方法: sr.BaseStream.Seek(0, SeekOrigin.Begin);//从流中当前开始的位置(SeekOrigin.begin)偏移0位读取)

    如果需要对服务器发送数据直接写入流,使用Write等函数就可以了。

    这里对自己找的资料也做一个总结。

    MemoryStream类:

    Memmory类相当于一个缓存流,首先需要对这个流开辟一个空间,然后将数据放入流中对其进行操作。该流的优势在于可以将数据先放入流中,然后使用指针对流中的任一个字节进行操作。

    学习例子:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.IO;
    
    
    namespace ConsoleApplication1
    {
        class Program
        {
            static void Main(string[] args)
            {
                //属性测试
                MemoryStream ms = new MemoryStream();
                Console.WriteLine(ms.CanRead);      //True  内存流可读
                Console.WriteLine(ms.CanSeek);      //True  内存流支持查找,指针移来移去的查找
                Console.WriteLine(ms.CanTimeout);   //False 内存流不支持超时
                Console.WriteLine(ms.CanWrite);     //True  内存流可写
    
                Console.WriteLine(ms.Capacity);     //0     分配给该流的字节数
                byte[] bytes = Encoding.UTF8.GetBytes("abcdedcba");//字符转为ASCII
                ms.Write(bytes, 0, bytes.Length);   //已将一段文本写入内存
                int l = bytes.Length;
                Console.WriteLine(ms.Capacity);     //256   再次读取为文本流分配的字节数已经变成了256,看来内存流是根据需要的多少来分配的
                Console.WriteLine(ms.Length);       //9    这个是流长度,通常与英文的字符数一样,真正占用的字节数。
    
                Console.WriteLine(ms.Position);     //9    流当前的位置,该属性可读可设置
    
                //Console.WriteLine(ms.ReadTimeout);    由于流不支持超时,此属性如果读取或者设置的话会报错
                //Console.WriteLine(ms.WriteTimeout);   由于流不支持超时,此属性如果读取或者设置的话会报错
    
                //方法测试
                
                byte[] byte1 = ms.GetBuffer();          //返回无符号字节数组 差点被忽悠了,无符号字节数组 其实就是byte(0~255),有符号字节sbyte(-128~127)
                string str1 = Encoding.UTF8.GetString(byte1);
                Console.WriteLine(str1);    //输出    abcdedcba
                long p = ms.Position;//指针当前位置
                ms.Position = 0;
                ms.Seek(0, SeekOrigin.Current);    //设置当前流正在读取的位置 为开始位置即从0开始
                //从内存中读取一个字节
                int i = ms.ReadByte();
                Console.WriteLine(i);                   //输出99
                byte[] bytes3 = ms.ToArray();
                foreach (byte b in bytes3)
                {
                    Console.Write(b + "-");//用于对比   输出 97-98-99-100-101-100-99-98-97-   可以看到    0,1,2第二位刚好是99
                }
    
                MemoryStream ms2 = new MemoryStream();
                byte[] bytes6 = Encoding.UTF8.GetBytes("abcde");
                ms2.Write(bytes6, 0, bytes6.Length);
                Console.WriteLine(ms2.Position);    //输出5 写完之后流的位置就到了最后,因此想用read读取必须加下面这一行代码。 
    
                //ms2.Seek(0, SeekOrigin.Begin);    //想要用Read方法读取完整的流,必须设置当前位置,Read是从Position的位置开始读。
                ms2.Position = 0;                   //Read是从当前位置开始读,这行代码和上面一行意义一样。
                byte[] byteArray = new byte[5] { 0, 0,0, 0, 0 }; //数组附初值全部是0
                ms2.Read(byteArray, 2, 1);   //读取一个字节,byteArray的第一个元素中,(注意从0开始)读取数据流里第二个位子开始读取一个
                Console.WriteLine(Encoding.UTF8.GetString(byteArray)); //nnann
                ms2.Read(byteArray, 2, 2);
                Console.WriteLine(Encoding.UTF8.GetString(byteArray)); //nnabn    //当超出接收数组总长度的时候,后面的元素会被移开
    
    
                //设置当前流的长度
                Console.WriteLine(ms.Length);   //输出9   当前流的长度是9
                ms.SetLength(20);
                Console.WriteLine(ms.Length);   //输出20
                foreach (byte b in ms.ToArray())    //将流的内容也就是内存中的内容转换字节数组
                {
                    Console.Write(b + "-");     //输出 97-98-99-100-101-100-99-98-97-0-0-0-0-0-0-0-0-0 由于设置了长度,因此空的自动补0
                }
                Console.WriteLine(Encoding.UTF8.GetString(ms.ToArray()));   //输出    abcdedcba   虽然长度变长了,但是没影响读取数据
    
                MemoryStream ms1 = new MemoryStream();
                byte[] bytes4 = ms1.ToArray();
                Console.WriteLine("此内存流并没有写入数据(Write)" + Encoding.UTF8.GetString(bytes4));//输出    此内存流并没有写入数据(Write)  因为内存为空
    
    
                //下面来一个指定位置的写入
                MemoryStream ms3 = new MemoryStream();
                byte[] bytesArr = Encoding.ASCII.GetBytes("abcdefg");
                ms3.Write(bytesArr, 0, bytesArr.Length);
                ms3.Position = 2;
                ms3.WriteByte(97);  //97代表的是a   这段代码的意思是,将原先第二个的c替换为a
                string str = Encoding.ASCII.GetString(ms3.ToArray());
                Console.WriteLine(str); //输出 abacdefg
    
                byte[] byteArr1 = Encoding.ASCII.GetBytes("kk");
                ms3.Position = 4;
                ms3.Write(byteArr1, 0, byteArr1.Length);
                Console.WriteLine(Encoding.UTF8.GetString(ms3.ToArray()));  //abadkkg   //从第4位替换掉了两个字节为KK
    
                Console.ReadKey();
            }
        }
    }
  • 相关阅读:
    Java LinkedList 源码剖析
    Java并发编程:线程池的使用
    Java 线程池的原理与实现
    多线程JAVA篇(一)
    软件开发中会用到的图
    linux文件名匹配——通配符使用
    XModem协议
    dmesg 命令七种用法
    定位精度单位CEP、RMS、2DRMS常识
    5G NR 技术简介
  • 原文地址:https://www.cnblogs.com/liyulongBlog/p/7998153.html
Copyright © 2020-2023  润新知