主要內容:
- HttpClient類
- Socket通信
- WCF通信
HttpClient類
在UWP中可以用來進行網絡通信的HttpClient類有兩個,System.Net.Http.HttpClient和Windows.Web.Http.HttpClient,兩者使用上的差別並不大,但我們優先考慮后者,因為它位於Windows.Foundation.UniversalApiContract程序集中,是本地代碼,效率更高。我們主要學習的也是Windows.Web.Http.HttpClient了。
使用HttpClient類,我們可以向指定的URI發出HTTP請求,並獲取從服務器返回的數據。發起請求的方式有GET、POST、 PUT和 DELETE,請求都是異步請求。同時我們還需要一個HttpResponseMessage對象,用來聲明從 HTTP 請求接收到的 HTTP 響應消息。
HttpClient httpClient = new HttpClient();
//添加用戶代理標頭
httpClient.DefaultRequestHeaders.Add("user-agent", "Mozilla/5.0 (compatible;MSIE 10.0;Windows NT 6.2;WOW64;Trident/6.0)");
HttpResponseMessage response = await httpClient.GetAsync(new Uri(tbUrl.Text));//發送請求,此處是GET的方式,其他方式類似
response.EnsureSuccessStatusCode();//確保請求成功
//Buffer方式讀取返回結果
IBuffer buffer = await response.Content.ReadAsBufferAsync();
//流方式讀取返回結果
IInputStream inputStream= await response.Content.ReadAsInputStreamAsync();
//字符串方式讀取返回結果
string result = await response.Content.ReadAsStringAsync();
上面是HttpClient類來實現HTTP請求的一般方式了,ok,看常規演示:
請求HTML頁面:我們請求返回的HTML,最后都轉換成了string類型,然后將這個string類型的值給了WebView控件的NavigateToString方法,它會自動處理HTML標記。(PS:你如果覺得這樣還不如直接把URI給WebView控件的Navigate方法,反正顯示效果一樣,那你就把string值用其他文本控件顯示吧!)還有,讀取字符串直接用ReadAsStringAsync方法就行啦,后面的只為折騰一下。
private async void btn_get_Click(object sender, RoutedEventArgs e) { //http://static.cnblogs.com/images/logo_small.gif //http://www.cnblogs.com/czhwust/p/Win10_file.html HttpClient httpClient = new HttpClient(); httpClient.DefaultRequestHeaders.Add("user-agent", "Mozilla/5.0 (compatible;MSIE 10.0;Windows NT 6.2;WOW64;Trident/6.0)"); if (String.IsNullOrEmpty(tbUrl.Text)) { tbstatus.Text = "輸入的URL為空"; } else { try { tbstatus.Text = " 等待響應......"; HttpResponseMessage response = await httpClient.GetAsync(new Uri(tbUrl.Text)); response.EnsureSuccessStatusCode();//確保請求成功 tbstatus.Text = response.StatusCode + " " + response.ReasonPhrase; //請求html //字符串方式讀取返回結果 string result = await response.Content.ReadAsStringAsync(); wv.NavigateToString(result); //Buffer方式讀取 //IBuffer buffer = await response.Content.ReadAsBufferAsync(); //using (var dataReader = DataReader.FromBuffer(buffer)) //{ // string result = dataReader.ReadString(buffer.Length); // wv.NavigateToString(result); //} //Stream方式讀取 //var inputstream = await response.Content.ReadAsInputStreamAsync(); // Stream stream= inputstream.AsStreamForRead(); // using (StreamReader reader=new StreamReader(stream)) // { // string result= reader.ReadToEnd(); // wv.NavigateToString(result); // } } catch (Exception ex) { tbstatus.Text = ex.ToString(); } } }
請求圖片:在請求圖片的時候,WriteToStreamAsync方法最簡捷,后面的同樣是為了折騰,但可以仔細體會一下。。。(PS:直接把網絡圖片的地址賦值給Image控件的Source屬性就能顯示圖片啦,我是砸場子的!)
private async void btn_get_Click(object sender, RoutedEventArgs e) { //http://static.cnblogs.com/images/logo_small.gif //http://www.cnblogs.com/czhwust/p/Win10_file.html HttpClient httpClient = new HttpClient(); httpClient.DefaultRequestHeaders.Add("user-agent", "Mozilla/5.0 (compatible;MSIE 10.0;Windows NT 6.2;WOW64;Trident/6.0)"); if (String.IsNullOrEmpty(tbUrl.Text)) { tbstatus.Text = "輸入的URL為空"; } else { try { tbstatus.Text = " 等待響應......"; HttpResponseMessage response = await httpClient.GetAsync(new Uri(tbUrl.Text)); response.EnsureSuccessStatusCode();//確保請求成功 tbstatus.Text = response.StatusCode + " " + response.ReasonPhrase; //請求圖片 BitmapImage bitmap = new BitmapImage(); // WriteToStreamAsync using (InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream()) { await response.Content.WriteToStreamAsync(stream); stream.Seek(0ul); bitmap.SetSource(stream); img.Source = bitmap; } // IInputStream >> IRandomAccessStream //IInputStream inputStream = await response.Content.ReadAsInputStreamAsync(); //using (IRandomAccessStream randomAccessStream = new InMemoryRandomAccessStream()) //{ // using (IOutputStream outputStream = randomAccessStream.GetOutputStreamAt(0)) // { // await RandomAccessStream.CopyAsync(inputStream, outputStream); // randomAccessStream.Seek(0); // bitmap.SetSource(randomAccessStream); // img.Source = bitmap; // } //} // IBuffer >> IRandomAccessStream //IBuffer buffer = await response.Content.ReadAsBufferAsync(); //using (IRandomAccessStream randomAccessStream = new InMemoryRandomAccessStream()) //{ // using (DataWriter datawriter = new DataWriter(randomAccessStream.GetOutputStreamAt(0))) // { // datawriter.WriteBuffer(buffer, 0, buffer.Length); // await datawriter.StoreAsync(); // randomAccessStream.Seek(0); // bitmap.SetSource(randomAccessStream); // img.Source = bitmap; // } //} } catch (Exception ex) { tbstatus.Text = ex.ToString(); } } }
Socket通信
Socket通信是一種點對點的通信技術,我們可以利用它開發點對點交互通信的軟件。作為一種常見的底層網絡通信技術,Socket具有簡單易用、連接穩定、數據傳送能力強等特點。HTTP通信是一種臨時的,無狀態的通信,在客戶端發出HTTP請求,服務端做出一次響應之后,HTTP連接就會斷開,當我們希望獲取新的數據時,只能再發送一次HTTP請求,而Socket通信則是以IP地址和端口號為連接的一個通信句柄,以端到端為通信模型的網絡通信協議。Socket能夠保證連接的持久性,一旦與服務器成功建立連接,服務器相應的端口將會處於開放狀態,此時客戶端能夠通過Socket建立的通道,向服務器開放的端口發出操作指令,而客戶端同時也能得到服務端即時反饋的信息,直到程序發出斷開指令或網絡斷開,Socket才會結束整個連接過程。
Socket通信支持兩種工作模式。一種是基於TCP模式,該模式是面向連接的,順序進行的可靠交付方式,另一種是UDP模式,它是無連接的,資源消耗少的不可靠交付方式。關於二者更詳細的區別,請自行百度了解。在UWP中Socket通信相關的類位於Windows.Networking.Sockets命名空間下,StreamSocket類對應於UDP,DatagramSocket類對應TCP。
直接開始演示:
首先,我們要完成服務端的編碼,新建一個控制台應用程序。(貼出全部代碼,不作過多解釋。)
static void Main(string[] args) { int receiveLength; IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Any, 9900); Socket newSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream,ProtocolType.Udp); newSocket.Bind(ipEndPoint); newSocket.Listen(10); Console.WriteLine(" 等待客戶端的連接 "); Socket client = newSocket.Accept(); IPEndPoint clientIp = (IPEndPoint)client.RemoteEndPoint; Console.WriteLine(" 連接客戶端地址 :" + clientIp.Address + ",端口號:" + clientIp.Port); while (true) { byte[] data = new byte[1024]; receiveLength = client.Receive(data); Console.WriteLine(" 獲取字符串的長度 " + receiveLength); if (receiveLength == 0) break; string getData = Encoding.UTF8.GetString(data, 0, receiveLength); Console.WriteLine(" 從客戶端獲得的數據 :" + getData); byte [] send = new byte[1024]; send = Encoding.UTF8.GetBytes("response :" + getData); client.Send(send, send.Length, SocketFlags.None); } Console.WriteLine("Discnnected from " + clientIp.Address); client.Close(); newSocket.Close(); }
然后就是客戶端了,這里需要注意的是客戶端在給服務端發消息時,同時讀取了服務端發送給客戶端的消息(服務端發給客戶端的消息是 response:+客戶端發來的消息)。
private async void btnConnect_Click(object sender, RoutedEventArgs e) { try { socket = new StreamSocket(); await socket.ConnectAsync(new HostName(tbIP.Text), tbPort.Text);//與服務端建立連接 tbInfo.Text = " 連接成功"; } catch (Exception ex) { tbInfo.Text = " 連接服務器錯誤:"+ex.ToString(); } } private void btnClose_Click(object sender, RoutedEventArgs e) { //斷開建立 reader.Dispose(); writer.Dispose(); socket.Dispose(); tbInfo.Text = " 關閉成功"; } private void btnSend_Click(object sender, RoutedEventArgs e) { string str = tbMsg.Text; SendMsg(str,socket);//發送消息 } private async void SendMsg(string str, StreamSocket socket) { try { //發送數據流 writer = new DataWriter(socket.OutputStream); writer.WriteString(str); await writer.StoreAsync(); //讀取數據流 reader = new DataReader(socket.InputStream); reader.InputStreamOptions = InputStreamOptions.Partial; await reader.LoadAsync(1024); string data = reader.ReadString(reader.UnconsumedBufferLength); tbReceivedMsg.Text += data + "\r\n"; } catch (Exception ex) { tbInfo.Text = " 出現異常:"+ex.ToString(); } }
運行結果:
WCF通信
WCF(Windows Communication Foundation)也是一種重要的數據通信技術,它在.NET Framework 3.0中引入,主要優點在於服務集成,使分布式開發更加靈活穩定。客戶端與WCF服務進行通信時需要借助代理,也就是添加服務引用。
首先我們來完成WCF服務部分的編碼,新建一個Visual C#語言的ASP.NET空Web應用程序。我們需要添加一個DataClass.cs的類文件和一個NewsDataService.svc的WCF服務文件。
DataClass.cs:
我們在添加[DataContract]時,可能無法導入命名空間,這時候我們要手動添加引用System.Runtime.Serialization,它的詳細路徑是 C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.Runtime.Serialization.dll
namespace WCFServer { [DataContract] public class DataClass { [DataMember] public string NewsTime { set; get; } [DataMember] public string NewsTitle { get; set; } } }
NewsDataService.svc:
public class NewsDataService : INewsDataService { public DataClass [] News() { List<DataClass> newsData = new List<DataClass>() { new DataClass {NewsTitle="這是新聞標題1",NewsTime="2015-10-10" }, new DataClass {NewsTitle="這是新聞標題2",NewsTime="2015-10-11" }, new DataClass {NewsTitle="這是新聞標題3",NewsTime="2015-10-12" }, new DataClass {NewsTitle="這是新聞標題4",NewsTime="2015-10-13" }, new DataClass {NewsTitle="這是新聞標題5",NewsTime="2015-10-14" }, }; return newsData.ToArray(); } }
INewsDataService.cs:
namespace WCFServer { [ServiceContract] public interface INewsDataService { [OperationContract] DataClass[] News(); } }
然后是客戶端部分的編碼。首先我們要添加服務引用(右鍵”解決方案資源管理器“中的“引用”節點即可看到此選項),然后在下面的對話框中的“地址”輸入WCF服務端的URI,點擊“轉到”,等加載完畢,自定義一個命名空間名稱,點擊“確定”即可,然后就可以在客戶端調用服務器上的WCF服務了。
我們首先得將服務端運行起來,客戶端才能調用,在運行服務端時,可能我們直接運行項目,會報錯(Forbidden),這時,我們可以選中NewsDataService.svc,然后右鍵,選擇“在瀏覽器中查看”,出現上邊右圖的效果,ok!
客戶端程序界面部分:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <TextBlock x:Name="textBlock" HorizontalAlignment="Left" Margin="49,54,0,0" TextWrapping="Wrap" Text="WCF網絡通信" VerticalAlignment="Top"/> <ListView x:Name="listView" HorizontalAlignment="Left" Height="266" Margin="49,118,0,0" VerticalAlignment="Top" Width="228"> <ListView.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding NewsTitle}"></TextBlock> <TextBlock Text="{Binding NewsTime}"></TextBlock> </StackPanel> </DataTemplate> </ListView.ItemTemplate> </ListView> <Button x:Name="btn_getData" Content="獲取數據" HorizontalAlignment="Left" Margin="49,409,0,0" VerticalAlignment="Top" Click="btn_getData_Click"/> </Grid>
后台代碼:
private async void btn_getData_Click(object sender, RoutedEventArgs e) { NewsDataServiceClient client = new NewsDataServiceClient(); ObservableCollection<DataClass> data = await client.NewsAsync(); listView.ItemsSource = data; }
運行效果:
緊接着,我們再來看如何調用Web Service。這個其實和調用WCF服務差不多的,首先也是添加服務引用(http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx),在http://webservice.webxml.com.cn這個網站上可以找到很多的Web Service供調用(不過好像經常打不開),然后這幾行代碼就OK
private async void btn_getData_Click(object sender, RoutedEventArgs e) { MobileCodeWSSoapClient client = new MobileCodeWSSoapClient(); string result=await client.getMobileCodeInfoAsync(tbTel.Text," "); txtresult.Text = result; }
運行結果:
好了,先到這里,本來還有一個RSS訂閱源的,太晚了,以后再補。晚安!