在Windows Store应用程序中使用Stram Socket与桌面客户端进行通信,一直没弄成功,总让俺觉得很震精,怎么会不成功呢。后来经过几回测试发现,原来是在DataReader那里出了问题,总算弄成了。
Stream Socket通常用于传输一些比较长的数据,如文件。但这里为了使演示变得更容易理解,我传输了一段字符。
首先,我们用WinForm做一个服务器端。界面不复杂,目的是侦听连接,收到传入的客户端连接后,向客户端发送一条字符串消息。
处理的逻辑代码如下:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.Net;
using System.Net.Sockets;
namespace TestServerApp
{
public partial class Form1 : Form
{
TcpListener m_Listener = null;//用于监听链接
TcpClient m_Client = null;//传入的客户端
public Form1()
{
InitializeComponent();
this.btnStart.Enabled = true;
this.btnStop.Enabled = false;
}
/// <summary>
/// 向客户端发送字符串
/// </summary>
private void SendMessage(TcpClient client)
{
using (var stream = client.GetStream())
{
byte[] buffer = Encoding.UTF8.GetBytes("奔,不停地奔,奔向传说中的荒原;飞,不停地飞,飞向天空的那一端。");
uint len = (uint)buffer.Length;
// 先发送长度
stream.Write(BitConverter.GetBytes(len), 0, sizeof(uint));
// 再发送数据
stream.Write(buffer, 0, buffer.Length);
}
}
private async void btnStart_Click(object sender, EventArgs e)
{
if (this.m_Listener == null)
{
this.m_Listener = new TcpListener(IPAddress.Parse(this.txtAddr.Text), Convert.ToInt32(this.udPort.Value));
}
this.m_Listener.Start();
this.lblMsg.Text = "监听已开始。";
this.btnStart.Enabled = false;
this.btnStop.Enabled = true;
try
{
m_Client = await m_Listener.AcceptTcpClientAsync();
SendMessage(m_Client);
}
catch (SocketException se)
{
this.lblMsg.Text = se.Message;
}
catch (Exception ex)
{
this.lblMsg.Text = ex.Message;
}
}
private void btnStop_Click(object sender, EventArgs e)
{
if (m_Listener != null)
{
m_Listener.Stop();
this.lblMsg.Text = "监听已停止。";
}
this.btnStart.Enabled = true;
this.btnStop.Enabled = false;
}
}
}
接着是Win8 App客户端。
因为我们要使用网络连接,在创建项目后,把开清单文件,切换到【功能】选项卡,把和网络连接有关的选项勾上。
打开主页MainPage的XAML代码编辑器(设计视图),简单布局一下界面。
<Page
x:Class="WCleintApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:WCleintApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0" Margin="3">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Text="服务器:"/>
<TextBlock Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Text="端口:"/>
<TextBox x:Name="txtServer" Grid.Row="0" Grid.Column="1" Margin="2,3"/>
<TextBox x:Name="txtPort" Grid.Row="1" Grid.Column="1" Margin="2,3"/>
</Grid>
<StackPanel Grid.Row="1" Margin="5">
<Button Content="连接" Click="onConnClick" Margin="3,8,0,12" Padding="4,2.5"/>
<TextBox x:Name="txtRec" Margin="5,7,0,0" Height="200" IsReadOnly="True"/>
</StackPanel>
</Grid>
</Page>
切换到隐藏代码视图,完成功能代码。
using System;
using System.Collections.Generic;
using System.IO;
........
using Windows.UI.Xaml.Navigation;
using Windows.Networking;
using Windows.Networking.Sockets;
using Windows.Storage.Streams;
namespace WCleintApp
{
/// <summary>
/// 可用于自身或导航至 Frame 内部的空白页。
/// </summary>
public sealed partial class MainPage : Page
{
StreamSocket mSocket = null;//局部变量
bool isConnected; //标识是否已经进行了连接
public MainPage()
{
this.InitializeComponent();
this.isConnected = false;
}
private async void onConnClick(object sender, RoutedEventArgs e)
{
if (mSocket ==null)
{
mSocket = new StreamSocket();
}
// 如果还没连接就进行连接
if (this.isConnected == false)
{
HostName hServer = new HostName(txtServer.Text);
try
{
await mSocket.ConnectAsync(hServer, txtPort.Text);
this.isConnected = true;
}
catch { this.isConnected = false; }
}
DataReader dr = new DataReader(mSocket.InputStream);
// ByteOrder要设置为LittleEndian,不然会报错
dr.ByteOrder = ByteOrder.LittleEndian;
dr.UnicodeEncoding = UnicodeEncoding.Utf8;//编码
// 先读出一个uint的值,4个字节,这样就知道要接收数据的长度
var il = await dr.LoadAsync(sizeof(uint));
// 如果不相等,说明读到的不是uint值,因为必须4个字节
if (il != sizeof(uint))
{
txtRec.Text = "无法确定数据大小。";
return;
}
// 确定流中数据的长度
uint strlen = dr.ReadUInt32();
// 继续加载剩下的数据
await dr.LoadAsync(strlen);
txtRec.Text = dr.ReadString(strlen);
//必须与流分离,避免将整个流也释放
dr.DetachStream();
dr.Dispose();
}
}
}
先运行服务器端,输入本机IP和监听端口,然后开始监听。
再运行客户端,输入服务器的IP和端口,进行连接,如果成功,就会收到服务器端发过来的字符串。