socket应用之从电脑发送图片到手机(1)之通信过程建立

news2024/11/25 20:44:58

本人曾经做了一个基于MPVd的C#开发的播放器,用于自娱自乐,后来又用websocket 写了个简单的远程控制器。由于websocket 要依赖于浏览器,因此有诸多不便,后来又用flutter写了一个,方便多了。
下面介绍具体实现。

1、通信机制建立

1.1 基本说明:

服务端代码为C#,客户端为Flutter(dart)

服务端:即播放器端(PC端)
客户端:即遥控端(手机端),目前只支持一个手机
通信方式:UDP和TCP

第一步:获得各自host的IP地址
第二步:服务端开启UDP 广播监听和TCP监听到指定端口
第三步:客户端发送广播索取服务端IP地址
第四步:服务端接收到索取IP地址命令,然后发送服务端IP地址到客户端
第五步:客户端根据返回的IP地址建立和服务端TCP 连接(socket)

1.2代码示例

1.2.1 获得IP地址

服务端代码:

       private string getLocalIP()
        {
            string localIP = string.Empty;
            using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, 0))
            {
                socket.Connect("8.8.8.8", 65530);
                IPEndPoint endPoint = socket.LocalEndPoint as IPEndPoint;
                localIP = endPoint.Address.ToString();
            }
            return localIP;
        }

客户端代码:
Note:这里是获得局域网的WIFI IP,这个显然对于大多数用户是正确的。手机不可能用有线方式也不可能使用WALN的IP,因此只获得WIFI IP, 没有WIFI无法使用。另外笔者并没有找到获得WIFI的优雅方法,是采用了搜索的方法获得的,感觉有瑕疵。

....
import 'dart:io';
.....
Future GetLoalWfiIP() async {
    for (var interface in await NetworkInterface.list()) {
      if (interface.name.toUpperCase().contains("WLAN")) {
        //print(interface.addresses.first.address);
        m_localIP = interface.addresses.first.address;
        return;
      }

      //   print('== Interface: ${interface.name} ==');
      //   for (var addr in interface.addresses) {
      //     print(
      //         '${addr.address} ${addr.host} ${addr.isLoopback} ${addr.rawAddress} ${addr.type.name}');
      //   }
      // }
    }
  }

1.2.2 开启监听

UDP监听:
Note:代码中包含了笔者定义的简单通信协议

public void SetUDPSvr()
        {
            try
            {
                Thread receThread = new Thread(new ThreadStart(RecvUDPThread));
                receThread.IsBackground = true;
                receThread.Start();
            }catch(Exception ex)
            {
                System.Windows.Forms.MessageBox.Show(ex.Message);
            }
        }
        // 实际监听在这里,采用了定义好的端口 8090,这个端口是要服务器和客户端实现协商好。
        private void RecvUDPThread()
        {
            m_udpClient = new UdpClient(new IPEndPoint(IPAddress.Any, 8009));
            //广播地址监听
            IPEndPoint endpoint = new IPEndPoint(IPAddress.Any, 0);
            //m_udpClient.Client.ReceiveTimeout = 1000*60;
            m_Stopped = false;
            while (!m_Stop)
            {
                try
                {
                    byte[] buf = m_udpClient.Receive(ref endpoint);
                    string msg = Encoding.Default.GetString(buf);
                    //parser the msg 
                    //if(msg == CMD_ICIP)
                    {
                        var rmtIP = endpoint.Address.ToString();
                        var rmtPort = endpoint.Port;
                        // send the server IP to the remote , simple protocol: ISIP:###.###.#.#
                        var sndMsg = string.Format("{0}:{1}{2}", appCfg.m_SVR_CMD_ISIP, m_HostIP,appCfg.m_CMD_SEND_END_LBL);
                        endpoint.Port = 8008;

                        buf = Encoding.Default.GetBytes(sndMsg);
                        m_udpClient.Send(buf, sndMsg.Length, endpoint);


                    }
                    //The simple protocol:IL:IP ask for the IP address of the host , IL is the indicator 
                    var strReceive = string.Format((endpoint as IPEndPoint).Address.ToString() + ":" + (endpoint as IPEndPoint).Port.ToString());
                    Console.WriteLine(strReceive);
                }catch(SocketException e)
                {
                    // do nothing 
                    if (e.SocketErrorCode == SocketError.TimedOut)
                    {
                        //do nothing 
                        Console.WriteLine(e.Message);
                    }
                    else
                    {
                        Console.WriteLine(e.Message);
                    }

                }
            }
            m_Stopped = true;
        }

1.2.3 客户端UDP方式索取服务端IP地址

Future<void> getSvrIPViaUdp() async {
    await RawDatagramSocket.bind(InternetAddress.anyIPv4, AppCfg.m_UdpSvrPort)
        .then((RawDatagramSocket socket) async {
      await GetLoalWfiIP(); // get m_localIP
      // 这里首先获得手机IP,然后将最后一位改写为255,生成一个子网内的广播地址。
      var pos = m_localIP.lastIndexOf(".");
      var udpIP = "${m_localIP.substring(0, pos + 1)}255";
      var updAdr = InternetAddress(udpIP);
      var sendStr = "Hello,I am here.";
      List<int> bytes = utf8.encode(sendStr);
      //m_RcvDat.clear();
      socket.broadcastEnabled = true;
      //建立监听事件,发送广播的命令在后面。
      socket.listen((RawSocketEvent e) {
        Datagram? d = socket.receive();
        if (d == null) {
          return;
        }

        String message = String.fromCharCodes(d.data).trim();
        //print('Datagram from ${d.address.address}:${d.port}: $message');
        // get the IP of the server
        var keyWord = "${AppCfg.m_SVR_CMD_ISIP}:";
        var len = keyWord.length;
        var endPos = message.lastIndexOf(AppCfg.m_CMD_SEND_END_LBL);
        //获得服务端传送过来的IP,记录下来。
        if (endPos >= 0) {
          AppCfg.m_TcpSvrIP = message.substring(len, endPos);
        }
        //print(m_svrIP);
      });
      // 发送广播
      socket.send(bytes, updAdr, AppCfg.m_UdpClntPort);
    });
  }

1.2.4 服务端发送IP地址到客户端

此代码在上述1.2.2 开启监听代码块中,如图示
在这里插入图片描述

1.2.5 客户端和服务端建立TCP连接

Future<void> setupConnection() async {
    if (m_isConnect) return;
    //await getSvrIPViaUdp();
    //索取服务器IP地址
    while (AppCfg.m_TcpSvrIP == "") {
      await getSvrIPViaUdp();
      await Future.delayed(const Duration(milliseconds: 100), () {
        print("Waiting for getting m_srvip.....\r\n");
      });
    }
    //获得IP地址后,建立和服务端的TCP 连接
    if (AppCfg.m_TcpSvrIP != "" && !m_isConnect) {
      m_socket = await Socket.connect(AppCfg.m_TcpSvrIP, AppCfg.m_TcpSvrPort);
      m_isConnect = true;
      //await for the first image sent from the server
      m_socket.listen(_receivedMsgHandler,
          onError: _errorHandler, onDone: _doneHandler, cancelOnError: false);
    }
  }

接收处理请看后续文章。

MaraSun BJFWDQ
兔年将至,预祝各位程序猿春节快乐!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/143781.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

RabbitMQ 消息持久化

RabbitMQ 消息持久化 持久化是为提高rabbitmq消息的可靠性&#xff0c;防止在异常情况(重启&#xff0c;关闭&#xff0c;宕机)下数据的丢失。设置完队列和消息的持久化&#xff0c;并不能完全保证消息不会丢失。尽管它告诉 RabbitMQ 将消息保存到磁盘&#xff0c;但当 Rabbit…

mapbox-gl添加threejs飞线

文章目录前言飞线实现1 初始化地图并加载three图层2 绘制飞线几何体将几何体正确定位在mapbox上正确操作BufferGeometry几何体3 tween实现动画全部代码总结待改进之处参考前言 mapbox-gl是一个基于webgl开发的三维地图渲染引擎&#xff0c;但是很多三维特效只用mapbox并不容易…

【CSDN 年终总结】CSDN的进阶之路—— “1+1=王”的2022总结

正文之前打个广告&#xff0c;我正在参加年度博客之星评选&#xff0c;请大家帮我投票打分&#xff0c;您的每一分都是对我的支持与鼓励。⭐ ⭐ ⭐ ⭐ ⭐https://bbs.csdn.net/topics/611386885?spm1001.2014.3001.6953 2022我在CSDN 2022 在CSDN是持续输出&#xff0c;持续…

TinyPng图片压缩的正确打开方式

https://tinypng.com/ TinyPNG使用智能的「有损压缩技术」来减少WEBP、JPEG和PNG文件的文件大小。通过选择性地减少图像中的「颜色数量」&#xff0c;使用更少的字节来存储数据。这种效果几乎是看不见的&#xff0c;但在文件大小上有非常大的差别。 使用过TinyPNG的都知道&…

MyBatis 万字长文:从入门到动态SQL超详细

文章目录1. 前言2. 创建项目3. 添加框架支持4. 建库5. 配置数据库连接信息和 XML 文件路径5.1 创建 Java 类5.2 Java 接口5.3 XML 文件6. 查询6.1 不带参数的查询6.2 单元测试6.3 带参数的查询7. 修改8. 增加8.1 将对象插入表中8.2 获取自增主键9. 删除10. 数据库字段和类属性名…

Video2StyleGAN: Disentangling Local and Global Variations in a Video翻译

点击下载论文 代码地址 摘要 使用预训练的StyleGAN生成器进行图像编辑已成为面部编辑的强大范例&#xff0c;它提供了对年龄、表情、照明度等的解纠缠控制。然而&#xff0c;该方法不能直接用于视频操作。我们认为主要因素是缺乏对面部位置、面部姿势和局部面部表情的精细和清…

腾讯云-云服务器购买流程-Java项目部署(详细的一批)

文章目录云服务器购买云服务搭建部署环境宝塔面板使用&#xff08;安装所需环境&#xff09;部署SpringBoot项目出现Error: Unable to access jarfile /www/wwwroot/xxxx.jar--server.port6066 问题解决腾讯云COS有什么用&#xff1f;如果感觉有用就一键三连吧&#xff0c;创作…

Electron 实现切换暗_亮模式与主题

文章末尾附上仓库地址&#xff01;&#xff01;&#xff01;&#xff01; 清单 模板基于 electron-vite-vue vue3 ts vite组件库 element-plushooks库 vueuse 、useElementPlusTheme 初始化工程 使用 electron-vite 作为模板&#xff0c;方便大家尽快吧项目跑起来 # 创建模…

Java面试之数据库篇

一、基础 1.数据库事务的特征ACID 原子性&#xff08;Atomicity&#xff09;&#xff1a;原子性是指事务包含的所有操作要么全部成功&#xff0c;要么全部失败回滚&#xff0c;这和前面两篇博客介绍事务的功能是一样的概念&#xff0c;因此事务的操作如果成功就必须要完全应用…

UOS服务器操作系统多版本Java切换

一、修改java的环境变量和软链接来实现版本切换 1、配置环境变量 sudo vim &#xff5e;/.bashrc 2、创建java运行程序软连接 3、使配置生效&#xff0c;并检查java版本 source /etc/profile 二、使用update-alternatives 进行版本的切换 1、同时安装了openjdk-8-jdk 和…

shell第七天作业——awk

题目 1、获取根分区剩余大小 2、获取当前机器ip地址 3、统计出apache的/var/log/httpd/access_log文件中访问量最多的前3个IP 4、打印/etc/passwd中UID大于500的用户名和uid 5、/etc/passwd 中匹配包含root或sys或tcp的任意行 6、请打印出/etc/passwd 第一个域&#xff0…

指针进阶之数组参数和指针参数

文章目录一、回顾1.字符指针2.指针数组和数组指针&#xff08;1&#xff09;指针数组&#xff08;2&#xff09;数组指针二、数组参数1.一维数组传参&#xff08;1&#xff09;整型数组&#xff08;2&#xff09;指针数组&#xff08;3&#xff09;总结2.二维数组传参&#xff…

基于Python tensorflow2.3实现的水果识别系统源码+模型+数据集,卷积神经网络的入门案例

水果识别-基于tensorflow2.3实现 水果识别是卷积神经网络的入门案例&#xff0c;这里我将模型的训练、测试、保存以及使用整合在了一起&#xff0c;至于原理部分&#xff0c;大家可以参考知乎或者B站上的回答&#xff0c;在这里我就不赘述了 完整代码下载地址&#xff1a;基于…

计算机网络实验---验证性实验

实验一/ipconfig 实作一 实作二 实验二/ping 实作一 实作二 实验三/tracert 实作一 实作二 实验四/ARP 实作一 实作二 实作二 实验五/DHCP 实作一 实验六/netstat 实作一 实作二 实验七/DNS 实作一 实作二 实作二 实验八/cache 实作一 实作二 总结 实验一/ipconfig 实…

[Leetcode] 二叉树的遍历

转载自&#xff08;有删减和少量改动&#xff09; 图解二叉树的四种遍历 https://leetcode.cn/problems/binary-tree-preorder-traversal/solution/tu-jie-er-cha-shu-de-si-chong-bian-li-by-z1m/1. 相关题目144.二叉树的前序遍历 https://leetcode.cn/problems/binary-tree-p…

【SpringMVC 入门教程】

SpringMVC_day02 &#x1f308;博客主页&#xff1a;屠一乐的博客 &#x1f4c5; 发文时间&#xff1a;2023.1.5 &#x1f388; 一定存在只有你才能做成的事 &#x1f339; 博主水平有限&#xff0c;如有错误&#xff0c;欢迎指正 欢迎各位&#x1f44d;收藏&#x1f48e;评论✉…

MacBookPro安装mysql遇到的几个问题

用Mac的好处是不用开关机&#xff0c;无弹窗无广告&#xff0c;坏处是在安装某些第三方的软件时&#xff0c;总是和视频教程上的winows版不一致&#xff0c;需要自己上网找资料尝试怎么安装。今天学python&#xff0c;需要安装mysql&#xff0c;幸好网上有一些文章&#xff0c;…

Vulnhub靶机:MISDIRECTION_ 1

目录介绍信息收集主机发现主机信息探测网站探测反弹shell方式1&#xff1a;使用nc方式2&#xff1a;使用bash方式3&#xff1a;使用MSF提权sudo提权passwd提权docker提权参考介绍 系列&#xff1a;Misdirection&#xff08;此系列共1台&#xff09; 发布日期&#xff1a;2019 …

【ClickHouse】从Mysql迁移到ClickHouse大全

从关系型的数据库(Mysql)升级到列式管理的联机分析型数据库(ClickHouse)&#xff0c;这不亚于是小米加步枪升级为加特林机关枪的性能提升了&#xff0c;查询能力等确实是大大的提升了&#xff0c;这出现了一个问题我们之前存储在Mysql里的历史数据怎么往ClickHouse里面迁移呢&a…

访问者模式Visitor

1.意图&#xff1a;表示一个作用于某对象结构中的各元素的操作。它允许在不改变各元素的类的前提下定义作用于这些元素的操作。 2.结构 Visitor&#xff08;访问者&#xff09;为该对象结构中ConcreteElement的每一个类声明一个Visit操作。该操作的名字和特征标识了发送Visit请…