Home > Article > Backend Development > Detailed explanation of graphic code of C# network programming
In today's software development, network Programming is a very important part. This article briefly introduces the concepts and practices of network programming. Friends in need can refer to the following
Reading Directory:
Basics
Socket Programming
Multi-threaded Concurrency
Blocking Synchronous IO
Basics
In today's software development, network programming is very As an important part, this article briefly introduces the concepts and practices of network programming.
Socket is a network programming interface. It is a layer of encapsulation of the transport layer TCP and UDP communication protocols. It is exposed through a friendly API to facilitate network communication between processes or multiple machines.
In network programming, there are two roles: client and server. For example, by opening a browser to access a program hanging on the Web software From a program perspective, a web page means that the client (browser) initiates a Socket request to the server, and the server returns the content of the web page to the browser for parsing and display. Before data communication between the client and the server, three confirmations will be made before the connection is officially established, which is a three-way handshake.
ClientSend messageAsk the server if it is ready
The server responds I am ready, what about you? Are you ready
The client responds to the server that I am also ready and can communicate
TCP/IPThe protocol is the basic protocol for communication between networks. The usage of the Socket interface exposed under different programming languages and different operating systems is also similar, but its internal implementation is different, such as Linux epoll under Windows and IOCP under Windows.
Instantiate Socket
Bind the public address port to the operating system
Start listening to the bound port
Waiting for client connection
IPEndPoint ip = new IPEndPoint(IPAddress.Any, 6389); Socket listenSocket = new Socket(ip.AddressFamily, SocketType.Stream, ProtocolType.Tcp); listenSocket.Bind(ip); listenSocket.Listen(100); listenSocket.Accept();
listenFunction There is an int type parameter, which represents the maximum number of connections waiting to be processed, which represents the number of connections that have been established but have not yet been processed. Each time the Accept function is called, a connection will be taken out of this waiting queue. Usually the server needs to serve the connections requested by multiple clients, so it will looptake out the connections from the waiting queue to receive and send.
while (true) { var accept= listenSocket.Accept(); accept.Receive(); accept.Send(); }
Multi-thread concurrencyThe above server program processes receiving and sending messages are completed under the current thread, which means A client connection must be processed before the next connection can be processed. If the current connection is for IO operations such as database or file reading and writing, it will greatly waste the server's CPU resources and reduce the server throughput.
while (true) { var accept = listenSocket.Accept(); ThreadPool.QueueUserWorkItem((obj) => { byte[] receive = new byte[100]; accept.Receive(receive); byte[] send = new byte[100]; accept.Send(receive); }); }As in the example, when a new connection request is detected, Accept() is called to take out the currently connected socket, and a new thread is used to process receiving and sending information, so that the server can implement fork a new process to handle client connections:
var connfd = Accept(listenfd,(struct sockaddr *)&cliaddr,&cliaddr_len); var m = fork(); if(m == 0) { //do something }Create a new thread to handle current limiting:
var *clientsockfd = accept(serversockfd,(struct sockaddr *)&clientaddress, (socklent *)&clientlen); if(pthreadcreate(&thread, NULL, recdata, clientsockfd)!=0) { //do something }
Blocking synchronous IOThe
model is used in the above examples, and it is simple and convenient to use.
while (true) { var accept = listenSocket.Accept(); byte[] receive = new byte[100]; accept.Receive(receive); byte[] send = new byte[100]; accept.Send(receive); }From the time the Receive function is called to the time it receives the data sent from the client, the function will always block and wait. The processing flow during this blocking period is as follows:
至此处理成功,开始处理下一个连接请求。 调用发送函数同样会阻塞在当前,然后把用户缓冲区(send字节数组)数据拷贝到内核中TCP发送缓冲区中。 TCP的发送缓冲区也有一定的大小限制,如果发送的数据大于该限制,send函数会一直等待发送缓冲区有空闲时完全拷贝完才会返回,继续处理后续连接请求。
异步IO
上篇提到用多线程处理多个阻塞同步IO而实现并发服务端,这种模式在连接数量比较小的时候非常适合,一旦连接过多,性能会急速下降。 在大多数服务端网络软件中会采用一种异步IO的方式来提高性能。
同步IO方式:连接Receive请求->等待->等待->接收成功
异步IO方式:连接Receive请求->立即返回->事件或回调通知
采用异步IO方式,意味着单线程可以处理多个请求了,连接发起一个Receive请求后,当前线程可以立即去做别的事情,当数据接收完毕通知线程处理即可。
其数据接收分2部分:
数据从别的机器发送内核缓冲区
内核缓冲区拷贝到用户缓冲区
第二部分示例代码:
byte[] msg = new byte[256]; socket.Receive(msg);
介绍这2部分的目的是方便区分其他几种方式。 对于用户程序来说,同步IO和异步IO的区别在于第二部分是否需要等待。
非阻塞式同步IO
非阻塞式同步IO,由同步IO延伸出来,把这个名词拆分成2部分描述:
非阻塞式,指的是上节"数据从别的机器发送内核缓冲区"部分是非阻塞的。
同步IO,指的是上节"内核缓冲区拷贝到用户缓冲区"部分是等待的。
既然是第一部分是非阻塞的,那就需要一种方法得知什么时候内核缓冲区是OK的。 设置非阻塞模式后,在连接调用Receive方法时,会立即返回一个标记,告知用户程序内核缓存区有没有数据,如果有数据开始进行第二部分操作,从内核缓冲区拷贝到用户程序缓冲区。 由于系统会返回个标记,那可以通过轮询方式来判断内核缓冲区是否OK。
设置非阻塞模式参考代码:
SocketInformation sif=new SocketInformation(); sif.Options=SocketInformationOptions.NonBlocking; sif.ProtocolInformation = new byte[24]; Socket socket = new Socket(sif);
轮询参考代码:
while(true) { byte[] msg = new byte[256]; var temp = socket.Receive(msg); if (temp=="OK"){ //do something }else{ continue } }
这种方式近乎淘汰了,了解即可。
基于回调的异步IO
上面介绍过:
异步IO方式:连接Receive请求->立即返回->事件或回调通知
当回调到执行时,数据已经在用户程序缓冲区已经准备好了,在回调代码中对这部分数据进行相应的逻辑即可。
发出接收请求:
static byte[] msg = new byte[256]; var temp = socket.BeginReceive(msg, 0, msg.Length, 0, new AsyncCallback(ReadCallback), socket);
回调函数中对数据做处理:
public static void ReadCallback(IAsyncResult ar) { var socket = (Socket)ar.AsyncState; int read = socket.EndReceive(ar); DoSomething(msg); socket.BeginReceive(msg, 0, msg.Length, 0, new AsyncCallback(Read_Callback), socket); }
当回调函数执行时,表示数据已经准备好,需要先结束接收请求EndReceive,以便第二次发出接收请求。 在服务端程序中要处理多个客户端的接收,再次发出BeginReceive接收数据请求即可。
这里的回调函数是在另外一个线程的触发,必要时要对数据加锁防止数据竞争:
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
The above is the detailed content of Detailed explanation of graphic code of C# network programming. For more information, please follow other related articles on the PHP Chinese website!