Enough for me to drive about theory, you give me practice, practice!
We have a lot of technology. Some are incredibly fast, others are incredibly convenient. Some allow you to fly at the speed of light, others allow you to develop at the speed of light.
Disputes about which approach is better rarely subside. Now I will show you how to cross a hedgehog with a snake. We have a .NET that can quickly do and have a Native that can quickly do.
')
For educational purposes, we will cross these two directions. The article has another goal. It is based on a program written by me and Arwyl called DuSter. This program is a dummy server that allows you to test network programs. The server is very easy to use, it is quite flexible, it supports protocol description files, which allow you to more or less automate the testing of any protocols. I was developing a network layer, my friend - business logic and protocol parsing. It turned out something incredibly well licked and pleasant. We are proud of our program, and we want to provide it to the world for non-commercial use.
There is the
CLR - Common Language Runtime, an environment that allows you to run programs written in languages that support the Common Language Infrastructure (
CLI ). The whole thing + compilers and libraries form the
.NET Framework , one of the most common development environments in the world. I will not talk about how programs written in .NET work, because this topic is worthy of a couple more articles. Let me just say the main thing necessary for our article. The machine code of .NET and the machine code of Native (Non-.NET) applications are not the same. Accordingly, an interesting thing comes out: we can take one Native application written in the Assembler language, and take another Native application written in the Pascal language, and cross them together. It is quite simple. We were given such a task in the university. I, following my curious nature, decided to show off. I decided to cross assembler and C #. I thought that everything would be simple, I will take, and I will enter the assembly code in C #. How wrong I was. Naturally, having learned about what
MSIL is , I realized that the idea was not the best, but I did not want to give up. I have been looking for a way out of this situation for a long time - and found: P \ Invoke via DllImport.
And so, we have - a program in the .NET language that works using the .NET runtime. The task is to make the runtime pull the external libraries. Well, let's complicate the task a little more - let the program allow working with sockets based on Windows Socket 2.0.
When we had network programming at the institute, we were forced to write using WS2, but we, like avid sharpists, turned up the nose from this library, because in comparison with the System.Net.Socket library WS2 is a pathetic parody of code.
We have been looking for a compromise with our teacher for a long time, and eventually we came to the following: We are allowed to use .NET, provided that we pull WS2 through DllImport.
Let's start, and immediately proceed to the analysis of the code:
[DllImport( "ws2_32.dll" , CharSet = CharSet.Auto, SetLastError = true )]
public static extern Int32 accept( Int32 socketHandle, ref SocketAddres socketAddress, ref Int32 addressLength);
[DllImport( "ws2_32.dll" , CharSet = CharSet.Auto, SetLastError = true )]
public static extern Int32 bind( Int32 socketHandle, ref SocketAddres socketAddress, Int32 addressLength);
[DllImport( "ws2_32.dll" , CharSet = CharSet.Auto, SetLastError = true )]
public static extern Int32 listen( Int32 socket, Int32 queue);
[DllImport( "ws2_32.dll" , SetLastError = true )]
public static extern Int32 WSAStartup(Int16 wVersionRequested, ref WSADATA lpWSAData);
[DllImport( "ws2_32.dll" , SetLastError = true )]
public static extern String inet_ntoa( Int32 inadr);
[DllImport( "ws2_32.dll" , SetLastError = true )]
public static extern Int32 inet_addr( String addr);
[DllImport( "ws2_32.dll" , SetLastError = true )]
public static extern Int32 WSACleanup();
[DllImport( "ws2_32.dll" , SetLastError = true )]
public static extern Int32 WSAGetLastError();
[DllImport( "ws2_32.dll" , SetLastError = true , CharSet = CharSet.Ansi)]
public static extern Int32 gethostbyname( String name);
[DllImport( "ws2_32.dll" , SetLastError = true )]
public static extern Int32 socket( Int32 af, Int32 type, Int32 protocol);
[DllImport( "ws2_32.dll" , SetLastError = true )]
public static extern Int32 closesocket( Int32 socket);
* This source code was highlighted with Source Code Highlighter .
This is C # code, it does the simple and obvious thing. Referring to the standard Windows library ws2_32.dll, it imports pointers to the above methods into .NET. That is, it looks like the following - I let my program use Native methods.
In more detail all the features of DllImport are consecrated by the user @ alex-blank in
this article.
The main methods are already in the program - it remains to bring them in order.
What has always annoyed me in the WS2 library are the ways of returning errors, and reading information. I am very cautious about methods that return the value of the bytes read and -1 in case of an error. Moreover, I don’t like to get GetLastError after getting the result -1 to understand what the error was. The mechanism for promoting the stack of exceptions that is present in .NET is much more suited to my aesthetic requirements.
A quick note from XaocCPS
If you didn’t know, I would like to note that this kind of error handling is the specificity of WinAPI. In it, all (or most) functions return error codes, including through HRESULT, 0, -1 ... Well, GetLastError is a system function created to understand what actually happened.
Actually, the transition to error handling through exceptions is one of the key features of the .net platform. At the time of release, this moment was widely advertised, positioned as a solution to the problem of diversity and mess with error codes.
* If you knew this, then I hope the information will be useful to those who did not know
Therefore, our next step was as follows: Bring the mechanism of working with sockets to the level of .NET applications. What is needed for that?
First of all, let's collect all the constants that are in the Native applications in enum, so that they do not hang where it is not necessary.
enum ADDRESS_FAMILIES : short
{
/// <summary>
/// Unspecified [value = 0].
/// </summary>
AF_UNSPEC = 0,
/// <summary>
/// Local to host (pipes, portals) [value = 1].
/// </summary>
AF_UNIX = 1,
...
* This source code was highlighted with Source Code Highlighter .
Next, the methods we exported from WS2 work with variables that do not exist in the .NET environment. Therefore, it was necessary to marshal a bit with
marshaling technology in order to make ends meet:
[StructLayout(LayoutKind.Sequential)]
public struct SocketAddres
{
public Int16 sin_family;
public UInt16 sin_port;
public Int32 sin_addr;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
public String sin_zero;
}
* This source code was highlighted with Source Code Highlighter .
This structure allows you to operate with a remote host address.
As a result, we have a complete import of the WS2 library into .NET. This is cool, but we found it insufficient. Because it is terribly inconvenient to use this library. Therefore, having WS2 methods at hand, we began to develop the NSocket class. The first step was to create the simplest class of exceptions. Because work with a network of problems is full, and the developer needs to report about these problems. And in .NET, the best way to report an error is to throw an Exception.
/// <summary>
///
/// </summary>
public class NSocketException : System.Net.Sockets.SocketException
* This source code was highlighted with Source Code Highlighter .
This class is a simple wrapper that allows you to report errors. Well, we already have mistakes, now we need to do something to throw them. For these purposes, written another 2 classes. NSocket and NNet. These classes do most of the networking. If the NNet class is more sharpened to work with the network, then NSocket is an object-oriented representation of the socket (which is what I miss most in WS2).
/// <summary>
/// .
/// </summary>
/// <param name="bindedSocket"> , </param>
/// <returns> </returns>
public static NSocket Accept(NSocket bindedSocket)
{
WS2_NET.SocketAddres n = new WS2_NET.SocketAddres();
Int32 toref = Marshal.SizeOf(n);
NSocket s = new NSocket(WS2_NET.accept(( Int32 )bindedSocket, ref n, ref toref));
s.Connected = true ;
return s;
}
* This source code was highlighted with Source Code Highlighter .
Here is one of the methods of the Nnet class that allows you to accept an incoming connection.
/// <summary>
/// .
/// IPv4
/// </summary>
/// <param name="type"> </param>
/// <param name="proto"> </param>
public NSocket(NSocketType type, NProtocol proto)
{
if ( this .Disposed)
throw new InvalidOperationException( "Component is disposed" );
socket = WS2_NET.socket(2, ( Int32 )type, ( Int32 )proto); // 2 = AIF_INET
if ( this .HasError)
throw new NSocketException(WS2_NET.WSAGetLastError());
this .Closed = false ;
this .Protocol = proto;
this .SocketType = type;
}
* This source code was highlighted with Source Code Highlighter .
And this is the NSocket class constructor, which initializes a new instance of the socket.
What do we have at the exit?
Yes, to be honest, I was not very good at understanding .NET, so there are some flaws in the implementation of classes, but in general, we started working with sockets from scratch for 1 week of not very hard work. It should be noted that here we have traced the evolutionary path from Native WS2 to .NET Objects. Indeed, the task is somewhat unclaimed, because in .NET there are not only excellent classes of working with sockets, but also classes that implement servers and clients of popular protocols. I'm not talking about such a thing as WCF - one of the pillars of .NET 3.0, which allows you to connect programs over the network without requiring knowledge of sockets. But! For the purposes of these giants who save network programmers from problems, forgotten by all WS2 work. To study them would be nice, purely for understanding.
In the real program, which we give you the code ten times more. We tried very hard and wrote comments to each method so that we could understand all this.
As a result, working with sockets turned into a song:
try
{
NetModule.NNet.StartWS();
if (CurrExemp.SocketProtocol == NetModule.NProtocol.Tcp)
CurrExemp.Socket = new NetModule.NSocket(NetModule.NSocketType. Stream , CurrExemp.SocketProtocol);
else
CurrExemp.Socket = new NetModule.NSocket(NetModule.NSocketType.Datagram, CurrExemp.SocketProtocol);
CurrExemp.Socket.Bind(CurrExemp.Port);
if (CurrExemp.SocketProtocol == NetModule.NProtocol.Tcp)
CurrExemp.Socket.Listen();
}
catch
{
if (CurrExemp.ClientSocket != null && CurrExemp.ClientSocket.Connected)
CurrExemp.ClientSocket.Close();
if (CurrExemp.Socket != null && CurrExemp.Socket.Binded)
CurrExemp.Socket.Close();
if (NetModule.NNet.Started)
NetModule.NNet.StopWS();
MessageBox.Show( "Can't create socket on specified port!" );
return ;
}
* This source code was highlighted with Source Code Highlighter .
This code shows how easy it is to start a new server for TCP or UDP protocols. There is no need to carry out many checks that are needed for WS2.
Using this information in this article, you can start creating your own applications that, for example, will use the OpenGL library. Although, no, not worth it, such a library already
exists .
Well, you can try to speed up your application using calculations on a very complex and fast Assembler. I can even say how easy it is to do.
MASM32 Here you will find a delightful package for work on asm under Windows. It allows you to export your assembler code as standard libraries that you can plug into your .NET applications.
You can also write a program for interacting with COM ports or USB interfaces, with a core in C / C ++ and a face in C #. I think many would agree that programming C # interfaces is much more convenient than on pure C.
And, well, and most importantly: I have posted all the source codes for working with sockets, XML business logic and the server itself for testing network applications.
Well, this is my story about how you can cross a hedgehog with a snake.