应用SOCKET实现网络通信

2012-02-14 14:57李光明
通信电源技术 2012年3期
关键词:IP地址端口应用程序

李光明

(西安政治学院,陕西 西安710068)

0 引 言

卫星遥测数据处理系统是基于C/S的体系结构,分为遥测参数处理软件和遥测参数处理结果显示软件,两个软件模块可在同一机器上或不同机器上独立运行,通过SOCKET链路交换数据,实时接收有效载荷管理工作站中的遥测数据,完成实时处理。处理结果实时回送有效载荷管理工作站进行入库处理,同时以组广播的形式向所有的显示工作站实时传送数据,供监视卫星运行情况。本文将详细讨论这项技术,同时列举出笔者在SOCKET编程过程中的几点经验。

1 SOCKET编程的基本概念

Windows NT提供了一个最重要的通信程序设计机制——Windows Sockets(WinSock),使我们在网络通信编程上有很大的发挥空间。

一个套接字(Socket)是一个通信端点。典型的通信发生于一个客户和一个服务器之间,就有两个端点,一个在客户端,一个在服务器端。对应的就有两个套接字,且这两个套接字在客户和服务器之间建立了双向数据传送的连接。

套接字基本上分为两类:流套接字、数据报套接字。流套接字(Stream Sockets)用于大流量数据的双向传输,数据流可分为记录流或字节流,这取决于协议。流通常用于无重复(UnDuplicated)的和顺序(Sequenced,保持包发送顺序)的传送和接收数据。流套接字保证数据发送。数据报套接字(Datagram Sockets)主要用于广播功能。数据报套接字支持双向数据流,不保证可靠、有序、无重复性,是面向无连接的传输机制。

套接字应用程序可以使用一个端口(port)与其它套接字应用程序通信。端口的含义可以这样理解:它的作用是可以实现在具有一个IP地址的单台机器上同时有效地运行多个客户或演示软件,各个到达的TCP包或UDP包都被指定给某一特定的端口。例如,可以在一个窗口中执行FTP,同时在另一个窗口运行自己的套接字应用程序或其它通信程序,确保不同通信程序的数据不被混淆在一起的机制就是端口。公用通信功能使用保留端口,用户可指定未被保留且未被使用的端口,或传递0作为端口值由Sockets自动分配端口。

每个套接字还有一个套接字地址,通常是应用程序运行所在计算机的IP地址。

Socket实际上代表了IP地址和端口号的组合,变成了通信中一种抽象化的终端节点。

套接字通信通常分为三个阶段:

(1)执行安装功能。创建并绑定一个套接字,定位并与远程计算机建立一个套接字连接。

(2)发送和接收数据。若正在编写一个服务类型的套接字应用程序,则可创建一个套接字并监听从客户来的套接字连接输入。若有多个用户想同步的建立连接,则可请求积压连接请求。

(3)执行清除功能。断开和关闭套接字连接。

与其它的Windows程序设计领域一样,可用API或MFC库对Windows Sockets编程。WinSock使用TCP/IP协议,但TCP/IP并不是 WinSock支持的唯一协议,它还支持 Novell的IPX/SPX、Digital的DECNet和除TCP/IP之外的其它协议。在TCP/IP协议组中,TCP是一种面向连接的协议,为用户提供可靠的、全双工的字节流服务,具有确认、流控制、多路复用和同步等功能,适于数据传输。而UDP协议则是无连接的,每个分组都携带完整的目的地址,各分组在系统中独立传送。它不能保证分组的先后顺序,不进行分组出错的恢复与重传,因此不能保证传输的可靠性,但是,它提供高传输效率的数据报服务,适于实时的语音、图像传输、广播消息等网络传输。

本文主要以 MFC下 Windows Sockets编程为例,介绍一般使用方法及注意事项。

2 编程基本流程

MFC下提供两种SOCKET类型供用户使用:SOCK_STREAM、SOCK_DGRAM,相应有两种类CasyncSocket、Csocket可供使用。

Csocket封装的socket使用TCP协议,提供有序的、可靠的、双向的、连接的、无重复并且无记录边界的比特流传输机制。CasyncSocket封装的socket使用UDP协议,支持双向数据流,但并不保证是可靠、有序、无重复的,为面向无连接的数据报传输机制。

MFC下客户和服务器利用面向连接的Csocket进行通信的过程、具体编程细节参考MFC下Chatter、Chatsrvr程序示例。

一般来说,客户方在OnReceive()中处理数据,服务器方在监听Socket的OnAccept()中处理客户连接,并在其中建立数据处理Socket,数据处理Socket在OnReceive()中处理数据。

客户和服务器利用CasyncSocket进行通信的过程,UDP协议下的通信较简单,不需要事先建立连接,而是通过数据定向发送、接收实现服务器/客户通信。

3 编程点滴

3.1 SOCKET创建

3.1.1 一般过程

以MFC下异步SOCKET创建为例:

(1)首先声明SOCKET对象

CAsyncSocket socket。

(2)创建SOCKET

socket.Create(UINT nSocketPort = 0,int nSocketType=SOCK_DGRAM,long lEvent= FD_READ|FD_WRITE|FD_OOB|FD_ACCEPT|FD_CONNECT|FD_CLOSE,LPCTSTR lpszSocketAddress=NULL);

其中,nSocketPort是此socket绑定的端口号,区分不同的应用程序;nSocketType是socket类型,此处应用UDP协议,选用数据报类型;lEvent指明此socket需要反应的消息,可重载相应的消息映射函数做出动作。

下面的讨论主要针对nSocketPort、lpszSocket-Address两个参数的指定,为叙述简便,标识nSocket-Port为端口,标识lpszSocketAddress为IP地址。记住,使用该类进行socket创建后,不要再调用bind()函数,因为MFC的CasyncSocket类已经将socket的create()和bind()封装在一起,形成Create()。

3.1.2 注意事项

(1)在同一计算机、同一应用程序中创建两个CAsyncSocket:socket1、socket2

a.socket1、socket2不能指定同一端口、同一地址;

b.socket1、socket2指定相同端口、但地址必须不同;

c.socket1、socket2指定相同地址、但端口必须不同;

d.若socket1调用Create()创建,指明端口,但未指明IP地址。则socket1的IP地址不能再改变,除非它是已连接的或I/O正在产生,否则得不到地址;socket2不能指定该端口、指定本机任一IP地址创建;socket2可以更换端口、指定本机任一IP地址创建;socket2可以更换端口、不指定IP地址创建。

(2)创建一个CAsyncSocket:socket

不要试图尝试以任何方式再次创建同一个socket,任何方式指端口、IP地址的组合方式,即:

不能以相同端口、不同IP地址再次创建该socket;

不能以相同端口、相同IP地址再次创建该socket;

不能以不同端口、相同IP地址再次创建该socket;

不能以不同端口、不同IP地址再次创建该socket。

3.2 组广播SOCKET的建立

组广播通信可以实现一台或多台机器向网络中的多台机器发送数据信息,且这种方式对发送端来说编程简洁,通过向一个单一组地址发送来实现点对多点通信。

3.2.1 组广播通信原理

TCP/IP协议地址分配中的D类地址即为多目地址(multicast address),范围是224.0.0.0~239.255.255.255。组广播通信双方认知一个D类地址中的组广播地址,发送数据向目的端口。该地址发送数据,接收数据方将本机IP地址加入该组地址中,并在该端口上建立SOCKET等待数据接收。数据以广播方式发送到各台联网计算机,由协议判断该由哪台计算机接收数据,判断的依据就是本机IP地址与组广播地址的关系。

3.2.2 组广播实现

组广播SOCKET的建立涉及到SOCKET属性的设置,即函数

int setsockopt (SOCKET s,int level,int optname,const char FAR * optval,int optlen)

设置了SOCKET的属性,创建组SOCKET时,level设置为IPPROTO_IP,optname设置为IP_ADD_MEMBERSHIP,optval类型为struct ip_mreq,组地址和本机地址就在该结构中指定,下面具体给出实例,函数和结构的具体信息可查VC帮助。

//接收方加入组地址实例

CAsyncSocket Socket; //声明异步SOCKET

Socket.Create(5000,SOCK_DGRAM); //以端口5000,缺省地址创建SOCKET

struct ip_mreq bindGroup; //声明结构

bindGroup.imr_multiaddr.s_addr=inet_addr(“232.20.1.1”); //填充组地址

bindGroup.imr_interface.s_addr=inet_addr(“10.3.17.13”); //填充本机地址

//设置SOCKET属性

int status= setsockopt(Socket->m_hSocket,IPPROTO_IP,

IP_ADD_MEMBERSHIP,

(char*)&bindGroup,sizeof(struct ip_mreq));

if(status==SOCKET_ERROR)//出错处理

int err= GetLastError();

CString mes;

mes.Format("Add Group Failed,Error Code:%d.",err);

MessageBox(mes);

3.3 Winsock的编程特点与异步选择机制

3.3.1 阻塞及其处理方式

在网络通讯中,由于网络拥挤或一次发送的数据量过大等原因,经常会发生交换的数据在短时间内不能传送完,收发数据的函数因此不能返回,这种现象叫做阻塞。Winsock对有可能阻塞的函数提供了两种处理方式:阻塞和非阻塞方式。在阻塞方式下,收发数据的函数在被调用后一直要到传送完毕或者出错才能返回。在阻塞期间,被阻的函数不断会调用系统函数GetMessage()来保持消息循环的正常进行。对于非阻塞方式,函数被调用后立即返回,当传送完成后由Winsock给程序发一个事先约定好的消息。

编程时应尽量使用非阻塞方式。因为在阻塞方式下,用户可能会长时间的等待过程中试图关闭程序,因为消息循环还在起作用,所以程序的窗口可能被关闭。这样当函数从Winsock的动态连接库中返回时,主程序已经从内存中删除,这显然是极其危险的。

3.3.2 异步选择函数 WSAAsyncSelect()的使用

Winsock通过WSAAsyncSelect()自动地设置套接字处于非阻塞方式。使用 Windows Sockets实现Windows网络程序设计的关键就是它提供了对网络事件基于消息的异步存取,用于注册应用程序感兴趣的网络事件。它请求 Windows Sockets DLL在检测到套接字上发生的网络事件时,向窗口发送一个消息。对UDP协议,这些网络事件主要为:

FD_READ期望在套接字收到数据(即读准备好)时接收通知;

FD_WRITE期望在套接字可发送数(即写准备好)时接收通知;

FD_CLOSE期望在套接字关闭时接电通知;消息变量wParam指示发生网络事件的套接字,变量1Param的低字节描述发生的网络事件,高字包含错误码。如在窗口函数的消息循环中均加一个分支:

int ok=sizeof(SOCKADDR);

case wMsg;

switch(1Param)

case FD_READ://套接字上读数据

if(recvfrom(sr.lpPlayData[j],dwDataSize,0,(struct sockaddr FAR*)&there1,

(int FAR*)&ok)==SOCKET_ERROR0

MessageBox}hwnd,“数据接收失败!”,“”,MB_OK);

return(FALSE);

case FD_WRITE://套接字上写数据

break;

在程序的编制中,应根据需要灵活地将WSAAsyncSelect()函数放在相应的消息循环之中。同时,按照程序容错误设计,应建立一个专门的容错处理函数。程序中可能出现的各种错误都将由该函数进行处理,依据错误的危害程度不同,建立几种不同的处理措施。这样,才能保证双方通信的顺利和可靠。

[1] [美]Douglas E Comer.Internet Working with TCP/IP[M].北京:电子工业出版社,1998.

[2] 蒋东兴.Windows Sockets网络程序设计大全[M].北京:清华大学出版社,1999.

[3] [美]Kate Gregory.Visual C++5开发使用手册[M].北京:机械工业出版社,1998.

[4] [美]Robert D Thompson.MFC开发人员参考手册[M].前导工作室,1998.

猜你喜欢
IP地址端口应用程序
一种有源二端口网络参数计算方法
一种端口故障的解决方案
铁路远动系统几种组网方式IP地址的申请和设置
多按键情况下,单片机端口不足的解决方法
删除Win10中自带的应用程序
谷歌禁止加密货币应用程序
公安网络中IP地址智能管理的研究与思考
《IP地址及其管理》教学设计
卫星三端口DC-DC变换器技术综述
三星电子将开设应用程序下载商店