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

本人曾经做了一个基于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;
        }

TCP 监听和接收

//开启监听
 public void SetupTCPSvr()
        {
            //服务器IP地址IPAddress ip = IPAddress.Parse("127.0.0.1");
            m_SvrSocket.ReceiveTimeout=1000;
            try
            {
                m_SvrSocket.Bind(new IPEndPoint(m_IP, m_Port));  //绑定IP地址:端口
                m_SvrSocket.Listen(10);    //设定最多10个排队连接请求
                                           //Console.WriteLine("启动监听{0}成功", m_SvrSocket.LocalEndPoint.ToString());
                                           //通过Clientsoket发送数据
                Thread myThread = new Thread(ListenTcpConnect);
                myThread.Start();
                Console.ReadLine();
            }catch(Exception ex)
            {
                System.Windows.Forms.MessageBox.Show(ex.Message);
            }
        }
       //接收线程(回调函数(代理))
      private void ListenTcpConnect()
        {
            m_Stopped = false;
           
                while (!m_Stop)
            {
                try
                {
                    Socket clientSocket = m_SvrSocket.Accept();
                    var message = string.Format("{0}:{1}", appCfg.m_RMT_CMD_ID, appCfg.m_RMT_CMD_NewSkt);
                    RemoteCmd remoteCmd = new RemoteCmd(message, clientSocket);
                    m_videoAdapter.AddCmdToRmtQue(remoteCmd);
                    //clientSocket.Send(Encoding.ASCII.GetBytes("Server Say Hello"));
                    Thread receiveThread = new Thread(ReceiveMessage);
                    receiveThread.Start(clientSocket);
                    m_ClientSktLst.Add(clientSocket);
                }
                catch (Exception e)
                {
                    //do nothing 
                    Console.WriteLine("ListenTcpConnect..." + 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
兔年将至,预祝各位程序猿春节快乐!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值