




已阅读5页,还剩6页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
/5/1700005.shtmlUnix和Windows跨系统通讯编程2003-05-24 00:00 作者: 张勤李华春 出处: 天极论坛 责任编辑:方舟 摘要本文介绍了套接字(Socket)的基本概念及编程技术,并结合实例说明在Unix和Windows下如何用套接字实现客户/服务器方式的通讯编程。关键词Berkeley Sockets Windows Sockets 通讯编程 一、 前言随着Internet的不断发展,客户机/服务 器模型得到了广泛的应用。客户应用程序向服务器程序请求服务。一个服务程序通常在一个众所周知的端口监听对服务的请求,也就是说,服务进程一直处于休眠状 态,直到一个客户对这个服务的地址提出了连接请求。在这个时刻,服务程序被“惊醒”并且为客户提供服务或对客户的请求作出适当的反应。 Unix最早是由美国贝尔实验室发明的一种多用户、多任务的通用操作系统,由于 Unix具有技术成熟、可靠性高、网络和数据库功能强、伸缩性突出和开发性好的特色,可满足各行各业的实际需要,特别是企业重要业务的需要,已经成为主要 的工作平台和重要的企业操作平台,而微软公司的 Windows操作系统用户界面友好,安装、使用也比较方便,应用软件丰富,在个人PC机上成为主流操作系统。因此服务端使用 Unix,客户端使用 Windows能充分利用它们各自的优点,这也是今后发展的一个趋势。同时,金融行业的中间业务发展迅速,银行主机采用 Unix系统,而各代收代付单位多采用 Windows或 WindowsNT系统,在它们之间传输数据文件也涉及跨系统通讯的问题,通过套接字 Socket能方便地实现 Unix和 Windows的跨系统通讯,本文拟就这一问题作一探讨。二、 SOCKET简介TCP/IP是计算机互连最常使用的网络通讯协议, TCP/IP的核心部分由网络操作系统的内核实现,应用程序通过编程接口来访问 TCP/IP,见下图:图1应用程序与Windows Socket关系图七十年代中,美国国防部高研署(DARPA)将TCP/IP的软件提供给加利福尼亚大学Berkeley分校后, TCP/IP很快被集成到Unix中,同时出现了许多成熟的TCP/IP应用程序接口(API)。这个API称为Socket接口。今天,SOCKET接 口是TCP/IP网络最为通用的API,也是在INTERNET上进行应用开发最为通用的API。九十年代初,由Microsoft 联合了其他几家公司共同制定了一套WINDOWS下的网络编程接口,即Windows Sockets规范。它是Berkeley Sockets的重要扩充,主要是增加了一些异步函数,并增加了符合 Windows 消息驱动特性的网络事件异步选择机制。 Windows Sockets规范是一套开放的、支持多种协议的 Windows下的网络编程接口。目前,在实际应用中的Windows Sockets规范主要有1.1版和2.0版。两者的最重要区别是1.1版只支持TCP/IP协议,而2.0版可以支持多协议,2.0版有良好的向后兼容 性,目前,Windows下的Internet软件都是基于 WinSock开发的。Socket实际在计算机中提供了一个通信端 口,可以通过这个端口与任何一个具有Socket接口的计算机通信。应用程序在网络上传输,接收的信息都通过这个Socket接口来实现。在应用开发中就 像使用文件句柄一样,可以对 Socket句柄进行读、写操作。我们将 Socket翻译为套接字,套接字分为以下三种类型:字节流套接字(Stream Socket)是最常用的套接字类型,TCP/IP协议族中的 TCP 协议使用此类接口。字节流套接口提供面向连接的(建立虚电路)、无差错的、发送先后顺序一致的、无记录边界和非重复的网络信包传输。 数据报套接字 (Datagram Socket)TCP/IP协议族中的UDP协议使用此类接口,它是无连接的服务,它以独立的信包进行网络传输,信包最大长度为32KB,传输不保证顺 序性、可靠性和无重复性,它通常用于单个报文传输或可靠性不重要的场合。数据报套接口的一个重要特点是它保留了记录边界。对于这一特点。数据报套接口采用 了与现在许多包交换网络(例如以太网)非常类似的模型。原始数据报套接字(Raw Socket)提供对网络下层通讯协议(如IP协议)的直接访问,它一般不是提供给普通用户的,主要用于开发新的协议或用于提取协议较隐蔽的功能。三、 基于SOCKET的应用开发图2面向连接协议的SOCKET编程模型面向连接协议的SOCKET编程模型应用最为广泛,因为面向连接协议提供了一系列的数据纠错功能,可以保证在网络上传输的数据及时、无误地到达对方。基于连接协议(字节流套接字)的服务是设计客户机/服务器应用程序时的标准,其编程模型如下: 尽管Windows Sockets和Berkeley Sockets都是TCP/IP应用程序的编程接口,但二者由于分属不同的系统,在某些环节仍有一些差别。Windows Sockets API没有严格地坚持Berkeley传统风格,通常这么做是因为在Windows环境中实现的难度。1.套接口数据类型和错误数值 Windows Sockets规范中定义了一个新的数据类型 SOCKET,这一类型的定义对于将来Windows Sockets规范的升级是必要的。这一类型的定义保证了应用程序向Win32 环境的可移植性。因为这一类型会自动地从16位升级到32位。 在 UNIX中,所有句柄包括套接口句柄,都是非负的短整数。 Windows Sockets 句柄则没有这一限制,除了INVALID_SOCKET 不是一个有效的套接口外,套接口可以取从0到 INVALID_SOCKET1 之间的任意值。因为 SOCKET 类型是unsigned ,所以编译已经存在于UNIX 环境中的应用程序的源代码可能会导致 signed/unsigned 数据类型不匹配的警告。因此,在socket() 例程和accept() 例程返回时,检查是否有错误发生就不应该再使用把返回值和1比较的方法,或判断返回值是否为负(这两种方法在BSD 中都是很普通,很合法的途径)。取而代之的是,一个应用程序应该使用常量INVALID_SOCKET ,该常量已在WINSOCK.H 中定义。例如:BDS 风格m_hSocket=socket();if(m_hSocket=1) /or m_hSocket0/Windows风格:m_hSocket=socket();if(m_hSocket=INVALID_SOCKET)2.select() 函数和FD_宏由于一个套接口不再表示为非负的整数,select() 函数在Windows Sockets API中的实现有一些变化:每一组套接口仍然用 fd_set 类型来代表,但是它并不是一个位掩码。typedef struct fd_setu_int fd_count;SOCKET fd_array; fd_set;整个组的套接口是用了一个套接口的数组来实现的。为了避免潜在的危险,应用程序应该坚持用FD_XXX 宏(FD_SET,FD_ZERO,FD_CLR,FD_ISSET)来设置,初始化,清除和检查fd_set 结构。3.错误代码errno,h_errno,WSAGetLastError() Windows Sockets 实现所设置的错误代码是无法通过 errno 变量得到的。另外对于 getXbyY() 这一类的函数,错误代码无法从h _errno 变量得到,错误代码可以使用 WSAGetLastError()调用得到,这种改进是为了适应多线程程序设计的需要。WSAGetLastError()允许程序员能够得到对应于每 一线程的最近的错误代码。为了保持与 BSD 的兼容性,应用程序可以加入以下一行代码:define errno WSAGetLastError() 这就保证了用全程的 errno 变量所写的网络程序代码在单线程环境中可以正确使用。当然,这样做有许多明显的缺点:如果一个源程序对套接口和非套接口函数都用 errno 变量来检查错误,那么这种机制将无法工作。此外,一个应用程序不可能为 errno 赋一个新的值 (在Windows Sockets 中,WSAGetLastError()函数可以做到这一点)。例如:BSD风格:retcode=recv();if(retcode=1 errno=EWOULDBLOCK)Windows风格:retcode=recv();if(retcode=1 WSAGetLastError ()=EWOULDBLOCK)虽然为了兼容性原因,错误常量与4.3BSD 所提供的一致:应用程序应该尽可能地使用“WSA”系列错误代码定义。且常量 SOCKET_ERROR是被用来检查API 调用失败的。虽然对这一常量的使用并不是强制性的,但这是推荐的。例如,上面程序更准确应该是:retcode=recv()if(retcode=SOCKET_ERRORWSAGetLastError()=WSAEWOULDBLOCK)4.重命名的函数有两种原因Berkeley 套接口中的函数必须重命名,以避免与其他的 API 冲突: close() 和closesocket() 在Berkeley套接口中,套接口出现的形式与标准文件描述字相同,所以 close() 函数可以用来象关闭文件一样关闭套接口。在Windows Sockets API中,套接字和正常文件句柄不是等同的,例如read(),write() 和close() 在应用于套接口后不能保证正确工作。套接口必须使用 closesocket()例程来关闭,用close() 例程来关闭套接口是不正确的。 ioctl()和iooctlsocket()Windows Sockets定义ioctlsocket() 例程,用于实现 BSD中ioctl() 和fcntl() 的功能。5.阻塞例程和 EINPROGRESS 宏 虽然Windows Sockets 支持关于套接口的阻塞操作,但是这种应用是被强烈反对的,如果程序员被迫使用阻塞模式(例如一个准备移植的已有的程序,BSD 包含了大量的阻塞函数,且其默认的工作方式都是阻塞的),那么他应该清楚地知道Windows Sockets中阻塞操作的语义。6.指针所有应用程序与Windows Sockets 使用的指针都必须是FAR 指针,为了方便应用程序开发者作用,Windows Sockets规范定义了数据类型LPHOSTENT 。7. Windows Sockets支持的最大套接口数目 一个Windows Sockets 应用程序可以使用的套接口的最大数目是在编译时由常量 FD_SETSIZE 决定的。这个常量在 select() 函数中被用来组建fd_set 结构。在WINSOCK.H 中缺省值是64。如果一个应用程序希望能够使用超过64个套接口,则编程人员必须在每一个源文件包含 WINSOCK.H 前定义确切的FD_SETSIZE值。有一种方法可以完成这项工作,就是在工程项目文件中的编译器选项上加入这一定义。 FD_SETSIZE定义的值对Windows Sockets实现所支持的套接口的数目并无任何影响。8.头文件 为了方便基于 Berkeley 套接口的已有的源代码的移植, Windows Sockets支持许多 Berkeley头文件。这些 Berkeley头文件被包含在WINSOCK.H中。所以一个 Windows Sockets应用程序只需简单的包含 WINSOCK.H就足够了(这也是一种被推荐使用的方法)。四、 跨系统通讯编程实例下面通过一个实例来具体说明 Socket 在Unix 和Windows 跨系统通讯编程中的应用。 Internet 上可以提供一种叫 IRC(Internet Relay Chatting,Internet 在线聊天系统)的服务。使用者通过客户端的程序登录到 IRC 服务器上,就可以与登录在同一 IRC 服务器上的客户进行交谈,这也就是平常所说的聊天室。在这里,给出了一个在运行 TCP/IP 协议的网络上实现 IRC 服务的程序。其中,服务器运行在 SCO Open Server 5.0.5上,客户端运行在Windows98 或 Windows NT 上。在一台计算机上运行服务端程序,同一网络的其他计算机上运行客户端程序,登录到服务器上,各个客户之间就可以聊天了。1.服务端 服务端用名为 client 的整型数组记录每个客户的已连接套接口描述字,此数组中的所有元素都初始化为1。同时服务器既要处理监听套接口,又要处理所有已连接套接口,因此需要用 到 I/O 复用。通过 select 函数内核等待多个事件中的任一个发生,并仅在一个或多个事件发生或经过某指定时间后才唤醒进程。维护一个读描述字集 rset ,当有客户到达时,在数组 client中的第一个可用条目(即值为1的第一个条目)记录其已连接套接口的描述字。同时把这个已连接描述字加到读描述字集rset 中,变量maxi 是当前使用的数组 client 的最大下标,而变量 maxfd1 是函数 select第一个参数的当前值。如下图:(假设有两个客户与服务器建立连接) 图3第二客户建立连接后的TCP服务器图4第二客户建立连接后的数据结构服务器在指定的端口上监听,每当一个套接口接收到信息,都将会把接收到的信息发送给每一个Client 。其主要源程序如下:int main(int argc,char argv)int i,maxi,maxfd,listenfd,connfd,sockfd;int nready,client;ssize_t n;fd_set rset,allset;char lineMAXLNE;socklen_t clilen;struct sockaddr_in cliaddr,servaddr;const int on=1;listenfd=Socket(AF_INET,SOCK_STREAM,0);if(setsockopt (listenfd,SOL_SOCKET,SO_REUSEADDR,on,sizeof(on)=1);err_ret(setsockopt error);bzero(servaddr,sizeof(servaddr);servaddr.sin_family =AF_INET;servaddr.sin_addr.s_addr =htonl(INADDR_ANY);servaddr.sin_port =htons(SERV_PORT);Bind(listenfd,(SA)servaddr,sizeof(servaddr);Listen(listenfd,LISTENQ);maxfd =listenfd; /初始化maxi = 1; /最大下标for(i=0;iFD_SETSIZE;i)clienti=1; /1表示可用FD_ZERO(allset);FD_SET(listenfd,allset);for(;)rset=allset; /结构赋值 nready=Select(maxfd1,rset,NULL,NULL,NULL); if (FD_ISSET(listenfd,rset) /有用户连接clilen=sizeof(cliaddr); connfd=Accept(listenfd,(SA)cliaddr,clilen);for(i=0;iFD_SETSIZE;i) if(clienti 0 clienti=connfd;/保存套接字 break;if(i=FD_SETSIZE); err_quit(too many clients);FD_SET (connfd,allset); /增加套接字到读描述字集if(connfdmaxfd);maxfd=connfd;if(imaxi);maxi=i;if(nready=0)continue;for (i=0;i=maxi;i) /检查所有用户连接 if(sockfd=clienti)0)continue; if(FD_ISSET(sockfd,rset)if(n=Readline(sockfd,line,MAXLINE)=0)/用户关闭连接 Close(sockfd); FD_CLR(sockfd,allset); clienti=1 elseprintf(s,line);Broadcast(client,maxi,line,n);if (nready=0)break;/将聊天内容发送到所有已连接的用户Broadcast(int client,int maxi,charstr,size_t n)int i;int sockfd;for(i=0;imaxi,i) if(sockfd=clienti)0) continue;writen(sockfd,str,n);2.客户端 为了实现非阻塞通信,利用异步选择函数 WSAAsynSelect() 将网络事件与 WinSock 消息联系起来,由该函数注册一些用户感兴趣的网络事件(如接收缓冲区满,允许发送数据,请求连接等)。当这些被注册的网络事件发生时,应用程序的相应函数 将接收到有关消息。应用程序在使用Windows Sockets DLL 之前必须先调用启动函数WSAStartup() ,该函数的功能有两点:一是由应用程序指定所要求Windows Sockets DLL 版本;二是获得系统 Windows Sockets DLL的一些技术细节。每一个WSAStartup()函数必须和一个WSACleanup()函数对应,当应用程序终止时,必须调用 WSACleanup()将自己从OLL 中注销。客户端程序用 VC6.0在Windows98 操作系统下设计,程序主框架由 AppWizard 生成,客户端核心代码在 CTalkDialog类中。只有一个 socket 变量m_hSocket 与服务端进行连接。连接建立好后,通过此 SOCKET发送和接收信息。手工加入CTalkDialog:OnSockConnect() ,完成基本套接字编程,为客户程序申请一个套接字,并将该套接字与指定服务器绑定,然后向服务器发出连接请求,启动异步选择函数等待服务器的响应。void CTalkDialog:OnSockConnect() struct sockaddr_in servaddr; WSADATA wsaData; if(WSAStartup(WINSOCK_VERSION,wsaData); MessageBox(Could not load Windows Sockets DLL,NULL,MB_Ok); return;m_hSocket=socket(AF_INET,SOCK_STREAM,0); memset(servaddr,0,sizeof(servaddr); servaddr.sin_family=AF_INET; /m_iPort,m_csIP 为通过注册对话框返回的端口号和IP地址servaddr.sin_port=htons(m_iport);servaddr.sin_addr.S_un.S_addr=inet_addr(m_csIP);if (connect(m_hSocket,(SA) servaddr,sizeof (servaddr)!=0) AfxMessageBox(连接服务器失败!);GetDlgItem(IDC_BUTTON_OUT)EnableWindow(FALSE); GetDlgItem(IDC_BUTTON_IN)EnableWindowTRUE GetDlgItem(IDC_EDITSEND)EnableWindow(FALSE); UpdateData(FALSE);else GetDlgItem(IDC_BUTTON_IN)EnableWindow(FALSE);GetDlgItem(IDC_BUTTON_OUT)EnableWindow(TRUE); GetDlgItem(IDC_EDITSEND)EnableWindow(TRUE); int iErrorCode=WSAAsyncSelect(m_hSocket,m_hWnd,WM_SOCKET_READ, FD_READ); if(iErrorCode=SOCKET_ERROR)MessageBox(WSAAsyncSelect failed on socket,NULL,MB_OK);手工加入CTalkDialog:OnSockRead(), 响应WinSock 发来的消息LRESULT CTalkDialog:OnSockRead(WPARAM wParamLPARAM 1Param)int iRead;int iBufferLength;int iEnd;int iSpaceRemain;char chIncomingData100;iBufferLength=iSpaceRemain=sizeof (chIncomingData); iEnd=0; iSpaceRemain=iEnd; iRead=recv(m_hSocket,(LPSTR)(chIncomingDataiEnd),iSpaceRemain,0); iEnd=iRead; if (iRead=SOCKET_ERROR) AfxMessageBox(接收数据错误!); chIncomingDataiEnd=0; if(lstrlen(chIncomingData)!=0)m_csRecv=m_csRecvchIncomingData;GetDlgItem(IDC_EDIT_RECV)SetWindowText(LPCSTR)m_csRecv);CEdit
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 鱼尾鳍的血液流动
- 济宁市2024-2025学年八年级上学期语文期末测试试卷
- 集安市2025-2026学年七年级上学期语文月考测试试卷
- 电路基础知识课件教学
- 2025年度财务人员年度考核表个人总结
- 高速车速安全知识培训课件
- 电解池和原电池课件
- 高速收费业务知识培训课件
- 电芯活化知识培训课件
- 道路园林绿化养护服务方案
- 建筑工程从数字化建造到智慧
- 新教材北师大版高中数学必修第一册全册教学课件
- 五年级上册英语课件-Unit1 Goldilocks and the three bears第四课时|译林版(三起) (共18张PPT)
- 水利工程安全防洪度汛专项方案-版
- 先天性复拇畸形虎口形态特点及治疗策略-PPT幻灯片
- 询价小组签到表
- 办公用房登记表
- QC080000有害物质管理评审报告
- 以“五位一体”模式提升理论学习中心组学习质量和成效的实践与探究
- 运维服务方案 全
- 国防战备公路工程可行性研究报告
评论
0/150
提交评论