Sdílet prostřednictvím


Networking in Windows 8 Apps - Using StreamSocket for TCP Communication

My progress with Windows 8 App Development

If you read my first post, you know that I just recently started developing for Windows 8. Well, for the past week I have been building a simple IRC client that is helping me get my feet wet with Windows 8 App development and the Windows 8 SDK. Building an IRC client is a perfect starting point on any platform, and specifically on Windows 8 since it allows me to learn and implement many of the new features in Windows 8 (such as the Search and Share Charm, Live Tiles, Toast Notifications and many more) and take advantage of the brand new APIs. I have been having a tremendous amount of fun developing the app and seeing the progress throughout the week, and I’m very excited to share my experience in a series of posts.

Before I get started on the first topic, I want to share some photos of the IRC client. Here are a couple screenshots from the day I started to where I am now:

Day 1

Day 7

Even though there are many (many, many) improvements and features waiting to be done, I’m trilled about what I have accomplished so far.

Using StreamSockets to initiate a TCP connection

There are a huge number of cool and exciting new features that Windows 8 offers, bit if you've made the obvious decision to build a Windows 8 App, chances are your app will first need to connect to a remote service in some way before implementing anything else. That is why I’m starting off writing about (what I believe any Windows 8 developer should know) networking with the Windows 8 API. Of course, depending on the service and network protocol, there are different ways to accomplish the task, and the Windows 8 API makes it very easy to get your app connected in minutes, but I will specifically talk about communicating over a TCP connection.

To get started, I used a lot of readily available online resources that I think you should at least open in a new tab:

The Windows 8 API defines a very useful object called StreamSocket that handles most of the TCP communication we need. StreamSocket and related classes belong to the Windows.Networking and Windows.Networking.Streams namespaces which also offer many other capabilities and classes such as connecting to a Web Service, reacting to Network Status changes, Background Data Transfer and more. If you want to learn more about other networking features with the Networking Namespace, I encourage you to visit https://msdn.microsoft.com/en-us/library/windows/apps/xaml/BR229573(v=win.10).aspx .

Let’s get started. To initiate a TCP connection to a host, we will first need to know the Hostname or IP address of the machine and the remote port the service is attached to. Once we know the hostname and the port, we only need few lines of code to initiate a connection:

    1: StreamSocket socket = new StreamSocket();
    2: HostName hostname = new HostName("myremoteserver.net");
    3: await socket.ConnectAsync(hostname, "80");

Congratulations, you are now connected to the target host. But what if the server is unreachable, or can’t receive any connections? To account for those scenarios, we use the wonderful try-catch statement. When a connection is unsuccessful, an exception is thrown which needs to be translated to a SocketErrorStatus that indicates the SocketError. Here is an example with the HostNotFound SocketErrorStatus:

    1: try
    2: {
    3:     StreamSocket socket = new StreamSocket();
    4:     HostName hostname = new HostName("myremoteserver.net");
    5:     await socket.ConnectAsync(hostname, "80");
    6: }
    7: catch (Exception exception)
    8: {
    9:     switch (SocketError.GetStatus(exception.HResult))
   10:     {
   11:         case SocketErrorStatus.HostNotFound:
   12:             // Handle HostNotFound Error
   13:             break;
   14:         default:
   15:             // Handle Unknown Error
   16:             break;
   17:     }
   18: }

All possible SocketErrorStatus Members can be used in the same way. Their definitions can be found here.

Great, now we can detect when a host is unreachable (or Connection Times out, etc) and we can react appropriately. Let’s move on to talking to the host service when the connection is successful.

The StreamSocket class is equipped with the InputStream and OutputStream Objects that are used to receive and send data accordingly. To make our lives easier (and shorten the code we have to write) we will use a DataReader and DataWriter to read and write to the data streams. The DataReader and DataWriter can be attached to a stream to simplify reading and writing, and to use them we need to instantiate them with the stream as input.

    1: // the Windows.Storage.Streams Namespace contains
    2: // the DataReader and DataWriter Objects
    3: using Windows.Storage.Streams;
    4:  
    5: // add after calling ConnectAsync on the StreamSocket Object
    6: DataReader reader = new DataReader(socket.InputStream);
    7: DataWriter writer = new DataWriter(socket.OutputStream);

To send data to the host, first we need to write the data to the DataWriter by calling one of the DataWriter.Write* methods, and finally committing the data by calling the StoreAsync method. The DataWriter handles the rest.

    1: // write a string to the OutputStream
    2: writer.WriteString("some data to send to host");
    3:  
    4: // commit and send the data in the OutputStream
    5: await writer.StoreAsync();

And the data has been sent.

Receiving data is very similar to sending data, but with few differences. To receive data, we need to tell the DataReader how much data to receive and we need to wait for the data to be available. However, most of the times we don’t know how much data will be sent. Luckily, we can tell the DataReader to receive only the available data (up to a certain count) by setting the InputStreamOptions to Partial. And after the raw data has been received, we can call one of the DataReader.Read* methods to retrieve it:

    1: // container for the received Data
    2: // in this case a string
    3: string receivedData;
    4:  
    5: // set the DataReader to only wait for available data
    6: reader.InputStreamOptions = InputStreamOptions.Partial;
    7:  
    8: // wait for the available data up to 512 bytes
    9: // count is the number of actually received bytes
   10: var count = await reader.LoadAsync(512);
   11:  
   12: // read the data as a string and store it in our container
   13: if (count > 0)
   14:     receivedData = reader.ReadString(count);

Now you can process the received data and start receiving or sending data again.

Using StreamSocket for IRC communication

Most of the work is already done and we only need to process the data we receive. Because of the IRC protocol, the TCP client is required to listen for incoming data constantly, so I created a new Event that would be called each time a new string line was available and go back to listening for data. I created a new IRCClient class that would handle the IRC implementation and I created a method that I attached to the TCP Client event to handle each line as it is received. This way, all the IRC work is done by the IRCClient class, and the TCP client can go back to start listening for data again without delay.

Comments

  • Anonymous
    October 28, 2012
    You must have done well in EECS 148! =]

  • Anonymous
    October 29, 2012
    Yup, one of my favorite :)

  • Anonymous
    January 02, 2014
    Thank you for this.  There are errors in the source code for the Windows(8.1) Store App Quickstart code.  I wasted 2 days trying to learn what was wrong.  This showed me!  THANK YOU! Quickstart: Connecting with a stream socket (Windows Store apps using C#/VB/C++ and XAML) has errors in the code

  • Anonymous
    September 25, 2014
    Thank you for this good article, really helpful. Can you please add how to get multipart data from StreamSocket.

  • Anonymous
    December 21, 2014
    thanks

  • Anonymous
    April 22, 2015
    Hello.. It said that "reader.LoadAsync(512)" will blocking the flow until at least one byte is available. Is there any way to set a timeout so that if timeout value elapsed, the LoadAsync will returning 0 (since no bytes read)?

  • Anonymous
    September 10, 2015
    Hello. I can connect with server. the server is application in c++ and I can't change the server. My app connect correctly but when send the info the server not responding. I need disconnect for the server respond. The process is. Connect to server < OK Server receive < OK Send hello with user and pass in Stream(string) < OK Server receive and send respond < NO OK Server not responding... waiting for... < I don't know Close the connection < OK Server respond and in log see try to send the confirmation but no can Thanks for help

  • Anonymous
    September 14, 2015
    Is in Spanish, but have a good solution. social.msdn.microsoft.com/.../comunicacion-tcpcip-con-streamsocket-y-datawriterdatareader

  • Anonymous
    October 04, 2015
    The comment has been removed