论文无忧网提供:计算机毕业论文范文|计算机毕业设计|计算机毕业论文
栏目导航 ASP Java Web .NET VB6.0 JAVA VC VF DELPHI PB 计算机网络 计算机科学与技术 PHP 安卓APP 其他 C# 代写论文
当前位置: > 计算机 > 计算机科学与技术 >

简易代理服务器的设计(论文+程序)

2.5.3   设置监听状态-listen() http://www.paper51.com

Listen函数设置套接字进入监听状态。为了接受连接,首先使用socket函数创建套接字,然后使用bind函数将它绑定到本地地址,再用listen函数为到达的连接指定backlog,最后使用accept函数接受请求的连接。Listen函数仅应用在支持连接的套接字上,如SOCK_STREAM类型。函数执行成功后,套接字将进入被动模式,到来的连接会通知要排队并等候接受处理。在同一时间处理多个连接请求的服务器通常使用listen函数,如果一个连接请求到达并且排队也满,客户端将接收错误。 http://www.paper51.com

2.5.4   建立套接字连接-connect()和accept()

内容来自www.paper51.com

这两个系统调用用于完成一个完整相关的建立,用于客户机与网络中的服务器建立连接时,用connect()这个调用连接将请求发到侦听方。之后,服务端就会调用accept(),而在调用accept ()的参数前应该先调用过listen(),Accept函数定义如下:

内容来自www.paper51.com

SOCKET accept(SOCKET soc, structsockaddr * addr, int * addrlen);

paper51.com

2.5.5   收发数据-send()和recv()

http://www.paper51.com

对流套接字来说,一般使用send和recv函数来收发数据。

内容来自论文无忧网 www.paper51.com

Send函数在一个连接的套接字上发送缓冲区内的数据,返回发送数据的实际字节数。Recv函数从对方接收数据,并将其存储到指定的缓冲区。Flags参数在这两函数中通常设为0。 内容来自论文无忧网 www.paper51.com

3        设计方案3.1    基本函数设计

http://www.paper51.com

我们根据上面讨论的代理服务器内容容易画出代理服务器的简单流程模型: 内容来自论文无忧网 www.paper51.com

图2 代理服务器的流程

内容来自www.paper51.com

从图中看出,我们需要设计的基本功能函数为ClientToProxy和ProxyToServer。其中,ClientToProxy用于实现收取Client数据请求并传给Server;ProxyToServer用于接收Server的数据、传给请求Client。在处理数据请求的过程中,我们必须知道Server的地址,这是非常重要的。这需要设计一个函数来解析地址,设计过程中用ReceiveInformation函数来实现。 http://www.paper51.com

另外,任何Windows Socket函数对IP地址和端口号的引用和传送给Windows Sockets函数的IP地址和端口号均是按照网络顺序组织的,这也包括了sockaddr_in结构这一数据类型中的IP地址域和端口域(但不包括sin_family域)。考虑到一个应用程序通常用“时间”服务对应的端口来和服务器连接,而服务器提供某种机制来通知用户使用另一端口,因此gethostbyname()函数返回的端口号已经是网络顺序了,可以直接用来组成一个地址,而不需要进行转换。如果用户输入一个数,而且指定使用这一端口号,则应用程序必须在使用它建立地址以前,把它从主机顺序转换网络顺序(使用htons()函数)。相应地,应用程序希望显示包含于某一地址中的端口号,这一端口号就必须在被显示前从网络顺序转换到主机顺序(ntohs)。

内容来自www.paper51.com

3.2    多线程流程

copyright paper51.com

由于代理服务器和大多数服务器一样,如果要处理多个请求,它应该使用多线程。其基本规划如下: copyright paper51.com

1.   等待来自客户(Web浏览器)的请求

paper51.com

2.   启动一个新的线程,以处理客户连接请求 paper51.com

3.   读取浏览器请求的第一行(该行内容包含了请求的目标URL) 内容来自论文无忧网 www.paper51.com

4.   分析请求的第一行内容,得到目标服务器的地址和端口

copyright paper51.com

5.   打开一个通向目标服务器(或下一个代理服务器,如合适的话)的Socket

内容来自论文无忧网 www.paper51.com

6.   把请求的第一行发送到输出Socket 内容来自www.paper51.com

7.   把请求的剩余部分发送到输出Socket 内容来自www.paper51.com

8.   把目标Web服务器返回的数据发送给发出请求的浏览器 copyright paper51.com

当然,如果考虑细节的话,情况会更复杂一些。实际上,这里主要有两个问题要考虑:第一,从Socket按行读取数据最适合进一步处理,但这会产生性能瓶颈;第二,两个Socket之间的连接必需高效。有几种方法可以实现这两个目标,但每一种方法都有各自的代价。例如,如果要在数据进入的时候进行过滤,这些数据最好按行读取;然而,大多数时候,当数据到达代理服务器时,立即把它转发出去更适合高效这一要求。另外,数据的发送和接收也可以使用多个独立的线程,但大量地创建和拆除线程也会带来性能问题。因此,对于每一个请求,我们将用一个线程处理数据的接收和发送,同时在数据到达代理服务器时,尽可能快速地把它转发出去。 copyright paper51.com

4        服务器的实现4.1 环境创建 paper51.com

设计使用Visual C++ 6.0作为编程工具,采用Windows XP系统平台。刚开始时运行VC++ 6.0开发工具,单击菜单“Tools/Options...”,弹出Options对话框,选择Directories选项卡,首先在“Show directories for:”下拉菜单中选择Include files,将SDK中头文件的目录添加到:“Directories:”列

内容来自www.paper51.com

表中,如图3所示: 内容来自论文无忧网 www.paper51.com

图3 Includefiles设置 内容来自论文无忧网 www.paper51.com

然后在“Show directoriesfor:”下拉菜单中选择Library files,进行同样的设置,如图4所示。在VC主窗口中,执行主菜单“File”/“New”命令,建立一个控制台应用程序类型的工程,工程名为“MiniProxy”;点击“OK”后选择“an application that supports MFC”,之后一直确定,完成项目工程的创建工作。

paper51.com

图4 Library files设置 paper51.com

4.2 功能实现 http://www.paper51.com

4.2.1   数据变量定义 paper51.com

定义代表http://类型协议的变量HTTP,定义缓冲区大小变量MAXBUFFERSIZE。运用结构体Str_BasalSocket,里面定义客户端到代理服务器及代理服务器到Server间套接字两个,以及客户端、代理服务器和Server间的连接状态变量两个;Str_ProParam结构体定义Server地址变量,指向结构体Str_BasalSocket的指针,联结Server主机的端口变量和代理服务器到Server连接状态的句柄变量,这个结构体是用来代理服务器与Server主机交换信息。

http://www.paper51.com

4.2.2   启动代理服务器 http://www.paper51.com

这里,在运用套接字编程中由于Winsock在被调用时是动态链接库Winsock DLL形式实现的,首先需调用WSAStartup()函数对Winsock DLL进行初始化,它的第一个参数指定要加载的Winsock库的版本,高字节为次版本号,低字节为主版本号;第二个参数是用来返回DLL库的详细信息,是指向WSADATA结构的指针。实现为:

copyright paper51.com

WSADATA wsaData;

paper51.com

if(::WSAStartup(0x202,&wsaData)!=0)

copyright paper51.com

{

内容来自www.paper51.com

printf("\nErrorin Startup session.\n");

内容来自www.paper51.com

       WSACleanup(); http://www.paper51.com

return -1; 内容来自www.paper51.com

} copyright paper51.com

之后,创建一个代理服务器(Proxy)用于网络通信的套接字listen_socket。为了将本地地址附加到所创建的套接字上以便能够有效地标识套接字,我们需用bind函数来完成这一步:

内容来自www.paper51.com

SOCKET listen_socket; http://www.paper51.com

sockaddr_in local; http://www.paper51.com

定义用于保存socket信息的变量。

copyright paper51.com

local.sin_family=AF_INET;

内容来自论文无忧网 www.paper51.com

地址家族用于指定地址格式。 paper51.com

local.sin_addr.s_addr=INADDR_ANY; 

copyright paper51.com

此处将本机IP地址填入此变量。 http://www.paper51.com

local.sin_port=htons(port);    paper51.com

字节顺序转换函数用于将将u_short类型变量port从主机字节顺序转化到TCP/IP网络字节顺序(即:host to nework short)。

http://www.paper51.com

再用listen_socket=socket(AF_INET,SOCK_STREAM,0)语句打开socket 描述符。

内容来自www.paper51.com

if(::bind(listen_socket,(sockaddr*)&local,sizeof(local))!=0) 内容来自论文无忧网 www.paper51.com

{ copyright paper51.com

       printf("\nError in Binding socket."); http://www.paper51.com

       WSACleanup();

copyright paper51.com

       return-3;

http://www.paper51.com

 } copyright paper51.com

绑定完成后,紧接着就将此套接字置入监听以准备接受客户端的连接请求,调用listen函数,::listen(listen_socket,5),最后就可以启动处理线程进行侦听。 http://www.paper51.com

4.2.3   请求处理过程 内容来自论文无忧网 www.paper51.com

在这一步设计函数ClientToProxy来处理收到客户请求,并将请求合理传送至Server(客户请求的数据服务器)。

paper51.com

当客户端有请求发到代理服务器时,侦听中的Proxy就会调用函数accept来响应对主机的连接请求,同时Proxy会启动另一个侦听线程,以准备接收客户端的下一个请求。 paper51.com

msg_socket=accept(listen_socket,(structsockaddr*)&from,&fromlen); http://www.paper51.com

此处接收客户端的连接请求,返回客户端的地址和端口。

http://www.paper51.com

AfxBeginThread(ClientToProxy,pParam); 内容来自论文无忧网 www.paper51.com

启动另一侦听,用来处理客户端传来的另一个请求。 内容来自论文无忧网 www.paper51.com

如果此客户端到代理服务器的连接正确,我们就在这一socket连接上使用revc函数接收数据。 内容来自论文无忧网 www.paper51.com

SPair.StateClitoProClosed=FALSE; http://www.paper51.com

SPair.StateProtoSerClosed=TRUE; 内容来自论文无忧网 www.paper51.com

SPair.soc_ClitoPro=msg_socket; paper51.com

retval=recv(SPair.soc_ClitoPro,Buffer,sizeof(Buffer),0); 内容来自www.paper51.com

如果从套接字接收数据失败,用下面代码给出提示,同时关闭socket句柄,设置客户端到服务器的状态为关闭。

内容来自www.paper51.com

if(retval==SOCKET_ERROR) 内容来自www.paper51.com

{ printf("\nErrorReceive");

内容来自论文无忧网 www.paper51.com

if(SPair.StateClitoProClosed==FALSE)

paper51.com

{ 内容来自www.paper51.com

   closesocket(SPair.soc_ClitoPro);

copyright paper51.com

   SPair.StateClitoProClosed=TRUE; copyright paper51.com

} http://www.paper51.com

} paper51.com

当客户端关闭连接时,这时接收的数据为0,也需要将此时socket状态进行调整:

http://www.paper51.com

printf("Client Closeconnection\n");

内容来自www.paper51.com

closesocket(SPair.soc_ClitoPro); paper51.com

SPair.StateClitoProClosed=TRUE;

http://www.paper51.com

下面的一步,需要把这些数据信息传给Server。为此,需要解析出Server的地址,创建函数ReceiveInformation来实现此功能,即声明int ReceiveInformation( char * str, char *address, int * port),其中将实现:

内容来自论文无忧网 www.paper51.com

char buf[MAXBUFFERSIZE], command[512],proto[128];

内容来自论文无忧网 www.paper51.com

由于客户端在连接时,都会和代理服务器连接,发出请求,一般为commandurl(//GET http://www.baidu.com/ HTTP1.1 == > GET / HTTP1.1)形式,定义buf用于存储接收到的请求字符串;command用于保存get,connet,user等命令,这里显然是get;proto保存协议。 内容来自www.paper51.com

p=strstr(buf,HTTP);

copyright paper51.com

这里strstr 调用用于在字符串buf中寻找http第一次出现的位置(它不会比较结束符NULL),将值赋给指针p。如果分析出请求是HTTP协议类型的,将进行如下处理,首先将字符串协议部份去掉,把地址存于address参数中,端口设为缺省的80,最终返回这些信息,成功解析出Server端的地址信息。

copyright paper51.com

至此,启动一个新的子线程,用于处理代理服务器和数据服务器间的数据传输。这个功能将在下面实现。 内容来自www.paper51.com

在刚才的处理线程中,如果代理服务器、Server以及客户端、代理服务器端的连接没有关闭,就使用send命令发送请求数据给数据服务器(Server),成功时返回接收的字节数,错误时显示出错信息,并关闭代理服务器到Server的连接套接字,设置连接状态为关闭,直到接下来的处理不出错为止。

copyright paper51.com

retval=send(SPair.soc_ProtoSer,Buffer,Len,0); 内容来自论文无忧网 www.paper51.com

printf("\n send()failed:error%d\n",WSAGetLastError()); paper51.com

closesocket(SPair.soc_ProtoSer);

http://www.paper51.com

SPair.StateProtoSerClosed=TRUE; copyright paper51.com

同时代理继续接收客户端的请求,成功时返回接收的字节数,错误时显示出错信息,关闭客户端到代理服务器的套接字,设置连接状态为关闭。直到接下来的处理不出错为止。

copyright paper51.com

------分隔线----------------------------
联系方式