• 【Win 10 应用开发】TCP通信过程


    基于TCP协议的通信,估计大伙儿都不陌生的,以前玩.net或玩C++的时候应该玩得很多吧。现在老周简单介绍一下在RT中如何用。

    TCP是基于连接的,所以,肯定有一方是监听者,通常称服务端或服务器,它负责接受连接请求,但不负责通信;接受连接后得到一个专用于通信的套接字。

    1、new一个StreamSocketListener,它用于监听TCP连接。

    2、处理StreamSocketListener实例的ConnectionReceived事件,当有新连接传入,会发生该事件,并可以获得用于通信的socket。

    3、绑定本地结点。BindEndpointAsync绑定特定本机地址和端口(或服务,如果是蓝牙通信,就是服务名,大多数情况下是端口号)。BindServiceNameAsync方法绑定本地端号或服务,该方法不指定地址,即绑定本机所有地址,如果有需要,你可以指定绑定到哪张网卡。如果所指定的端口是空白字符串("",不能为null),就会自动选择一个随机端口进行绑定。要是绑定的是本地的随机端口,你可以从StreamSocketListener.Information的LocalPort属性中获取已绑定的端口。

    4、在StreamSocketListener.ConnectionReceived事件的处理中,访问事件参数的Socket属性得到一个StreamSocket实例,然后你就可以用它来进行通信了。

    5、当不需要时调用Dispose方法即可释放。

    下面来练习一下。老周发现一个现象,UWP两个应用程序在同一台机器上不能连接,要用两台机器来测试,但在同一个应用中就可以本地测试。

    不过,后来想想,其实也无妨,毕竟UWP是通用应用,如果服务器一个应用,客户一个应用,这样反而不合理了,因为这样用户就要安装两个应用,在通用平台而言不太好,把服务器和客户端都放在同一个应用中较好,让用户自行选择是作为服务器端还是客户端来运行。如果用户选择当前应用作为服务器,就开启监听;如果用户选择作为客户端运行,就允许其输入远程设备的IP和端口进行连接。

    下面代码开启连接监听并绑定机地端口。

                if (listener != null)
                {
                    listener.ConnectionReceived -= OnConnReceived;
                    listener.Dispose();
                    listener = null;
                }
    
                listener = new StreamSocketListener();
                listener.ConnectionReceived += OnConnReceived;
                await listener.BindServiceNameAsync("");

    调用BindServiceNameAsync时传递的是空字符串的参数,表示让应用程序自动选择一个随机端口来监听。为了让客户端知道该连接哪个端口,可以把本地监听端口显示在界面上。

      runPort.Text = listener.Information.LocalPort;

    处理ConnectionReceived事件,如果接收到连接请求,就向客户端发送一条文本消息:“你好,我是你外公,我叫服务器。”。

            private async void OnConnReceived(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
            {
                // 获取用于通信的socket
                StreamSocket socket = args.Socket;
                // 向客户端发送字符串:
                //        你好,我是你外公,我叫服务器。
                using (DataWriter writer = new DataWriter(socket.OutputStream))
                {
                    string content = "你好,我是你外公,我叫服务器。";
                    writer.UnicodeEncoding = UnicodeEncoding.Utf8; //注意
                    // 计算长度
                    uint len = writer.MeasureString(content);
                    // 写入长度
                    writer.WriteUInt32(len);
                    // 再写内容
                    writer.WriteString(content);
                    // 提交数据
                    await writer.StoreAsync();
                }
                // 这个socket不要了,扔掉
                socket.Dispose();
            }


    前一文章中,老周给大伙介绍过DataWriter的作用,这时我们用得上,用来把字符串写入网络流。注意,应该设置UnicodeEncoding属性为Utf-8编码,这个编码比较通用,就不会出现乱码。

    由于字符串的长度是可变的,客户端并不知道我们要发送的内容有多大,为了让接收者能够准确接收数据,应该先向流中写入数据长度,然后再写内容。接收方在读的时候,可以先读出长度,再读内容,因为表示长度的值是uint,它的值大小是固定的4个字节。

    下面代码为客户端发起连接。

                StreamSocket socket = new StreamSocket();
                try
                {
                    HostName svname = new HostName(txtIp.Text);
    
                    // 连接
                    await socket.ConnectAsync(svname, txtPort.Text);
                    // 接收数据
                    DataReader reader = new DataReader(socket.InputStream);
                    reader.UnicodeEncoding = UnicodeEncoding.Utf8; //注意
                    // 长度
                    await reader.LoadAsync(sizeof(uint));
                    uint len = reader.ReadUInt32();
                    // 读内容
                    await reader.LoadAsync(len);
                    string msg = reader.ReadString(reader.UnconsumedBufferLength);
                    runRecMsg.Text = msg;
                    // 释放
                    reader.Dispose();


    在读取接收到的数据时,用的是DataReader类,而且记住要统一编码utf-8,然后先加载4个字节,读出内容长度,再加载剩余的字节,最后读出字符串。

    好,最后一步就是配置清单文件,打开清单文件,默认用设计器打开,切换到[功能]选项卡,勾选“Internet(客户端与服务器)”与“专用网络(客户端与服务器)”,而“Internet(客户端)”可以取消。

    XML代码如下。

      <Capabilities>
        <Capability Name="internetClientServer" />
        <Capability Name="privateNetworkClientServer" />
      </Capabilities>

    运行结果请看下面的艳图。

    啊,今天的话题就扯到这里吧,改天再扯其他话题。

    示例源代码下载

  • 相关阅读:
    scratch第四集——过河孙小弟
    scratch第二集——scratch中的知识帧动画怎么用?
    scratch第九集——雷霆打怪大作战
    scratch第三集——scratch列表方法
    scratch第一集——飞机大作战
    position
    BOM与DOM
    grid布局
    登录界面
    用js输出同样字符出现的次数
  • 原文地址:https://www.cnblogs.com/tcjiaan/p/5293133.html
Copyright © 2020-2023  润新知