UWP之使用StreamSocket建立聊天室

一、了解Socket编程

Socket就是在两个端口之间建立管道连接来传输数据。

二、SocketStream使用流程

由于聊天室基于C/S模型,所以需要Client客户端与Server模型,在两个UWP之间是不能进行Socket通信的,所以测试的时候可以写在同一个UWP内。

建立TCP的连接。

客户端:

public async Task Start()
        {
            try
            {
                if (_isWorking == true) return;
                HostName _hostname = new HostName(_IPAddress);
                _streamSocket = new StreamSocket();
                _streamSocket.Control.KeepAlive = false;
                await _streamSocket.ConnectAsync(_hostname, _port);
                _isWorking = true;

                await Task.Run(async () =>
                {
                    var reader = new DataReader(_streamSocket.InputStream);
                    reader.UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.Utf8;
                    try
                    {
                        while (_isWorking)
                        {
                            await reader.LoadAsync(sizeof(uint));
                            uint len = reader.ReadUInt32();
                            await reader.LoadAsync(len);
                            string msg = reader.ReadString(reader.UnconsumedBufferLength);
                        }
                    }
                    catch (Exception ex)
                    {

                    }
                });
            }
            catch (Exception ex)
            {

            }
        }
开始建立连接,其中各个变量名即为类名。

在使用中await _client.Start()即可建立连接,注意ip和remoteServiceName的赋值;

发送消息:

public async Task SendMsg(string data)
        {
            if (!_isWorking)
                return;

            if (_streamSocket == null)
                return;
            if(_writer == null)
                _writer = new DataWriter(_streamSocket.OutputStream);
            MessageModel msg = new MessageModel
            {
                _message = data,
                _sendTime = DateTime.Now,
                _messageType = MessageType.TextMessage,
                horizontal = Windows.UI.Xaml.HorizontalAlignment.Left
            };
            await WriteData(JsonConvert.SerializeObject(msg));
        }

        private async Task WriteData(string data)
        {
            var bytes = Encoding.UTF8.GetBytes(data);
            _writer.WriteInt32(bytes.Length);
            _writer.WriteBytes(bytes);
            await _writer.StoreAsync();
        }
发送消息主要是借助DataWriter在已经建立的socket连接的StreamSocket的OutputStream中写入数据。其中MessageModel为发送消息的模型,自定义类型,转换为json字符串发送出去,在接收端再重新反序列化。

核心代码:

await _streamSocket.ConnectAsync(_hostname, _port);

await _writer.StoreAsync();

注意:接收数据也可以放在Client中,对StreamSocket的InputStream进行读操作即可,但由于聊天室基于Peer to Peer模型,没有核心服务器,所以每个节点都需要建立多个客户端给不同的服务端发送数据,一个服务端负责接收数据。

服务端:

public async Task Start()
        {
            try
            {
                if (_isWorking == true) return;
                ClientSockets = new List<StreamSocket>();

                _streamSocketListener = new StreamSocketListener() { Control = { KeepAlive = false } };
                _streamSocketListener.ConnectionReceived += OnConnectedReceived;
                await _streamSocketListener.BindEndpointAsync(new HostName(_IPAddress), _port);
                _isWorking = true;
                //SuccessInvoke
            }
            catch
            {
                //Fail.Invoke
                StopServer();
            }
        }

        private async void OnConnectedReceived(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
        {
            _dataWriter = null;
            //读取数据
            var reader = new DataReader(args.Socket.InputStream);
            //增加到socket列表中
            ClientSockets.Add(args.Socket);

            try
            {
                //维持数据流
                while (_isWorking == true)
                {
                    await reader.LoadAsync(sizeof(uint));
                    //第一个数字保存长度
                    uint len = reader.ReadUInt32();
                    var actualStringLength = await reader.LoadAsync(len);
                    var dataArray = new byte[actualStringLength];
                    reader.ReadBytes(dataArray);
                    var dataJson = Encoding.UTF8.GetString(dataArray);
                    var data = JsonConvert.DeserializeObject<MessageModel>(dataJson);
                    Messenger.Default.Send(data, "MsgReceivedAction");

                    //MsgRcv.Invoke(data, new EventArgs() );
                    //MsgRcv.Invoke(new Action() => _RcvMessages?.Add(new MessageModel() { _message = data, horizontal = Windows.UI.Xaml.HorizontalAlignment.Left }));
                    //Thread loop = new Thread();
                    //_RcvMessages?.Add(new MessageModel() { _message = data, horizontal = Windows.UI.Xaml.HorizontalAlignment.Left }));
                    //var data = JsonConvert.DeserializeObject<MessageModel>(msg);
                    //sendMsg(args.Socket, data);
                    //MsgReceivedAction?.Invoke(data);
                }
            }
            catch (Exception)
            {
                //
            }
        }

服务端就不需要发送数据的部分了,值得注意的是OnConnectedReceived是接受新的连接的时候进行的操作,在内部用lambda表达式建立一个async异步执行的函数,while(1)执行,当接受到数据的时候用mvvmlight框架(NuGet包管理器内搜索)进行数据显示,在不同的page间进行事件绑定,在主页面注册一个函数,进行事件处理:

Messenger.Default.Register<MessageModel>(this, "MsgReceivedAction", MsgReceivedAction);


private void MsgReceivedAction(MessageModel obj)
        {
            DispatcherHelper.CheckBeginInvokeOnUI(() =>
            {
                Messages.Add(obj);
                CurrentChat._message.Add(obj);
                //CurrentChat._message.Add();
            });
        }

其中Messages是一个ObservableCollection<MessageModel>类型的对象,用于在ListView中显示Message.

由于ObservableCollection需要重新绘制UI,所以只用简单的方法,比如上文中注释的所有方法都是行不通的,如简单地传送进去ObservableCollection对象Add,或者Action.Invoke等,需要借助DispatcherHelper或者系统的Dispatch来实现想要的效果。

<ListView.ItemContainerStyle>
                        <Style TargetType="ListViewItem">
                            <Setter Property="HorizontalContentAlignment"
                                    Value="Stretch"/>
                        </Style>
                    </ListView.ItemContainerStyle>
这个语句可以实现条目控件可以 充满ListViewItem。


一个聊天室的基本原理大致就是如此,程序员的魅力大多来自于创造性,太多的知识反而成为了成长的桎梏。


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值