c# socket 通信 同步的实现方式和异步实现方式的思考

socket通信的套路,无论是同步的方式或者异步的实现方式,套路总是不变的,那就是服务端开启一个线程监听客户端socket,客户端创建一个socket去连接服务端的监听端口,服务端接收这个客户端socket,在开辟一个线程负责与客户端线程通信(send receive 数据),这里有个误区,并不是监听socket与客户端socket进行通信,监听socket只是负责接收客户端连接请求,最后接收到的socket与客户端socket进行通信。

关于同步和异步的主要区别在哪里?

Accept Receive是阻塞的方法,通常线程在执行到这里的时候就阻塞了。那么他们之后的代码是无法执行的,Accept Receive的异步方法允许你执行他们之后的处理。举个简单的例子,假如客户端正在Receive一段数据,数据比较长,需要10秒,那么Receive之后的代码我们想立刻执行,不需要等待接收完毕才执行,这时候异步Receive就起到作用了,但是这里面依旧有个误区,异步会不会起到增大并发处理效果?并不会,异步只是起到不阻塞线程的作用(异步本身也是开辟一个新的线程去等待接收数据完毕进行处理),并不会起到增大并发接收数据的能力。并发处理归根结底还是由线程来控制的,就算是同步的处理方式,我们开辟多个线程针对对同一个socket进行Receive操作,依旧可以起到并发的效果。

 private void ConnectToServer()
        {//创建一个套接字
            IPEndPoint ipep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 6001);
            clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            //将套接字与远程服务器地址相连
            try
            {
                //客户端连接
                clientSocket.Connect(ipep);
            }
            catch (SocketException ex)
            {
                MessageBox.Show("connect error: " + ex.Message);
                return;
            }

            for (int i = 0; i <= 3;i++ )
            {
                Thread th = new Thread(new ThreadStart(Receive));
                th.IsBackground = true;
                th.Start();
            }
        }

        public void Receive()
        {
            byte[] data = new byte[1024];
            while (true)
            {
                //接收服务器信息
                int bufLen = 0;
                try
                {
                    if (clientSocket.Connected)
                    {
                        bufLen = clientSocket.Receive(data);
                        if (bufLen == 0)
                        {break;
                        }
                    }

                }
                catch (Exception ex)
                {
                    MessageBox.Show("Receive Error:" + ex.Message);
                    return;
                }
                string clientcommand = System.Text.Encoding.UTF8.GetString(data, 0, bufLen);//.Substring(0, bufLen);

                SetTextValue(clientcommand+Thread.CurrentThread.ManagedThreadId.ToString());

            }
        }

以上代码是客户端接收数据的处理过程,开辟了4个线程针对同一个socket做并发接收数据的能力。

接下来看看异步接收数据的实现方式

   private void ConnectToServer()
        {
            byte[] data = new byte[1024];

            //创建一个套接字
            IPEndPoint ipep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 6001);
            clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            //将套接字与远程服务器地址相连
            try
            {
                //客户端连接
                clientSocket.Connect(ipep);
            }
            catch (SocketException ex)
            {
                MessageBox.Show("connect error: " + ex.Message);
                return;
            }
            if (clientSocket.Connected)
            {
                clientSocket.BeginReceive(buffer, 0, data.Length, SocketFlags.None, Receive, clientSocket);
            }

        }

        static byte[] buffer = new byte[1024];
        public void Receive(IAsyncResult ar)
        {
            try
            {
                var socket = ar.AsyncState as Socket;
                var bufLen = socket.EndReceive(ar);
                if (bufLen > 0)
                {
                    string clientcommand = System.Text.Encoding.UTF8.GetString(buffer, 0, bufLen);//.Substring(0, bufLen);
                    SetTextValue(clientcommand + Thread.CurrentThread.ManagedThreadId.ToString());
                    socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(Receive), socket);
                }
                if (bufLen == 0)
                {
                    SetTextValue("socket shutdown");
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("Receive Error:" + ex.Message);
            }
        }

经过测试,异步接收数据的线程始终是同一个线程,所以异步本身没有起到并发处理的能力,除非开辟多个线程进行异步接收数据。

如何实现这样的一种场景?

我并不想机械的开辟多个线程对一个socket进行receive,而是根据需要灵活动态地开辟线程,既保证并发能力,也不浪费线程。c#中的socket机制根据什么来做到这一点?值得思考的问题。

最后总结一下同步和异步的方式

如果我们站在主线程的角度,如果我们进行的socket操作,send也好、receive也好,我们不想阻塞主线程造成不好的体验,那么异步和同步差异很大。但是如果我们在主线程的基础上开辟了新的线程进行这些同步操作,个人认为异步和同步的差异不是很大,也许只是代码code的方式不同而已,因为异步本身没有解决上面提出的并发处理问题。(这是我个人的观点)

如何做好线程终结和异常处理?

如果我们关闭一个socket

最好的方式是先shutdown两边的socket,通知远程正在通信的socket,这时候远程socket会空接一次,如果处在循环中,可以break终结线程,如果是异步的,可以避免再次异步调用,也可以避免捕获不必要的异常,但是还是需要加上异常处理机制。因为如果我们直接close一个socket,远程socket的receive会捕获异常

 if (clientSocket.Connected)
            {
                clientSocket.Shutdown(SocketShutdown.Both);
                clientSocket.Close();
            }

服务端可以缓存客户端socket?

如果实现复杂的通信需求,多个客户端,客户端通过服务端转发,所以服务端如果不缓存这些客户端socket是无法做到转发功能的。因此客户端连接的时候可以带上自己的标志信息,服务端缓存这些socket,根据标志取出socket进行转发,这是通信类软件的实现思路。但是同时要做好对这些客户端socket的管理操作,中断、异常、关闭等及时清理。

时间: 2024-10-26 05:49:32

c# socket 通信 同步的实现方式和异步实现方式的思考的相关文章

Socket 通信原理(Android客户端和服务器以TCP&amp;&amp;UDP方式互通)

ZERO.前言 有关通信原理内容是在网上或百科整理得到,代码部分为本人所写,如果不当,还望指教. 一.Socket通信简介 Android与服务器的通信方式主要有两种,一是Http通信,一是Socket通信.两者的最大差异在于,http连接使用的是“请求—响应方式”,即在请求时建立连接通道,当客户端向服务器发送请求后,服务器端才能向客户端返回数据.而Socket通信则是在双方建立起连接后就可以直接进行数据的传输,在连接时可实现信息的主动推送,而不需要每次由客户端想服务器发送请求. 那么,什么是s

Java系列讲座一:TCP方式Socket通信案例讲解

开发网络通信软件,如QQ,阿里旺旺等即时通讯软件时,Socket通信是必备的基础知识,今天跟大家谈谈Socket通信中TCP方式通信的软件开发步骤及主要代码讲解: 第一步:开发服务器端程序Server.java,源代码如下: package T16; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; imp

基于Java NIO的Socket通信

Java NIO模式的Socket通信,是一种同步非阻塞IO设计模式,它为Reactor模式实现提供了基础. 下面看看,Java实现的一个服务端和客户端通信的例子. NIO模式的基本原理描述如下: 服务端打开一个通道(ServerSocketChannel),并向通道中注册一个选择器(Selector),这个选择器是与一些感兴趣的操作的标识(SelectionKey,即通过这个标识可以定位到具体的操作,从而进行响应的处理)相关联的,然后基于选择器(Selector)轮询通道(ServerSock

python的socket通信实例

一.socket简介 1. 套接字 套接字是为特定网络协议(例如TCP/IP,ICMP/IP,UDP/IP等)套件对上的网络应用程序提供者提供当前可移植标准的对象. 它们允许程序接受并进行连接,如发送和接受数据.为了建立通信通道,网络通信的每个端点拥有一个套接字对象极为重要. 套接字为BSD UNIX系统核心的一部分,而且他们也被许多其他类似UNIX的操作系统包括Linux所采纳. 许多非BSD UNIX系统(如ms-dos,windows,os/2,mac os及大部分主机环境)都以库形式提供

android中非阻塞socket通信

1.什么是同步与异步,阻塞与非阻塞 首先我们要明白搞明白:同步就等于阻塞?异步就等于非阻塞?这是不对的,同步不等于阻 塞,而异步也不等于非阻塞. 1)那什么是同步编程? 什么是同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回.根据这个定义,android中绝大多数函数都是同步调用.但是一般而言,我们在谈论同步.异步的时候,特指那些需要其他部件协作或者需要一定时间完成的任务.在android中,由于主线程(UI线程的不安全性),我们经常会用到handler的SendMessage

第二十天 TCP 及socket通信原理、http协议及web服务、httpd核心配置详解

一.TCP及socket通信原理详解 二.http协议及web服务原理(一) 三.http协议及web服务原理(二) 四.httpd核心配置详解 1.tcp.udp是一种传输协议,实现进程地址标记,套接字是一个虚拟设备,用来表明主机上的某个进程      众所周知:0-1023:管理员才有权限使用,永久地分配给某应用使用(由IANA分配)      注册端口:1024-41951:只有一部分被注册,分配原则上非特别严格.      动态端口或私有端口:41952-65535:由内核分配临时端口,

Delphi Socket通信及多线程编程总结

http://cxhblog.blog.sohu.com/41930676.html 一.Socket通信: Delphi在ScktComp单元中对WinSock进行了封装,该单元提供了TAbstractSocket.TClientSocket.TClientWinSocket.TCustomSocket.TCustomWinSocket.TCustomServerSocket .TServerClientThread.TServerWinSocket.  TServerClientWinSoc

马哥教育第二十天TCP及socket通信原理详解、http协议、httpd

1.tcp.udp是一种传输协议,实现进程地址标记,套接字是一个虚拟设备,用来表明主机上的某个进程      常用端口:0-1023:管理员才有权限使用,永久地分配给某应用使用      注册端口:1024-41951:只有一部分被注册,只要确保主机上没有进程使用该端口.      动态端口或私有端口:41952-65535:由内核分配临时端口,如果临时端口不够可以通过修改内核参数修改临时端口范围       /proc/sys/net/ipv4/ip_local_port_range:定义两个

python - socket通信笔记

参考: 通过编写聊天程序来熟悉python中多线程和socket的用法:https://www.cnblogs.com/mingjiatang/p/4905395.html python socket通信:https://yq.aliyun.com/articles/40745?spm=5176.100239.blogcont40768.17.FIFTZv 1.socket使用方法 a.在python中使用socket时要iamport socket b.在使用socket中又服务器端和客户端之