2024年3月27日发(作者:)

一、技术概述

1.1 基于TCP/IP的通信技术

基于TCP/IP的通信基本上都是利用SOCKET套接字进行数据通讯,程序一般分为服务器端和

用户端两部分。

第一部分 服务器端

一、创建服务器套接字(create)。

二、服务器套接字进行信息绑定(bind),并开始监听连接(listen)。

三、接受来自用户端的连接请求(accept)。

四、开始数据传输(send/receive)。

五、关闭套接字(closesocket)。

第二部分 用户端

一、创建用户套接字(create)。

二、与远程服务器进行连接(connect),如被接受则创建接收进程。

三、开始数据传输(send/receive)。

四、关闭套接字(closesocket)。

1.2 .NET 平台下几种SOCKET模型的简要性能参考(摘抄)

(1)Socket + Threads/ThreadPool

实现:Accept一个Socket,就交给一个线程去管理,比较笨,但也比较有效,因为是同步方

式,控制起来很方便。高级点的,就是交给一个线程池去管理,线程池由系统自动托管,省

去了开销线程的时间。一般小型项目,用这个完全足够,开发也简单。但要注意,如果若干

Socket长时间占用线程池中的线程,同时其它连接数又比较多,很容易出现提示说你没有足

够的线程供使用。呵呵,让Socket少做点事,少占用时间,换一个快点的CPU是不错的方

式。另外,如果有一些比较好的第三方线程池组件,也可以选择使用,比如SmartThreadPool。

(2)Socket + Select

实现:Select是很常用的一种模型。是在阻塞功能中轮询一个或多个Socket,将要处理的

Socket放到一个IList中,当Select轮询结束后,然后我们再自己处理这个IList中的Socket。

具体的用法可以看一下MSDN。Select的效率并不能说是高的,因为当队列中待处理的Socket

比较多的时候,处理最后几个Socket相当于要遍历所有前面的Socket,非常不划算的。

(3)Socket + Asynchronous

实现:BeginXXXX,EndXXXX,再熟悉不过了吧。异步Socket归根到底,还是用的线程池技

术,用线程池来处理异步IO。这就又引出个问题,.NET的线程池又是用的什么实现方式,

以前看过有人说,.NET的线程池是用的完成端口来实现的,我不知道这样的说法是不是正

确,从查到的资料中也没有办法确认(希望这点有朋友可以告诉我)。异步Socket对于程序

的处理流程来说比同步复杂了许多,异步回调函数的控制不如同步方式那样直观。但有一点

我想应该是要注意的,就是回调函数应该轻装上阵,不应该处理过多的事务,对传递数据的

处理,应该交给其它线程进行处理。

(4)IOCP(完成端口)

实现:现在.NET下有一些伪IOCP,还没有见过开放出来的用这些伪IOCP来实现的SOCKET

例子。我说的20000~50000个客户端连接,是指在C++下开发的情况,这样的情况下,需要

用到的基本技术还包括内存池、查询算法等

基于目前阶段的综合考虑选择用多线程Socket+thread但是也用到线程池里的一些方法。

1.3 多线程技术

我们可以把线程看成是一个进程(执行程序)中的一个执行点,每个进程在任何给定时刻可

能有若干个线程在运行。一个进程中的所有线程共享该进程中同样的地址空间,同样的数据

和代码,以及同样的资源。进程中每个线程都有自己独立的栈空间,和其它线程分离,并且

不可互相访问。每个线程在本进程所占的CPU时间内,要么以时间片轮换方式,要么以优

先级方式运行。如果以时间片轮换方式运行,则每个线程获得同样的时间量;如果以优先级

方式运行,则优先级高的线程将得到较多的时间量,而优先级低的线程只能得到较少的时间

量。方式的选择主要取决于系统时间调度器的机制以及程序的实时性要求。

二、具体的实现方法如下:

初始化:当启动工作的时候create 套间字,bind绑定端口,listen监听端口,在监听listen

函数有一个参数是监听同时连接的最大个数。如listen(10);当多个GPRS客户端连接的时候要

进行排队。然后 accept连接请求。监控是一个线程,线程函数是一个死的循环。可以一直

在监听客户端的连接。主要的代码:

private void acceptclient()

{

while (true)

{

if (s != null)

{

try{

temp = ();//为新建连接创建新的Socket。

……

(temp);

i++;

serWorkItem(new WaitCallback(recdata), socketthread[i - 1]);

serWorkItem(new WaitCallback(senddata), socketthread[i - 1]);

}

catch (Exception err){

// (e, "信息提示", ,

ation);}}

ip = ((IPEndPoint)EndPoint).ng();

+= "GPRS:"+ip+"已经连接"+"rn";

tempdata = 1;

}

}

当有连接的时候的会用到线程池ThreadPool类的函数启动一个接受线程同时将此时的

SOCKET参数传输到启动的线程里(异步回调机制)。接受线程中的线程函数也是一个死的循环,

可以接受GPRS客户端发送过来的数据;当再有一个连接的时候会再启动一个接受线程不断

的接受GPRS客户端发送来的数据,当断开的时候SOCKET为NULL线程结束。依次下去…如

果同时几个GPRS客户端连接的时候,listen函数里有参数,可以对同时连接的客户端监听

并对其自动排队。主要的代码:

private void recdata(Socket ss)

{

orIllegalCrossThreadCalls = false;

while(true)

{

stringrecvStr = "";

byte[] recvBytes = new byte[1024];

int bytes;

if (ss == null)

{

= "ffer";

break;

}

try

{

……

bytes = e(recvBytes, , 0);//从客户端接受信息

recvStr = ing(recvBytes, 0, bytes);

}

catch (Exception err)

{

// (e, "信息提示", ,

ation);

break;

}

}

public void recdata(object ss)

{

a((Socket)ss);

}

发送线程函数如下:

private void senddata(Socket tt)

{

orIllegalCrossThreadCalls = false;

while (true)

{

stringsendStr = "";

byte[] sendBytes = new byte[1024];

int bytes;

if (tt == null)

{ break; }

try

{

sendStr = ;

if (sendStr != "")

{

(1000);

sendBytes = es(sendStr);

bytes = (sendBytes, , 0);//从客户端接受信息

("发送数据成功!");

……

}

}

catch (Exception err)

{

//(e, "信息提示",

ation);

}

}

}

public void senddata(object tt)

,

{

ta((Socket)tt);

}

三、实验验证

当有多个客户端连接服务器程序的时候可以正常的通信,客户端可以向服务器端发送数据,

而服务器端发送数据在全部的客户端也可以看到。真正实现了一对多点之间的通信。

四、收获感想

通过对多线程的理解和资料的查询知道一对一点、一对多点之间通信的原理。而对线程池的

有了一个清晰的认识,在GPRS服务器端使用线程池和多线程感觉差别不是很大,当然这儿

使用线程池只是想利用这里面的提供的异步回调和参数的传递。如果真正使用线程池的话,

也是首先启动监控线程,当有客户端连接的时候然后在线程池里拿一个线程使用,当结束时

再放回到线程池,这和直接启动一个线程的差别感觉不是太大,甚至还不如直接启动一个线

程。所以选择后者。以上是自己对多线程的一点看法和理解,不知道是不是有些欠妥,对于

项目的需求还有待于验证,其他的多线程的方法还需要再实践中琢磨。