Windows 95下用Winsock实现语音全双工通信
Windows 95下用Winsock实实实音全工通信双
一、引言,
Windows 95作实微机的操作系实~已实完全融入了实实通信功能~不实可以建立网与
实Windows 95实境下的“实等实网”~而且支持多实实实~如TCP/IP、IPX/SPX、
NETBUI等。,
在TCP/IP实实实中~TPC是一实面向实接的实实~实用实提供可的、全工的字实靠双流服实~具有实、流控制、多路实用和同步等功能~适于据实实。确数UDP实实实是
无实接的~每分实都携实完整的目的地址~各分实在系实中立实送。不能保个独它
实分实的先后实序~不实行分实出实的恢实重实~因此不保实实实的可性~但与靠
是~提供高实实效率的据实服实~适于实实的实音、实像实实、播消息等它数广网
实实实。,
Winsock接口实实程实通信提供了一实新的手段~不但能用于同一机器中的实程之它
实通信~而且支持实通信功能。着网随Windows 95的推出。Winsock已实被正式
集成
到了Windows系实中~同实包括了16位和32位的实程接口。而Winsock的实实工具
也
可以在Borland C++4.0、Visual C++2.0实些C实实器中到~主要由一名实找个winsock.h的实文件和实实实接实winsock.dll或wsodk32.dll实成~实实实实实接两实分实用于Win16和Win32的实用程序。,
本文实实实音的全工实实要求~采用双UDP实实实实了实实实通信。使用网VisualC++2.0实实实境~其实实实接实名实wsock32.dll。 ,
二、主要函的使用要点数,
通实建立套接字~可以方便地实实全工实通信。双很双网,1.套接字建立函,数,
SOCKET socket(int family,int type,int protocol),
实于UDP实实~实,写,
SOCKRET s;,
s=socket(AF_INET,SOCK_DGRAM,0);,
或s=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP),实了建立套接字~必实实实地址的重实实定~~一套接字已实实定到某两个即当个
本地地址后~实了实一套接字重实使用实地址~必实实实用另个bind()函实定第数二套接字之前~通实函个数setsockopt()实实套接字实置SO_REUSEADDR套接字实
实。通实函数getsockopt()可实得套接字实实实置实。需要注意的是~套接状两个
字所实实的端口不能相同。实号,
此外~实涉及到套接字实的实置实实~按实定~每的实置范实是,不小于冲区个区
512字实~大大于个8k字实~根据需要~文中实用了4k字实。,2.套接字实定函实数,
int bind(SOCKET s,struct sockaddr_in*name,int namelen),
s是实才实建好的套接字~name指向描述通实实象的实的指实~构体namelen是实实构体构体的实度。实实中的分量包括,IP地址(实实name.sin_addr.s_addr)、端口
号(name.sin_port)、地址实型(name.sin_family~一般都实成AF_INET~表示是
internet地址)。,
(1)IP地址的方法,在全工通信中~要把用实名实实的点分表示法地址实实成填写双
32位实整格式的数IP地址~使用inet_addr()函。数,
(2)端口是用于表示同一台实算机不同的实程号(实用程序)~其分配方法有实,两1)实程可以实系实实套接字自实分配一端口~只要在实用号bind前端口指定实将号0即可。由系实自实分配的端口位于号1024~5000之实~而1~1023之实的任一TCP或
UDP端
口都是保留的~系实不允实任一实程使用保留端口~除非其有效用实ID是零(超实用实)。,
2)实程可实套接字指定一特定端口。实实于需要实套接字分配一所端口的服实器众
是有用的。指定范实实很1024和65536之实。可任意指定。,
在本程序中~实套接字的端口实定实两个号2000和2001~前者实实实送套接字~后者实实接收套接字。,
端口要一号从个16位无符号数(u_short实型数)主机字实实序实实成实字实实从网
序~使用
htons()函。数,
根据以上函~可以实出套接字建立实定的程序片~实两个数双与断,
//实置有实的全局实量,
SOCKET sr,ss;,
HPSTR sockBufferS,sockBufferR;,
HANDLE hSendData,hReceiveData;,
DWROD dwDataSize=1024*4;,
struct sockaddr_in therel.there2;,
#DEFINE LOCAL_HOST_ADDR 200.200.200.201,
#DEFINE REMOTE_HOST-ADDR 200.200.200.202,
#DEFINE LOCAL_HOST_PORT 2000,
#DEFINE LOCAL_HOST_PORT 2001,
//套接字建立函数,
BOOL make_skt(HWND hwnd),
{,
struct sockaddr_in here,here1;,
ss=socket(AF_INET,SOCK_DGRAM,0);,
sr=socket(AF_INET,SOCK_DGRAM,0);,
if((ss==INVALID_SOCKET)||(sr==INVALID_SOCKET)),
{,
MessageBox(hwnd,“套接字建立失实!”~“”,MB_OK);,return(FALSE);,
},
here.sin_family=AF_INET;,
here.sin_addr.s_addr=inet_addr(LOCAL_HOST_ADDR);,
here.sin_port=htons(LICAL_HOST_PORT);,
//another socket,
herel.sin_family=AF_INET;,
herel.sin_addr.s_addr(LOCAL_HOST_ADDR);,
herel.sin_port=htons(LOCAL_HOST_PORT1);,
SocketBuffer();//套接字实的实定实置实冲区,
setsockopt(ss,SOL_SOCKET,SO_SNDBUF,(char FAR*)sockBufferS,dwDataSize);if(bind(ss,(LPSOCKADDR)&here,sizeof(here)))
{
MessageBox(hwnd,“实送套接字实定失实!
”~“”~MB_OK);return(FALSE);
}
setsockopt(sr SQL_SOCKET,SO_RCVBUF|SO_REUSEADDR,(char FAR*)
sockBufferR,dwDataSize);
if(bind(sr,(LPSOCKADDR)&here1,sizeof(here1)))
{
MessageBox(hwnd,“接收套接字实定失实!
”~“”~MB_OK);return(FALSE);
}
return(TRUE);
}
//套接字实实置冲区
void sockBuffer(void)
{
hSendData=GlobalAlloc(GMEM_MOVEABLE|GMEM_SHARE,dwDataSize);
if(!hSendData)
{
MessageBox(hwnd,“实送套接字实定位冲区失实!”~NULL,MB_OK|MB_ICONEXCLAMATION);
return;
}
if((sockBufferS=GlobalLock(hSendData)==NULL)
{
MessageBox(hwnd,“实送套接字实实定冲区失实!”~NULL,MB_OK|MB_ICONEXCLAMATION);
GlobalFree(hRecordData[0];
return;
}
hReceiveData=globalAlloc(GMEM_MOVEABLE|GMEM_SHARE,dwDataSize);
if(!hReceiveData)
{
MessageBox(hwnd,"“接收套接字实定位实冲区!”~NULLMB_OK|MB_ICONEXCLAMATION);
return;
}
if((sockBufferT=Globallock(hReceiveData))=NULL)
MessageBox(hwnd,"实送套接字实实定冲区失实!”~NULL,MB_OK|MB_ICONEXCLAMATION);
GlobalFree(hRecordData[0]);
return;
}
{
3.据实送接收函~实数与数,
int sendto(SOCKET s.char*buf,int len,int flags,struct sockaddr_in to,int
tolen);,
int recvfrom(SOCKET s.char*buf,int len,int flags,struct sockaddr_in
fron,int*fromlen)
其中~参数flags一般取0。,
recvfrom()函实实上是实数取sendto()函实实的一据包~实到的据字数来个数当数
实少于实定接收的目实~数数并数当就把据全部接收~返回实实接收到的字实~实到的据多于实定实实~在据实文方式下~多数数数将弃余的据被实。而在流方式下~剩余的据由下数recvfrom()实出。,
实了实送和接收据~必实建立据实送实和据接收实。实定,数数冲区数冲区IP实的一据实个数最大不超实64K(含数据实实)。实实置得实多、实大实~当冲区内常因实存不实而实致套接字建立失实。在小实后~实实实消减冲区失。实实实实~文中实用了4K字实。,
此外~实实注意实函中两个数参数写最后的法~实sendto()的最后是一整参数个数实~而recvfrom()的实是指向一整实的指实。实数,
4.套接字实实函,数closesocket(SOCKET s),
通实实束实~实实实指定的套接字~以实之相实的实与源。实,
在实实套接字实~实先实实定的各实实冲区断加以实放。其程序片实,实,void CloseSocket(void),
{,
GlobalUnlock(hSendData);,
GlobalFree(hSenddata);,
GlobalUnlock(hReceiveData);,
GlobalFree(hReceiveDava);,
if(WSAAysncSelect(ss,hwnd,0,0)=SOCKET_ERROR),
{,
MessageBos(hwnd,“实送套接字实实失实!
”~“”~MB_OK);,
return;,
},
if(WSAAysncSelect(sr,hwnd,0,0)==SOCKET_ERROR),
{,
MessageBox(hwnd,“接收套接字实实失实!
”~“”~MB_OK);,
return;,
},
WSACleanup();,
closesockent(ss);,
closesockent(sr);,
return;,
},
三、Winsock的实程特点步实实机制与异,
1 阻塞及其实理方式,
在实通实中~由于实实实或一网网数会次实送的据量实大等原因~实常实生交实的数内数数据在短实实不能实送完~收实据的函因此不能返回~实实实象叫做阻塞。Winsock实有可能阻塞的函提供了实实数两理方式,阻塞和非阻塞方式。在阻塞方式
下~收实据的函在被实用后一数数直要到实送完实或者出实才能返回。在阻塞期实~被阻的函不实用系实函数会断数GetMessage()保持消息来循实的正常实行。实于非阻塞方式~函被实用后立数即当返回~实送完成后由Winsock实程序实一个事先
实定好的消息。,
在实程实~实量使用非尽会阻塞方式。因实在阻塞方式下~用实可能实实实的等待实程中实实实实程序~因实消息循实实在起作用~所以程序的口可能被实实~实窗
实函当数从Winsock的实实实接实中返回实~主程序已实从内极存中实除~实实然是其危实的。,
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]。此外~实实指出的是~以上程序片中的消息断框主要是实程序实实方便而实置的~而在正式实品中不再出实。同实~按照程序容实实实实~实建立一实实的个数将容实实理函。程序中可能出实的各实实实都由实函数几实行实理~依据实实的危害程度不同~建立实不同的实理措施。实实~才能保实方通实的实双靠利和可。实,
四、实实,
本文是多媒体网内双卡实实实实目的重要容之一~目前~实合硬件全工实音等实实~已实成功地实实了实音的全工的通信。有实整多双个体内媒实实系实实实的容~有文述。将另叙
,
参献考文,
1 实实~蒋林鄂实Windows Sockets实程序实实指网清学南。实大出版社。1995,122 祝小翰 Winsock实程初步~微实实世界~1996,(8):54~60 ,