Scalable SOCKBASE design and implementation (1)

zhaozj2021-02-16  95

Scalable SOCKBASE design and implementation (1)

table of Contents

Summary

Problems in Sockets Network Programming

Scalable SockBase design

SockBase programming implementation

Inheritance from SockBase and Its Usage

Summary

System.net Namespace provides a simple programming interface for multiple protocols used on the current network. If you need more programming for underlying control, developers need to use System.Net.Sockets namespace. System.net.sockets provides a managed implementation of a Windows Sockets (Winsock) interface for developers who need to strictly control network access. However, Sockets programming is both annoyance, no scalability, requires developers to control the acceptance and sending of messages, and processing, these work related to business logic, need to write code when programming, once demand changes, Change the reception message list and processing of Sockets. At the same time, both constructs of the command string require the underlying programmers to control, not only easy to make mistakes, but also difficult to change. In the face of complex and variable business logic, such architectures are unusable, while presenting a high requirement for programmers, a large extent of workload is placed on the work of the underlying repetitiveness. Therefore, in order to provide an easy-to-expanded Sockets programming architecture, the developer puts the attention to the business logic, and we propose the design scalable SockBase ideas, and achieve this architecture. Experience shows that not only solves the above existence. Question, and have achieved very good results.

Problems in Sockets Network Programming

In a general Sockets network programming, it is not difficult to appear:

While (sock! = null) {

Temp = readmsg (); // Call the sock.recevie (..) function, turn Byte [] into a string

IF (Temp.trim () == "login")

{

// Do some thing ...

Sock.send (Transmsg ("OK");

}

Else IF (Temp.trim () == "Show")

{

// Do some thing ...

Sock.send (Transmsg (IPS));

}

Else IF (Temp.trim () == "Upload") {

// Do some thing ...

Sock.send (Transmsg ("OK");

// Do some thing ...

Sock.send (Transmsg ("OK");

}

ELSE IF (Temp.trim () == "List") {

// Do some thing ...

Sock.send (Transmsg (Files));

}

Else IF (Temp.trim () == "get") {

// Do some thing ...

Sock.send (Transmsg ("OK");

Temp = read msg (). Trim ();

// Do some thing ...

}

}

From the above code, we can notice that for all news from the client, it is unified in this while () loop ... ...

For the reception of the message command, it is clear that it is similar to the above code mode, uniformly put it into a place. But the above code is the structure of the Switch Case used for the message. This brings a problem. Switch case is Writing in the program code preparation phase, that is, the so-called hard compaction. This is not modified during the program run. This makes the program to send different messages to users in different input / different conditions during operation, or User-defined, or newly added extended commands after the program is completed. At the same time, it is also due to the Switch Case structure, so that the processing of the message is also fixed, and the message processing function is also not dynamically modified. This makes The extensibility of the program is very poor, and for the underlying as described above, for the Socket operation, it cannot be directly used in other software. (Because the message command, the processing function is not necessarily exactly the same). That is, In the usual Sockets network development, developers control the acceptance and transmission of messages yourself, these work related to business logic needs to write code when programming, once the demand changes, and has to rewrite the list of Sockets And process. At the same time, both constructs of the command string require the underlying programmers to control, not only easy to make mistakes, but also difficult to change. In the face of complex and variable business logic, such architectures are unusable, while presenting a high requirement for programmers, a large extent of workload is placed on the work of the underlying repetitiveness.

Scalable SockBase design

For the above questions, we propose scalable sockbase. Scalability is primarily to receive any messages, and can have different processing functions to the same message in different situations..

We think of Windows's message processing mechanism. When we want to handle a system message, or when we handle our custom message, first we add custom messages to the list of procedures, while passing Windows programming The message map in the message map, run the function to handle this message. After receiving this message, you can find the message processing function we bind to it, and then call ..

Go back to Sockets, let's make a thing similar to a Windows message mapping table. There are two elements, one is the message command received, the other is to receive the handler after receiving this message, in the program developer During the development process, as long as the message mapping table is initialized before the specific message is received, it is enough. SockBase automatically calls the corresponding message processing function.

SockBase programming implementation

The above part is theory. Now let's start completing the implementation code of SockBase.

1. Define message mapping table

Based on the above mentioned, there is a need for a similar message mapping table. Here, we use HashTable to store data for messages and process functions .. Since HashTable is a key / value collection, we do the message command For the key, the corresponding message processing function is made as a value. Since there are many kinds, and we want all messages, we can call the corresponding processing functions in one place. So we use the .NET delegate as a HashTable Value.

Delegation is as follows:

Public Delegate Command (String Args);

The method is as follows:

Hashtable commands = new hashtable ();

Commands.add (/ * message command * /, new command (/ * specific processing function * /));

As long as it is called

(Command) Commands [/ * message command * /]) (/ * parameter * /);

Yes ~

2, SOCKBASE specific implementation

Ok, now, the preparation of the message mapping table has been completed. Now SockBase is implemented: P

(1) Confirmation of constructor and variables, implementation

Public class socketbase: idisposable {

// The command processing set to be processed

Protected hashtable m_commandhandlerlist;

Protected NetWorkstream Readstream;

Protected NetWorkstream Writestream;

Protected socket m_sock;

// passed the instance of the socket through the constructor.

Public SocketBase (socket Sock) {

M_Sock = SOCK; ReadStream = New NetworkStream (M_SOCK);

WriteStream = New NetworkStream (M_Sock);

}

Public void dispose ()

{

// Turn the local set of junction

Try

{

IF (m_sock! = NULL)

{

IF (m_sock.connected)

{

m_sock.shutdown (socketshutdown.both);

m_sock.close ();

}

m_sock = NULL;

}

}

Catch (Exception EX)

{

}

}

}

(2) Send and receive functions

The preparation has been completed. Now we start to receive messages for M_Sock, and send messages.

The first is the message transmission and reception. Due to the uncertainty of socket, it is easy to send multiple messages sent together, so we decided to send a fixed size package every message, receive the corresponding package, receive the corresponding Size package.

Define a fixed size of a package in SockBase:

Private static int defaultebuffersize = 5120;

Public Int buffersize

{

Get {

IF (m_buffersize! = 0)

Return M_Buffersize;

Else

Return Defaultebuffersize;

}

Set {m_buffersize = value;

}

Another is sending, receiving functions

Public string receivemsg ()

{

BYTE [] RECS = New byte [buffersize];

INT count = 0;

Int Num;

Do {

Num = Readstream.read (Recs, Count, Buffersize-Count);

IF (NUM == 0) {

Throw new Exception ("Close Close");

}

Count = NUM;

WHILE (Count

Return System.Text.Encoding.Getencoding (Encoding) .getstring (Recs) .Replace ("/ 0", ");

}

Public Void Send (String MSG)

{

BYTE [] Sender = new byte [buffersize];

Byte [] temp = system.text.encoding.Unicode.getbytes (msg);

Array.copy (Temp, Sender, Temp.length);

WriteStream.write (Sender, 0, Sender.length);

Writestream.flush ();

}

(3) Message distribution function

Ok, the following is the function that is sent to the message:

Public void cmdhandler (String ClientMessage)

{

// Analyze the command

String [] cmdlist = clientMessage.split (';');

String cmdtext = "";

For (INT I = 1; I

{

IF (i == cmdlist.length-1)

{

cmdtext = cmdlist [i];

}

Else

{

CMDText = Cmdlist [i] ":";

}

}

// Looking for a suitable matching process

IF (m_commandhandlerlist.containskey (cmdlist [0])) {(Command) m_connamdhandlerlist [cmdlist [0]) (cmdText);

}

}

We judge through all the keys (ie, the message command) in M_CommandHandlerList, if the command received, the corresponding value (ie Command delegate) is directly called directly (ie Command delegation).

(4) SockBase starting starting point

The last part, the starting point of the entire SockBase run:

Public void listensocket ()

{

Try

{

While (m_sock! = null && m_sock.connected)

{

// Intercept the message and make the corresponding processing

CmdHandler (ReceiveMSG ());

}

}

}

Now we just join the commands and handlers of the messages we want to process directly in the m_commandrandlerlist, run listEnsocket (), and proceed accordingly..

Inheritance from SockBase and Its Usage

The basic architecture of SockBase is achieved above. For most Sockets network programming, it can be applied. The following is the method of use..

Here, we have inherited from SockBase to a client_listenthread. In this class, we pass the constructor to pass the corresponding socket instance to m_sock. Initialize the message mapping table, use a thread to run listensockt to receive The message is sent, and the handler is called.

Public Class Client_ListentHread: SocketBase

{

#Region All fields contains the command field

#ndregion

#Region All methods

Public client_listenthread (socket client_socket): Base (socket)

{

LoadcommandrandlerList ();

}

/ / Load all command processing queues

Public void loadingcommandhandlerlist ()

{

CommandHandlerItem.com ("getfile", new command (getfilehandler);

CommandHandlerItem.com.Add ("Fileok", Command (FileokHandler);

}

/ / The following is all command processing functions

Private void getfileHandler (String cmdtext)

{

/ / Check if the file exists

IF ((New FileManager ()). CheckfileExist (cmdtxt)))

{

Send ("OK");

}

Else

{

Send ("failure");

}

}

Private void fileokhandler (String cmdtext)

{

Dispose ();

}

#ndregion

}

Run it by the following function:

Listen_socket = new socket (addressfamily.internetwork, sockettype.stream, protocoltype.tcp);

...

Listen_socket.listen (-1);

While (true) {

Client_Listenthread ClientthRead = new client_listenthread (listen_socket.accept ());

IF (clientthread.sock.connected)

{

Thread filethread = new thread (New ThreadStart (ClientthRead.Listensocket);

FileThread.isBackground = true;

FileThread.start ();

}

to sum up

Through the above SockBase, we can dynamically modify the message mapping table without changing the SockBase. This makes developers to focus on business logic, which greatly facilitates Sockets-based network programming development.

转载请注明原文地址:https://www.9cbs.com/read-12105.html

New Post(0)