Udostępnij za pośrednictwem


Writing a C# socket listener, if you must

Socket programming can be tough and complicated. If you are reading this because you are trying to decide on a communication mechanism for your new project then let me save you some reading. Stick with something else if you can.

Web Services gives you operating system and limited language independence and can be done over a range of transport. I have shown in another article how to use a Win Form as a web service client. .NET has great web services support cross vendor interoperability is easier than ever before. .NET remoting is also good choice if it is all .NET. If you need socket based communications then make sure there is not an easier option than writing your own. If you need to support multiple legacy clients then you may have to go this route but check and see if there are any applicable third-party libraries you can use.

But if you need to write your own then this article will teach you how to do it in .NET using C#. If you only need a basic listener and client you should look into the Socket helper classes. These include the TcpClient, TcpServer and UdpClient classes in the Sockets namespace. Since UDP is connectionless there is no listener and the same class is used for your client and server. These classes are still powerful and will fulfill most production needs with few lines of code and fewer options and parameters to know.

Many socket communication applications of this type are written in C or C++. While the technology in .NET socket applications is not actually simpler than C++, it is simpler for you to write and maintain them in C# because .NET provides better abstraction of the underlying Win32 API functions.

One of the ways that C# makes it easier to develop network programs is the manipulation of strings. Much of the effort in socket programming is string manipulation. It is amazing how many security holes start with simple buffer overflows. C# handles strings in two different ways and you are going to want to get to know them.

Look at this code snippet and think about what is going on.

String astring = new string(“hello”);
astring+= “ “;
astring +=”world”;
Console.WriteLine(“My string = ‘{0}’”, astring);

Instantiate a string and then append it twice right? Logically this is true but .NET actually does not do it that way. When you create a string a set amount of memory is allocated and that allocation cannot be changed. This means that strings are immutable. So when you modify a string C# creates a new memory space for it and flags the original one for garbage collection. Creation a new memory space, placing the new value in it, and pointing to it creates overhead. Garbage collection is only a partial solution. The original memory is not reclaimed until the garbage collector gets around to it. Now look at the example above. The process of creating the new string and abandoning the old one actually happens twice, leaving 3 separate strings allocated in memory.

Instead you should use the StringBuilder class which is in the System.Text namespace. This saves resources because the StringBuilder class paradoxically does not create strings, it creates StringBuilder objects. An instance of the StringBuilder class starts with a minimum of 16 bytes of memory and dynamically can increase of decrease this memory utilization. You can use the Length property of the StringBuilder object to manually change the memory allocated.

StringBuilder mySb = new StringBuilder(“1234567890”);
//the length is 10 and the value is “1234567890”
mySb.Length = 7;
//the length is 7 and the value is “1234567”
mySb.Length = 10
//the length is 10 and the value is “1234567”

When you manually decrease the length you also reduce size of the memory allocation and the extra values are dropped off. When you increase it the unused portion of the larger memory is filled with empty values. In the example above remember you have not created a string, just a StringBuilder object. When you want to turn it into a string you call the ToString() method as in this example.

myLabel.Text = mySb.ToString();

C# stream manipulation is also easier and more elegant than it was in C or C++. Streams include client server communications as well as communication with hardware or logical devices. Depending on the type of stream you are accessing, there are three basic functions. You can write data from a memory buffer to a stream, you can read data from a stream and you can ‘seek’, or search for, a specific pattern of data in a stream.

Winsock is Microsoft’s stream communication API. Winsock defines a standard service provider interface (SPI) between the application programming interfaces (API), which include header and library files in an SDK or DLLs (Winsock DLL, Ws2.dll) and the protocol stacks. Winsock supports multiple protocols including IPv6. The Winsock SPI can be used to create your own transport service provider or to extend an existing transport SPI by using a Layered Service Provider (LSP).

Microsoft’s has done a good job of making Winsock API calls backwards compatible and minimizing versions. But there are differences which you should be aware of. Every version of Windows after Windows 95 has used Winsock version 2.2. Patched Windows 95 boxes should be running 2.0. In most cases the techniques here should work fine. However, Windows 95 originally shipped with version 1.0 and, only recently, Windows CE was upgraded from version 1.1 to version 2.2. So if you are planning to deploy on Windows 95 or CE, be prepared for some extra research and testing

IP-based communication programming involves two basic categories of communications, connectionless and connection-oriented. The types of solutions we are discussing here use a connection oriented SOCK_STREAM type socket over TCP versus a connectionless SOCK_DGRAM type socket over UDP.

Our socket communication will follow the following steps.

1) Create a socket
2) Bind the socket to an address or end point
3) Listen for an incoming communications attempt
4) Accept the communication
5) Send and receive messages buffers
6) Shutdown the communication channel
7) Close the socket connection

You will need to add the statement “using System.Net;” to the top of your classes to try out the techniques listed here. This is the namespace containing most the classes you are going to need to create our sockets and manage the communications.

Each of the parties in a socket communication is called an end point. When you create your endpoint you need to decide which Address Family you are using. This specifies which addressing scheme that a socket will use to resolve an address. The one you will most likely use is "InterNetwork" which indicates that you are using IP version 4 such as what is used on most Ethernet networks and the Internet (e.g. 10.0.0.1). .NET and the .NET compact framework support a lot of addressing schemes out of the box. Some of the more useful and interesting ones include AppleTalk, ATM, Banyan, Data Link, ECMA, InterNetworkV6, NetBIOS, OSI and SNA. You can also specify "Unknown" and "Unspecified." This article addresses only “InterNetwork” addressing but the concepts detailed here transfer to the other address family types.

Each endpoint has a unique address which is a combination of an IP address and a port number. You will need the local IP addresses of your devices for communication to occur. There is an IPAddress class which you can instantiate it by passing the value of your IP address as a byte array or a long. If you need to pass in a string as your values then there is a parse method which is shown below

IPAddress.Parse(string ipString);

This method will take the string and attempt to convert it to an IP Address.

Do not hard code your IP address as a constant or store it in a configuration file because these would have to change as the server’s IP address changes and it complicates working in a multi-server environment such as clusters or where you have separate development, testing, staging and production servers. For these and for a variety of reasons to you are going to want to look up the IP address dynamically.

There are a lot of ways of dynamically looking up IP addresses including looking the IP address up in the registry. Unfortunately the registry keys you need can be different from one Windows version to another. Another way to do it is to query a local DNS server for the IP address. You can find these in the System.Net namespace.

IPHostEntry myiHe = Dns.GetHostByName(Dns.GetHostName());
IPAddress myIp = myiHe.AddressList[0];

To get the IP address from the DNS and assign the value to the IPAddress object we create an IPHostEntry value and assign it to a DNS object using the GetHostByName method which is familiar to Winsock developers. In order to get the host by name we use the GetHostName method to tell us our local host name. Then we create an IPAddress object called myIp and set its value to the AddressList property.

Once you have your IPAddress value set then you need to establish a socket address to make your endpoint. The IPEndPoint requires an IP address and a port number. The IP address can be passed as either an IPAddress object like above or a long.

IPEndPoint ipEnd = new IPEndPoint(IPAddress address, int port);

So using the example above the constructor could look like...

IPEndPoint ipEnd = new IPEndPoint(myIp, 10000);

Unlike the IP address you probably do want to make the port number both dynamic and configurable. You just want to make sure that you are not using a port that is needed by another application on your system and preferably not one used by other applications on your network. Make sure you look at a list of common port numbers and try to select one that will not conflict with common applications. You may want to discuss this with your network administrator, particularly if there is a chance your software will run over a firewall or VPN. There are lists of port numbers assignments available on the Internet.

When you create your socket you need to indicate the address family, socket type and protocol type. We have already said that we are using “InterNetwork” as our address type and now we are gong to look at the others.

The socket type defines the type of data connection and the protocol type defines the network protocol. For IP communications using the InterNetwork address family we can select from the following combinations.

The one you are most likely option is to create a connection-oriented socket using a Stream socket type and TCP as the protocol. However if we want to create a connectionless socket then we use the Dgram socket type and UDP as our protocol. It is also possible to use the “Raw” socket type which supports ICMP and “Raw” as our protocol type values but both of these have specialized applications.

The type of socket you decide to construct dictates the options available. There are quite a few options including setting KeepAlives, MaxConnections, SendTimeout, receive options and multicasting options. Refer to the .NET documentation for SocketOptionName values associated with the type of socket that you have selected.

So now we are ready to create our socket object which has a number of important methods. The constructor for it looks like this…

Socket myServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

Next we bind it to our endpoint using the Bind() method.

myServer.Bind(ipEnd);

Now we call the Listen() method, passing it an integer value representing the maximum length of the connection queue.

myServer.Listen(10);

Next we create our socket client and call its Accept() method. This method triggers the server to block, waiting for a client connection. Blocking communications can be controlled programmatically as well.

Socket myClient = myServer.Accept();

That is all that is required to make a socket listener. I plan to follow up with a look at building a TCP listener client and finally communicating between the two.