.NET Socket开发之同步Socket实现

转载自 http://dotnet.chinaitlab.com/ASPNET/731870.html

        很多人认为在网络应用的服务端Socket不应该使用同步Socket.是的,在大多数情况下是这样的,但是也有一些场景下我们使用同步Socket可能会得到更的结果。如在下面的两种场景下我们便可以考虑使用同步的Socket.

  一、客户端数量比较少:

  数量比较少是指会同时连接到服务器的客户端数量一般在50人以下。这种情况下我们可以考虑使用同步Socket+Thread来实现我们的服务端。这样会让我们编写逻辑更清晰的代码而性能不会下降太多。

  二、客户端数量较多但都是短连接:

  短连接是指客户端的连接在处理完一次收发之后就产即断开的场景,比如说HTTP协议就是一种短连接。HTTP在客户端发出请求时建立一个Socket连接,并通过Socket发出一个URL请求,服务端在处理完这个请求并回发相应的页面后便会断开这个连接。那么在这种场景下我们也可以使用同步Socket来实现我们的需求。

  那么应该如果实现我上面提到的两种需求呢。对于这两种需求,我将采用不同的方案来实现它们。

  首先我们来看看第一种需求,这里我采用Socket+Thread来实现,基本的流程如下:

  首先创建一个Socket,并且给它绑定一个EndPoint后开始监听。接下来我们创建一个线程,在这个线程中我们用一个无限循环来接收来自客户端的连接请求。在接收到一个请求后,为这个客户端创建一个新的线程,并且在这个线程中也使用一个无限循环接收来自这个客户端的数据。下面让我们来看看代码:

  首先我们创建一个Socket用来侦听客户端的连接:

Socket listener = new Socket(AddressFamily.InterNetwork, 
SocketType.Stream, ProtocolType.Tcp);
IPEndPoint locEP= new IPEndPoint(IPAddress.Any, 2000);
listener.Bind(locEP);
listener.Listen(100);

  然后创建一个线程来处理客户端的连接请求:

Thread acceptThread = new Thread(new ThreadStart(AcceptWorkThread));
acceptThread.Start();
 
private void AcceptWorkThread()
...{
    Thread.CurrentThread.IsBackground = true;
    while (true)
    ...{
        Socket accept = listener.Accept();
        IPEndPoint remoEP = (IPEndPoint)accept.RemoteEndPoint;
        string recString = "接收到来自" + remoEP.Address.ToString() + "的连接。";
        this.Invoke(new AddListItemHandler(this.AddListItem), new string[] ...
{ recString });
        Thread receiveThread = new Thread(new ParameterizedThreadStart
(ReceiveWorkThread));
        receiveThread.Start(accept);
    }
}

        最后我们来看看如何接收数据:

private void ReceiveWorkThread(object obj)
...{
    Thread.CurrentThread.IsBackground = true;
    Socket socket = (Socket)obj;
    byte[] buffer = new byte[1024];
    while (true)
    ...{
        int receiveCount = socket.Receive(buffer);
        if (receiveCount > 0)
        ...{
            IPEndPoint remoEP = (IPEndPoint)socket.RemoteEndPoint;
            string recString = "来自客户端" + remoEP.Address.ToString() + "的
消息:" + Encoding.Default.GetString(buffer, 0, receiveCount);
            this.Invoke(new AddListItemHandler(this.AddListItem), new string[]
 ...{ recString });
            socket.Send(buffer, receiveCount, SocketFlags.None);
        }
        else
        ...{
            socket.Close();
            break;
        }
    }
}

  好了,整个实现就完成了。

  现在让我们来看看第二个需求:

这个方案我们将采用另外一个方法来实现,为什么不采用上一个方法来实现呢?让我们来分析一下。我们知道,在上一个实现中,每接入一个客户端就要创建一个线程,如果有大量的客户端接入的话,就会创建过多的线程。但是如果线程过多的话,Windows就需要更多的CPU时间来切换线程的上下文(这也是上一个实现不能接入很多客户端的原因)。

  我们知道,在这个方案中每一个连接都是短连接。而且顺序都是固定的。都是:接入->接收->发送这样的顺序,那么我们就可以在一个方法中完成整个处理。这样,我们就可以利用线程池来实现我们所需要的。好了,让我们用代码来说话吧:

  首先我们创建一个Socket用来侦听客户端的连接:

Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, 
ProtocolType.Tcp);
IPEndPoint locEP= new IPEndPoint(IPAddress.Any, 2000);
listener.Bind(locEP);
listener.Listen(100);

  接下来我们要创建一个线程池:

Thread[] ClientThreadList = new Thread[30];
foreach (Thread th in ClientThreadList)
...{
    th = new Thread(new ThreadStart(ClientWorkThread));
    th.Start();
}

  最后让我们看看线程都要做些什么:

private void ClientWorkThread()
...{
    byte[] buffer = new byte[1024];
    while (true)
    ...{
        Socket socket = listener.Accept();
        string recString = "接收到来自" + remoEP.Address.ToString() + "的连接。
";
        this.Invoke(new AddListItemHandler(this.AddListItem), new string[] 
...{ recString });
        int receCount = socket.Receive(buffer);
        if (receCount>0)
        ...{
            string recString = "来自客户端" + remoEP.Address.ToString() + "
的消息:" + Encoding.Default.GetString(buffer, 0, receiveCount);
            this.Invoke(new AddListItemHandler(this.AddListItem), new string[] 
...{ recString });
            socket.Send(buffer, receCount, SocketFlags.None);
        }
        socket.Shutdown(SocketShutdown.Both);
        socket.Close();
    }
}

        为什么我们要这样做呢?

  首先我们创建了一个Socket用于侦听客户端的连接请求,接下我们创建了一个拥有30个线程的线程池。并在每个线程中实现了Accept、Receive、Send和Close(),以完成连接、接收、发送、关闭的操作。

  现在我们假设有一个客户连接到服务器了,这时会有一个线程Accept到这个请求,并开始接收客户端发送过来的数据,接收到数据之后处理完发送给客户端,然后关闭这个连接,再次进入等待连接状态。而其它29个线程由于没有Accept到这个请求,仍然处理等待接入状态。



.net 稳定 高效 易用 可同步 TCP 通信框架 使用平台: WinXP,WIN7,WIN8,WINCE,WINPHONE。 使用.net 2.0 框架。 主要功能介绍: 1、可以代替 Oracle,Mysql客户端 在不安装Oracle,MySql客户端的情况下访问, 对数据库进行间接访问(需开始框架的服务器端)。 2、可以使本来没有网经功能的Sqlite具有网络访问的能力。(也是需要开启服务器端) 以上两点可以兼容现有代码生成器时,客户端代码仅需要特别小的改动就可以。 3、基本功能。可以实现聊天,传文件,图片。 4、使用长连接,有断线自动连接功能,心跳包。 5、使用自定义数据包协议,自建Session机制加强数据连接安全。 6、框架稳定,支持高并发。 7、简单的事件处理机制。使用更加简单。 8、支持同步处理,使程序的开发更架简单,不需要另行回调处理。 下载地址: 使用方式: 首选需要 引用 DataUtils.v1.1.dll。DataUtils 内包含客户端与服务器端 处理类。 1、服务器端 代码示例。 设置服务器端默认端口 ,不设置端口会使用默认端口 TcpSettings.DefultPort = 8511; 既可以使用静态默认对象,也可以创建服务器端对象。 SocketListener server= new SocketListener(); 对象创建后 注册一些事件,以接收客户端发送的信息。 SocketListener.Server.RegeditSession += new Feng.Net.Tcp.SocketListener.RegeditSessionEventHandler(server_RegeditSession); RegeditSession 事件用于是否允许客户端连接此服务器。可以使用用户名,密码的核对方式。 SocketListener.Server.DataReceive += new SocketListener.DataReceiveEventHandler(server_DataReceive); DataReceive 在这个事件里处理接收到的数据。 事件注册完成就可以打开监听 SocketListener.Server.StartListening(); 2、客户端 代码示例 设置服务器的IP地址 TcpSettings.DeafultIPAddress = "192.168.1.3"; TcpSettings.DefultPort = 8511;//不设置端口会使用默认端口。 这样就可以使用默认的静态客户端了。 也可以自己创建对象。 客户端创建后需要在Connected事件注册用户,以限制某些用户是否可以使此链接。用户来源可以是数据库等。 void client_Connected(object sender, SocketClient sh) { Client.RegeditSession("aaa", "bbb"); } 发送文字消息给其他用户 SocketClient.Client.SendToOtherUser(string user, string text); //USER代表发达的目白用户,text表示为发送的内容。 发送图片,音频,视屏可以使用 SocketClient..SendToOtherUser(string user, byte[] data)////USER代表发达的目白用户,data表示为发送的内容。 data数据中数据有多种类型时可以使用 using (Feng.IO.BufferWriter bw = new Feng.IO.BufferWriter()) { bw.WriteBitmap(new Bitmap(100, 100)); bw.Write(text);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值