数据套接字的服务进程客户进程通信前不必建立连接, 通信的步骤见图2。 copyright paper51.com 1) 服务进程首先调用Socket()创建一个数据套接字,并调用Bind()将服务器地址捆扎在该套接字上,然后调用Recvfrom()等待客户进程发来的请求; paper51.com 2) 客户进程在调用Socket()创建一个数据报套接字后,调用Bind()将客户机地址捆扎在此套接字上,接着调用Sendto()向服务进程发送请求,然后调用Recvfrom()等待服务进程返回该请求的处理结果; copyright paper51.com
3) 服务进程在执行客户进程所请求的任务后,调用Sendto()将处理结果返回给客户进程; paper51.com
4) 服务进程和客户进程通过调用Close()撤消套接字。 paper51.com
paper51.com 3、套接字编程示例 内容来自www.paper51.com
下面给出一个运用字节流套接字在TCP/IP网络上实现客户机/服务器方式进程通信的实例。在此例中,服务进程先于客户进程运行,当双方建立连接后,服务进程通过该连接向客户进程不断发送一个连续增长的序列数,客户进程每接收到50个序列数就在屏幕上显示一个‘.’,显示至20个点后换行,直至任意一方进程被中断为止。 copyright paper51.com
# include <sys/types.h> paper51.com # include <sys/socket.h> copyright paper51.com # include <netinet/in.h> paper51.com # include <netdb.h> paper51.com
# include <stdio.h> copyright paper51.com main() 内容来自论文无忧网 www.paper51.com { 内容来自论文无忧网 www.paper51.com
int sock,namelen,seq,netint; paper51.com struct sockaddr-in server; 内容来自论文无忧网 www.paper51.com //存服务器的internet地址 copyright paper51.com char msgsock; paper51.com char buf[1024]; copyright paper51.com
//创建internet域的TCP协议的字节流套接字 内容来自www.paper51.com sock=socket(AF-INET,SOCK-STREAM,IPPROTO-TCP); paper51.com if(sock<0) { 内容来自www.paper51.com
perror("socket"); copyright paper51.com exit(1); 内容来自www.paper51.com } http://www.paper51.com
//将本地主机(服务器)的地址捆扎到创建的套接字上 http://www.paper51.com server.sinfamily=AF-INET; 内容来自论文无忧网 www.paper51.com //internet域 http://www.paper51.com sevrer.sinaddr.s-addr=INADDR-ANY; //使用任意合法地址 copyright paper51.com sevrer.sinport=htons(1032); 内容来自论文无忧网 www.paper51.com //公认的服务端口号 paper51.com if(bind(sock,&server,sizeof(server))<0){ http://www.paper51.com perror("bind"); http://www.paper51.com exit(2); copyright paper51.com } paper51.com //阻塞至客户方有连接请求到来,建立一新套接字用于通信 内容来自www.paper51.com namelen=sizeof(server); copyright paper51.com if((msgsock=accept(sock,&server,&namelen))<0){ copyright paper51.com perror("accept"); copyright paper51.com exit(4); 内容来自www.paper51.com } http://www.paper51.com //此时连接已建立,可以进行通信 内容来自www.paper51.com
seq=0; http://www.paper51.com for(;;){ 内容来自论文无忧网 www.paper51.com
netint=htonl(seq); 内容来自www.paper51.com //主机字节顺序转为网络字节顺序 copyright paper51.com
write(msgsock,&netint,4); 内容来自www.paper51.com //向客户方写序列数 paper51.com seq++; paper51.com
} http://www.paper51.com } 内容来自www.paper51.com 3.2远程控制系统tightvnc服务端的设计 内容来自www.paper51.com TightVNC的整体结构如图3所示: http://www.paper51.com paper51.com 内容来自www.paper51.com 服务端我们按照它们各自的功能做一下划分,其结构如下: paper51.com 表1功能模块图 http://www.paper51.com
Kernel copyright paper51.com vncBuffer.cpp vncClient.cpp vncDesktop.cpp vncServer.cpp WinVNC.cpp paper51.com GUI paper51.com vncAbout.cpp vncAcceptDialog.cpp vncAdvancedProperties.cpp vncConnDialog.cpp vncMenu.cpp vncProperties.cpp vncTimedMsgBox.cpp paper51.com
Misc paper51.com d3des.c Log.cpp MinMax.cpp RectList.cpp stdhdrs.cpp tableinitcmtemplate.cpp tableinittctemplate.cpp tabletranstemplate.cpp translate.cpp vncauth.c vncInstHandler.cpp vncKeymap.cpp vncRegion.cpp vncService.cpp copyright paper51.com Network copyright paper51.com VSocket.cpp vncSockConnect.cpp vncHTTPConnect.cpp rfbproto.h 内容来自www.paper51.com Encoding http://www.paper51.com vncEncodeCoRRE.cpp vncEncodeHexT.cpp vncEncoder.cpp vncEncodeRRE.cpp vncEncodeTight.cpp vncEncodeZlib.cpp vncEncodeZlibHex.cpp paper51.com 其服务端的主要功能模块结构如下: http://www.paper51.com 其核心框架就是四个类vncClient,vncServer,vncDesktop和vncBuffer.下面我就这四个类之间的联系和用途作一下简单的分析: paper51.com vncServer: paper51.com vncServer主要是做如下的一些工作:容许vncClient动态的添加和删除;将本地vncDesktop对象内部状态的任何改变“传播”到各个客户端;传播客户端的鼠标和键盘事件到本地的vncDesktop对象。同时,其还创建了vncSockConnect,vncCORBAConnect和vncHTTPConnect来接受Socket,Corba和HTTP的连接。vncServer为每个连接上来的客户端分配了一个ClientID(其实就是内部客户对象数组的Index),并且提供了对客户端管理的众多函数库。 paper51.com
同时,vncServer还提供了对客户Teleport,Capability,KeyboardEnabled,PointerEnabled,Name,Authenticated属性的get/set方法。 copyright paper51.com 下面我们来看一下vncServer对客户端连接上来和客户端认证成功这两个事件的处理流程: http://www.paper51.com
vncServer::AddClient: copyright paper51.com
首先vncServer在其内部的vncClient *m_clientmap[MAX_CLIENTS]数组中为新连接上的客户端分配一个空闲的slot,并将其作为此客户的 clientID。 然后,为此连接分配一个vncClient对象,根据传递过来的参数,设置vncClient对象的相关属性,然后调用vncClient::Init方法将vncServer的实例指针和 clientID传给vncClient实例。接着,m_clientmap[clientid] = client并将此用户加入vncServer的未认证用户链表。 paper51.com vncServer::Authenticated(vncClientIdclientid): copyright paper51.com 首先从未认证用户列表中根据clientid获取vncClient对象,并将其从unauth list 中删除。如果是vncServer的第一个用户,创建vncDesktop对象,并调用m_desktop->Init(this)来初始化该vncDesktop对象。接下来,为这个用户分配一个vncBuffer *buffer = new vncBuffer(m_desktop);并通过调用vncClient::SetBuffer为vncClient设置这个Buffer,最后将此用户添加到auth list中。 内容来自论文无忧网 www.paper51.com
vncServer提供了一个用户列表的操作接口,这些接口通过将vncServer的方法调用映射到对auth list中各个客户的同样的方法的函数调用。 http://www.paper51.com vncDesktop: 内容来自www.paper51.com vncDesktop是一个全局唯一的对象,根据注释,vncDesktop主要是处理从display buffer中获取数据;同时,它还利用RFBLib DLL为vncServer提供诸如鼠标移动和屏幕更新等信息。上面提到,vncServer在第一个用户连接上来时发现其m_desktop为空时就创建一个vncDesktip对象,并调用 vncDesktop::Init(this)对其初始化。在vcnDesktop::Init的实现中我们发现其创建了一个vncDesktopThread,vncDesktop的方法调用大部分都在这个vncDesktopThread里完成的。下面我们来分析一下这个线程都做了些什么: 内容来自论文无忧网 www.paper51.com
vncDesktopThread::run_undetached(void*arg): 内容来自www.paper51.com 首先调用vncDestktop::Startup初始化,vncDesktop对象,然后就是处理桌面消息,调用m_server->UpdateMouse()和m_server->UpdateRegion(),接下来调用vncServer::TriggerUpdate来发送屏幕更新到每个vncClient。然后就是处理RFB_SCREEN_UPDATE和RFB_MOUSE_UPDATE这两个注册消息。 paper51.com vncClient: 内容来自论文无忧网 www.paper51.com vncClient做了数据发送的工作,在vncClient::SendUpdate函数的实现中,我们可以看到vncClient调用SendRFBMsg首先发送,然后SendCursorShapeU发送鼠标形状更新,SendCursorPosUpdate发送鼠标Pos更新,发送SendCopyRect,最后调用SendRectangles发送需要更新的矩形的相关数据。其实每个客户端vncClient在调用vncClient::Init初始化的时候都开了一个线程,客户端的行为基本上都是在vncClientThread::run里完成的。该线程在跟客户端交互完成了认证,Pixel格式,Encoding算法等信息的协商后,就进入loop循环开始接受和处理远程客户端发过来的rfbKeyEvent,rfbFramebufferUpdateRequest, http://www.paper51.com rfbPointerEvent,rfbSetEncodings,rfbSetPixelFormat, rfbClientCutText消息。 copyright paper51.com
vncBuffer: 内容来自www.paper51.com
vncBuffer主要处理发送数据的Encoding工作,其提供了远程客户的本地视图,其主要是利用内部的vncDesktop指针来获取相关的数据。 paper51.com |