??xml version="1.0" encoding="utf-8" standalone="yes"?>久久伊人精品一区二区三区,午夜精品久久久久久久无码,综合久久一区二区三区 http://www.shnenglu.com/eday/category/3561.htmlzh-cnSat, 24 May 2008 00:43:07 GMTSat, 24 May 2008 00:43:07 GMT60用Winsock实现语音全双工通信使用http://www.shnenglu.com/eday/archive/2007/01/31/18209.html??Wed, 31 Jan 2007 03:47:00 GMThttp://www.shnenglu.com/eday/archive/2007/01/31/18209.html
  一、引a?br />
  Windows 95作ؓ微机的操作系l,已经完全融入了网l与通信功能Q不仅可以徏立纯Windows 95环境下的“对{网l”,而且支持多种协议Q如TCP/IP、IPX/SPX、NETBUI{。在TCP/IP协议l中QTPC是一U面向连接的协义Qؓ用户提供可靠的、全双工的字节流服务Q具有确认、流控制、多路复用和同步{功能,适于数据传输。UDP协议则是无连接的Q每个分l都携带完整的目的地址Q各分组在系l中独立传送。它不能保证分组的先后顺序,不进行分l出错的恢复与重传,因此不保证传输的可靠性,但是Q它提供高传输效率的数据报服务,适于实时的语韟뀁图像传输、广播消息等|络传输?br />
  Winsock接口E间通信提供了一U新的手D,它不但能用于同一机器中的q程之间通信Q而且支持|络通信功能。随着Windows 95的推出。Winsock已经被正式集成到了Windowspȝ中,同时包括?6位和32位的~程接口。而Winsock的开发工具也可以在Borland C++4.0、Visual C++2.0q些C~译器中扑ֈQ主要由一个名为winsock.h的头文g和动态连接库winsock.dll或wsodk32.dlll成Q这两种动态连接库分别用于Win16和Win32的应用程序。?br />
  本文针对话音的全双工传输要求Q采用UDP协议实现了实时网l通信。用VisualC++2.0~译环境Q其动态连接库名ؓwsock32.dll。?

  二、主要函数的使用要点?/b>

  通过建立双套接字Q可以很方便地实现全双工|络通信。?br />
  1.套接字徏立函敎ͼ?br />

SOCKET socket(int family,int type,int protocol)?

  对于UDP协议Q写为:?br />
SOCKRET s;?br />s=socket(AF_INET,SOCK_DGRAM,0);?br />或s=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP)?/td>

  Z建立两个套接字,必须实现地址的重复绑定,卻I当一个套接字已经l定到某本地地址后,Z让另一个套接字重复使用该地址Q必Mؓ调用bind()函数l定W二个套接字之前Q通过函数setsockopt()套接字设|SO_REUSEADDR套接字选项。通过函数getsockopt()可获得套接字选项讄状态。需要注意的是,两个套接字所对应的端口号不能相同。此外,q涉及到套接字缓冲区的设|问题,按规定,每个区的讄范围是:不小?12个字节,大大?k字节Q根据需要,文中选用?k字节。?br />
  2.套接字绑定函数?br />
int bind(SOCKET s,struct sockaddr_in*name,int namelen)?/td>

  s是刚才创建好的套接字Qname指向描述通讯对象的结构体的指针,namelen是该l构体的长度。该l构体中的分量包括:IP地址(对应name.sin_addr.s_addr)、端口号(name.sin_port)、地址cd(name.sin_familyQ一般都赋成AF_INETQ表C是internet地址)。?br />
  (1)IP地址的填写方法:在全双工通信中,要把用户名对应的点分表示法地址转换?2位长整数格式的IP地址Q用inet_addr()函数。?br />
  (2)端口h用于表示同一台计机不同的进E?应用E序)Q其分配Ҏ有两U:1)q程可以让系lؓ套接字自动分配一端口P只要在调用bind前将端口h定ؓ0卛_。由pȝ自动分配的端口号位于1024~5000之间Q?~1023之间的Q一TCP或UDP端口都是保留的,pȝ不允怓Q一q程使用保留端口Q除非其有效用户ID是零(用户)。?br />
  2)q程可ؓ套接字指定一特定端口。这对于需要给套接字分配一众所端口的服务器是很有用的。指定范围ؓ1024?5536之间。可L指定。?br />
  在本E序中,对两个套接字的端口号规定?000?001Q前者对应发送套接字Q后者对应接收套接字?br />
  端口可从一?6位无W号?u_shortcd?从主机字节顺序{换成|络字节序Q用htons()函数。?br />
  Ҏ以上两个函数Q可以给出双套接字徏立与l定的程序片断?br />
//讄有关的全局变量?br />SOCKET sr,ss;?br />HPSTR sockBufferS,sockBufferR;?br />HANDLE hSendData,hReceiveData;?br />DWROD dwDataSize=1024*4;?br />struct sockaddr_in therel.there2;?br />#DEFINE LOCAL_HOST_ADDR 200.200.200.201?br />#DEFINE REMOTE_HOST-ADDR 200.200.200.202?br />#DEFINE LOCAL_HOST_PORT 2000?br />#DEFINE LOCAL_HOST_PORT 2001?br />//套接字徏立函数?
BOOL make_skt(HWND hwnd)?br />{?br />struct sockaddr_in here,here1;?br />ss=socket(AF_INET,SOCK_DGRAM,0);?br />sr=socket(AF_INET,SOCK_DGRAM,0);?br />if((ss==INVALID_SOCKET)||(sr==INVALID_SOCKET))?br />{?br />MessageBox(hwnd,“套接字建立p|!”,“?MB_OK);?br />return(FALSE);?br />}?br />here.sin_family=AF_INET;?br />here.sin_addr.s_addr=inet_addr(LOCAL_HOST_ADDR);?br />here.sin_port=htons(LICAL_HOST_PORT);?br />//another socket?br />herel.sin_family=AF_INET;?br />herel.sin_addr.s_addr(LOCAL_HOST_ADDR);?br />herel.sin_port=htons(LOCAL_HOST_PORT1);?br />SocketBuffer();//套接字缓冲区的锁定设|?br />setsockopt(ss,SOL_SOCKET,SO_SNDBUF,(char FAR*)sockBufferS,dwDataSize);
if(bind(ss,(LPSOCKADDR)&here,sizeof(here)))
{
MessageBox(hwnd,“发送套接字l定p|!”,“”,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,“接收套接字l定p|!”,“”,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,"“接收套接字~冲区定位|!”,NULL
MB_OK|MB_ICONEXCLAMATION);
return;
}
if((sockBufferT=Globallock(hReceiveData))=NULL)
MessageBox(hwnd,"发送套接字~冲区锁定失?”,NULL,
MB_OK|MB_ICONEXCLAMATION);
GlobalFree(hRecordData[0]);
return;
}
{

  3.数据发送与接收函数Q?br />
int sendto(SOCKET s.char*buf,int len,int flags,struct sockaddr_in to,int
tolen);?br />int recvfrom(SOCKET s.char*buf,int len,int flags,struct sockaddr_in
fron,int*fromlen)

  其中Q参数flags一般取0。?br />
  recvfrom()函数实际上是dsendto()函数发过来的一个数据包Q当d的数据字节少于规定接收的数目Ӟ把数据全部接收Qƈq回实际接收到的字节敎ͼ当读到的数据多于规定值时Q在数据报文方式下,多余的数据将被丢弃。而在方式下Q剩余的数据׃recvfrom()d。ؓ了发送和接收数据Q必d立数据发送缓冲区和数据接收缓冲区。规定:IP层的一个数据报最大不过64K(含数据报?。当~冲|得q多、过大时Q常因内存不够而导致套接字建立p|。在减小~冲区后Q该错误消失。经q实验,文中选用?K字节。?br />
  此外Q还应注意这两个函数中最后参数的写法Q给sendto()的最后参数是一个整数|而recvfrom()的则是指向一整数值的指针。?br />
  4.套接字关闭函敎ͼclosesocket(SOCKET s)?br />
  通讯l束Ӟ应关闭指定的套接字,以释与之相关的资源。?br />
  在关闭套接字Ӟ应先寚w定的各种~冲区加以释放。其E序片断为:?br />
void CloseSocket(void)?br />{?br />GlobalUnlock(hSendData);?br />GlobalFree(hSenddata);?br />GlobalUnlock(hReceiveData);?br />GlobalFree(hReceiveDava);?br />if(WSAAysncSelect(ss,hwnd,0,0)=SOCKET_ERROR)?br />{?br />MessageBos(hwnd,“发送套接字关闭p|!”,“”,MB_OK);?br />return;?br />}?br />if(WSAAysncSelect(sr,hwnd,0,0)==SOCKET_ERROR)?br />{?
MessageBox(hwnd,“接收套接字关闭p|!”,“”,MB_OK);?br />return;?br />}?br />WSACleanup();?br />closesockent(ss);?br />closesockent(sr);?br />return;?br />}?/td>

  三、Winsock的编E特点与异步选择机制?/b>

  1 d及其处理方式?br />
  在网l通讯中,׃|络拥挤或一ơ发送的数据量过大等原因Q经怼发生交换的数据在短时间内不能传送完Q收发数据的函数因此不能q回Q这U现象叫做阻塞。WinsockҎ可能d的函数提供了两种处理方式Q阻塞和非阻塞方式。在d方式下,收发数据的函数在被调用后一直要C送完毕或者出错才能返回。在d期间Q被ȝ函数不会断调用系l函数GetMessage()来保持消息@环的正常q行。对于非d方式Q函数被调用后立卌回,当传送完成后由Winsockl程序发一个事先约定好的消息。?br />
  在编E时Q应量使用非阻塞方式。因为在d方式下,用户可能会长旉的等待过E中试图关闭E序Q因为消息@环还在v作用Q所以程序的H口可能被关闭,q样当函CWinsock的动态连接库中返回时Q主E序已经从内存中删除Q这昄是极其危险的。?br />
  2 异步选择函数WSAAsyncSelect()的用?br />
  Winsock通过WSAAsyncSelect()自动地设|套接字处于非阻塞方式。用WindowsSockets实现Windows|络E序设计的关键就是它提供了对|络事gZ消息的异步存取,用于注册应用E序感兴的|络事g。它hWindows Sockets DLL在检到套接字上发生的网l事件时Q向H口发送一个消息。对UDP协议Q这些网l事件主要ؓQ?br />
  FD_READ 期望在套接字收到数据(卌准备?时接攉知Q?br />
  FD_WRITE 期望在套接字可发送数(卛_准备?时接攉知Q?br />
  FD_CLOSE 期望在套接字关闭时接电通知?br />
  消息变量wParam指示发生|络事g的套接字Q变?Param的低字节描述发生的网l事Ӟ高字包含错误码。如在窗口函数的消息循环中均加一个分支:?br />
int ok=sizeof(SOCKADDR);?br />case wMsg;?br />switch(1Param)?br />{?br />case FD_READ:?br />//套接字上L据?br />if(recvfrom(sr.lpPlayData[j],dwDataSize,0,(struct sockaddr FAR*)&there1,
?br />(int FAR*)&ok)==SOCKET_ERROR0?br />{?br />MessageBox)hwnd,“数据接收失?”,“”,MB_OK);?br />return(FALSE);?br />}?br />case FD_WRITE:?br />//套接字上写数据?br />}?br />breakQ?/td>

  在程序的~制中,应根据需要灵zdWSAAsyncSelect()函灵敏放在相应的消息循环之中Q其它说明可参见文献[1]。此外,应该指出的是Q以上程序片断中的消息框主要是ؓE序调试方便而设|的Q而在正式产品中不再出现。同Ӟ按照E序定w误设计,应徏立一个专门的定w处理函数。程序中可能出现的各U错误都由该函数进行处理,依据错误的危害程度不同,建立几种不同的处理措施。这P才能保证双方通话的顺利和可靠。?br />
  四、结论?/b>

  本文是多媒体|络传输目的重要内容之一Q目前,l合g全双工语韛_{设备,已经成功地实C话音的全双工的通信。有x个多媒体传输pȝ设计的内容,有另文叙述?



? 2007-01-31 11:47 发表评论
]]>
用VC++6.0的Sockets API实现一个聊天室E序http://www.shnenglu.com/eday/archive/2007/01/31/18207.html??Wed, 31 Jan 2007 03:34:00 GMThttp://www.shnenglu.com/eday/archive/2007/01/31/18207.html1.VC++|络~程及Windows Sockets API?/font>

  VC++对网l编E的支持有socket支持QWinInet支持QMAPI和ISAPI支持{。其中,Windows Sockets API是TCP/IP|络环境里,也是Internet上进行开发最为通用的API。最早美国加州大学Berkeley分校在UNIX下ؓTCP/IP协议开发了一个APIQ这个API是著名的Berkeley Socket接口(套接?。在桌面操作pȝq入Windows时代后,仍然l承了SocketҎ。在TCP/IP|络通信环境下,Socket数据传输是一U特D的I/OQ它也相当于一U文件描q符Q具有一个类g打开文g的函数调?socket()。可以这L解:Socket实际上是一个通信端点Q通过它,用户的SocketE序可以通过|络和其他的Socket应用E序通信。Socket存在于一?通信?(为描qC般的U程如何通过Socketq行通信而引入的一U抽象概?里,q且与另一个域的Socket交换数据。Socket有三cR第一U是SOCK_STREAM(式)Q提供面向连接的可靠的通信服务Q比如telnet,http。第二种是SOCK_DGRAM(数据?Q提供无q接不可靠的通信Q比如UDP。第三种是SOCK_RAW(原始)Q主要用于协议的开发和试Q支持通信底层操作Q比如对IP和ICMP的直接访问?br />
  2.Windows Socket机制分析

  2.1一些基本的Socketpȝ调用

  主要的系l调用包括:socket()-创徏SocketQbind()-创建的Socket与本地端口绑定;connect()与accept()-建立Socketq接Qlisten()-服务器监听是否有q接hQsend()-数据的可控缓冲发送;recv()-可控~冲接收Qclosesocket()-关闭Socket?br />
  2.2Windows Socket的启动与l止

  启动函数WSAStartup()建立与Windows Sockets DLL的连接,l止函数WSAClearup()l止使用该DLLQ这两个函数必须成对使用?br />
  2.3异步选择机制

  Windows是一个非抢占式的操作pȝQ而不采取UNIX的阻塞机制。当一个通信事g产生Ӟ操作pȝ要根据设|选择是否对该事g加以处理QWSAAsyncSelect()函数是用来选择pȝ所要处理的相应事g。当Socket收到讑֮的网l事件中的一个时Q会l程序窗口一个消息,q个消息里会指定产生|络事g的SocketQ发生的事gcd和错误码?br />
  2.4异步数据传输机制

  WSAAsyncSelect()讑֮了Socket上的d应通信事g后,每发生一个这L事g׃产生一个WM_SOCKET消息传给H口。而在H口的回调函C应该添加相应的数据传输处理代码?br />
  3.聊天室程序的设计说明

  3.1实现思想

  在Internet上的聊天室程序一般都是以服务器提供服务端q接响应Q用者通过客户端程序登录到服务器,可以与d在同一服务器上的用户交谈,q是一个面向连接的通信q程。因此,E序要在TCP/IP环境下,实现服务器端和客L两部分程序?br />
  3.2服务器端工作程

  服务器端通过socket()pȝ调用创徏一个Socket数组?卌定了接受q接客户的最大数?Q与指定的本地端口绑定bind()Q就可以在端口进行侦听listen()。如果有客户端连接请求,则在数组中选择一个空SocketQ将客户端地址赋给q个Socket。然后登录成功的客户可以在服务器上聊天了?br />
  3.3客户端工作流E?br />
  客户端程序相对简单,只需要徏立一个Socket与服务器端连接,成功后通过q个Socket来发送和接收数据可以了?br />
  4.核心代码分析

  限于幅Q这里仅l出与网l编E相关的核心代码Q其他的诸如聊天文字的服务器和客L昄读者可以自行添加?br />
  4.1服务器端代码

  开启服务器功能:

void OnServerOpen() //开启服务器功能
{
 WSADATA wsaData;
 int iErrorCode;
 char chInfo[64];
 if (WSAStartup(WINSOCK_VERSION, &wsaData)) //调用Windows Sockets DLL
  { MessageBeep(MB_ICONSTOP);
   MessageBox("Winsock无法初始?", AfxGetAppName(), MB_OK|MB_ICONSTOP);
   WSACleanup();
   return; }
 else
  WSACleanup();
  if (gethostname(chInfo, sizeof(chInfo)))
  { ReportWinsockErr("\n无法获取L!\n ");
   return; }
  CString csWinsockID = "\n==>>服务器功能开启在端口QNo. ";
  csWinsockID += itoa(m_pDoc->m_nServerPort, chInfo, 10);
  csWinsockID += "\n";
  PrintString(csWinsockID); //在程序视图显C提CZ息的函数Q读者可自行创徏
  m_pDoc->m_hServerSocket=socket(PF_INET, SOCK_STREAM, DEFAULT_PROTOCOL);
  //创徏服务器端SocketQ类型ؓSOCK_STREAMQ面向连接的通信
  if (m_pDoc->m_hServerSocket == INVALID_SOCKET)
  { ReportWinsockErr("无法创徏服务器socket!");
   return;}
  m_pDoc->m_sockServerAddr.sin_family = AF_INET;
  m_pDoc->m_sockServerAddr.sin_addr.s_addr = INADDR_ANY;
  m_pDoc->m_sockServerAddr.sin_port = htons(m_pDoc->m_nServerPort);
  if (bind(m_pDoc->m_hServerSocket, (LPSOCKADDR)&m_pDoc->m_sockServerAddr,   
     sizeof(m_pDoc->m_sockServerAddr)) == SOCKET_ERROR) //与选定的端口绑?br />   {ReportWinsockErr("无法l定服务器socket!");
    return;}
   iErrorCode=WSAAsyncSelect(m_pDoc->m_hServerSocket,m_hWnd,
   WM_SERVER_ACCEPT, FD_ACCEPT);
   //讑֮服务器相应的|络事g为FD_ACCEPTQ即q接hQ?br />   // 产生相应传递给H口的消息ؓWM_SERVER_ACCEPT
  if (iErrorCode == SOCKET_ERROR)
   { ReportWinsockErr("WSAAsyncSelect讑֮p|!");
    return;}
  if (listen(m_pDoc->m_hServerSocket, QUEUE_SIZE) == SOCKET_ERROR) //开始监听客戯接请?br />   {ReportWinsockErr("服务器socket监听p|!");
    m_pParentMenu->EnableMenuItem(ID_SERVER_OPEN, MF_ENABLED);
    return;}
  m_bServerIsOpen = TRUE; //监视服务器是否打开的变?br /> return;
}

  响应客户发送聊天文字到服务器:ON_MESSAGE(WM_CLIENT_READ, OnClientRead)

LRESULT OnClientRead(WPARAM wParam, LPARAM lParam)
{
 int iRead;
 int iBufferLength;
 int iEnd;
 int iRemainSpace;
 char chInBuffer[1024];
 int i;
 for(i=0;(i
  //MAXClient是服务器可响应连接的最大数?br />  {}
 if(i==MAXClient) return 0L;
  iBufferLength = iRemainSpace = sizeof(chInBuffer);
  iEnd = 0;
  iRemainSpace -= iEnd;
  iBytesRead = recv(m_aClientSocket[i], (LPSTR)(chInBuffer+iEnd), iSpaceRemaining, NO_FLAGS);   //用可控缓冲接收函数recv()来接收字W?br />  iEnd+=iRead;
 if (iBytesRead == SOCKET_ERROR)
  ReportWinsockErr("recv出错!");
  chInBuffer[iEnd] = '\0';
 if (lstrlen(chInBuffer) != 0)
  {PrintString(chInBuffer); //服务器端文字昄
   OnServerBroadcast(chInBuffer); //自己~写的函敎ͼ向所有连接的客户q播q个客户的聊天文?br />  }
 return(0L);
}

  对于客户断开q接Q会产生一个FD_CLOSE消息Q只ȝ应地用closesocket()关闭相应的Socket卛_Q这个处理比较简单?br />
  4.2客户端代?br />
  q接到服务器Q?br />
void OnSocketConnect()
{ WSADATA wsaData;
 DWORD dwIPAddr;
 SOCKADDR_IN sockAddr;
 if(WSAStartup(WINSOCK_VERSION,&wsaData)) //调用Windows Sockets DLL
 {MessageBox("Winsock无法初始?",NULL,MB_OK);
  return;
 }
 m_hSocket=socket(PF_INET,SOCK_STREAM,0); //创徏面向q接的socket
 sockAddr.sin_family=AF_INET; //使用TCP/IP协议
 sockAddr.sin_port=m_iPort; //客户端指定的IP地址
 sockAddr.sin_addr.S_un.S_addr=dwIPAddr;
 int nConnect=connect(m_hSocket,(LPSOCKADDR)&sockAddr,sizeof(sockAddr)); //hq接
 if(nConnect)
  ReportWinsockErr("q接p|!");
 else
  MessageBox("q接成功!",NULL,MB_OK);
  int iErrorCode=WSAAsyncSelect(m_hSocket,m_hWnd,WM_SOCKET_READ,FD_READ);
  //指定响应的事Ӟ为服务器发送来字符
 if(iErrorCode==SOCKET_ERROR)
 MessageBox("WSAAsyncSelect讑֮p|!");
}

  接收服务器端发送的字符也用可控缓冲接收函数recv()Q客L聊天的字W发送用数据可控缓冲发送函数send()Q这两个q程比较单,在此׃加赘qC?br />
  5.结

  通过聊天室程序的~写Q可以基本了解Windows Sockets API~程的基本过E和_要之处。本E序在VC++6.0下编译通过Q在使用windows 98/NT的局域网里运行良好?br />


? 2007-01-31 11:34 发表评论
]]>
Windows Sockets API实现|络异步通讯http://www.shnenglu.com/eday/archive/2007/01/31/18206.html??Wed, 31 Jan 2007 03:32:00 GMThttp://www.shnenglu.com/eday/archive/2007/01/31/18206.html摘要Q?/strong>本文对如何用面向连接的式套接字实现对|卡的编E以及如何实现异步网l通讯{问题进行了讨论与阐q?

  一?引言

  ?0q代初,国加利尼亚大学伯克利分校的研Ih员ؓTCP/IP|络通信开发了一个专门用于网l通讯开发的API。这个API是Socket接口Q套接字Q?-当今在TCP/IP|络最为通用的一UAPIQ也是在互联|上q行应用开发最为通用的一UAPI。在微Y联合其它几家公司共同制定了一套Windows下的|络~程接口Windows Sockets规范后,׃在其规范中引入了一些异步函敎ͼ增加了对|络事g异步选择机制Q因此更加符合Windows的消息驱动特性,使网l开发h员可以更加方便的q行高性能|络通讯E序的设计。本文接下来针对Windows Sockets APIq行面向q接的流式套接字~程以及对异步网l通讯的编E实现等问题展开讨论?br />
  二?面向q接的流式套接字~程模型的设?/b>

  本文在方案选择上采用了在网l编E中最常用的一U模?-客户?服务器模型。这U客?服务器模型是一U非对称式编E模式。该模式的基本思想是把集中在一L应用划分成ؓ功能不同的两个部分,分别在不同的计算Zq行Q通过它们之间的分工合作来实现一个完整的功能。对于这U模式而言其中一部分需要作为服务器Q用来响应ƈ为客h供固定的服务Q另一部分则作为客hE序用来向服务器提出h或要求某U服务?br />
  本文选取了基于TCP/IP的客h/服务器模型和面向q接的流式套接字。其通信原理为:服务器端和客L都必d立通信套接字,而且服务器端应先q入监听状态,然后客户端套接字发出q接hQ服务器端收到请求后Q徏立另一个套接字q行通信Q原来负责监听的套接字仍q行监听Q如果有其它客户发来q接hQ则再徏立一个套接字。默认状态下最多可同时接收5个客Lq接hQƈ与之建立通信关系。因此本E序的设计流E应当由服务器首先启动,然后在某一时刻启动客户机ƈ使其与服务器建立q接。服务器与客h开始都必须调用Windows Sockets API函数socket()建立一个套接字sockets,然后服务器方调用bind()套接字与一个本地网l地址捆扎在一P再调用listen()使套接字处于一U被动的准备接收状态,同时规定它的h队列长度。在此之后服务器可以通过调用accept()来接收客h的连接?br />
  相对于服务器Q客L的工作就昑־比较单了Q当客户端打开套接字之后,便可通过调用connect()和服务器建立q接。连接徏立之后,客户和服务器之间可以通过q接发送和接收资料。最后资料传送结束,双方调用closesocket()关闭套接字来l束q次通讯。整个通讯q程的具体流E框囑֏大致用下面的程图来表示Q?br />

        面向q接的流式套接字~程程C意?

三?软g设计要点以及异步通讯的实?br />
  Ҏ前面设计的程序流E,可将E序划分Z部分Q服务器端和客户端。而且整个实现q程可以大致用以下几个非常关键的Windows Sockets API函数其惯穿下来Q?br />
  服务器方Q?br />
socket()->bind()->listen->accept()->recv()/send()->closesocket()

  客户机方Q?br />
socket()->connect()->send()/recv()->closesocket()

  有鉴于以上几个函数在整个|络~程中的重要性,有必要结合程序实例对其做较深入的剖析。服务器端应用程序在使用套接字之前,首先必须拥有一个SocketQ系l调用socket()函数向应用程序提供创建套接字的手Dc该套接字实际上是在计算Z提供了一个通信埠,可以通过q个埠与M一个具有套接字接口的计机通信。应用程序在|络上传输、接收的信息都通过q个套接字接口来实现的。在应用开发中如同使用文g句柄一P可以对套接字句柄q行d操作Q?br />
sock=socket(AF_INET,SOCK_STREAM,0);

  函数的第一个参数用于指定地址族,在Windows下仅支持AF_INET(TCP/IP地址)Q第二个参数用于描述套接字的cdQ对于流式套接字提供有SOCK_STREAMQ最后一个参数指定套接字使用的协议,一般ؓ0。该函数的返回g存了新套接字的句柄,在程序退出前可以?closesocket(sock);函数来将光放。服务器方一旦获取了一个新的套接字后应通过bind()该套接字与本机上的一个端口相兌Q?br />
sockin.sin_family=AF_INET;
sockin.sin_addr.s_addr=0;
sockin.sin_port=htons(USERPORT);
bind(sock,(LPSOCKADDR)&sockin,sizeof(sockin)));

  该函数的W二个参数是一个指向包含有本机IP地址和端口信息的sockaddr_inl构cd的指针,其成员描qC本地端口号和本地L地址Q经qbind()服务器q程在网l上标识出来。需要注意的是由?024以内的埠号都是保留的埠号因此如无特别需要一般不能将sockin.sin_port的埠可|ؓ1024以内的倹{然后调用listen()函数开始侦听,再通过accept()调用{待接收q接以完成连接的建立Q?br />
//q接h队列长度?Q即只允许有一个请?若有多个h,
//则出现错误,l出错误代码WSAECONNREFUSED?br />listen(sock,1);
//开启线E避免主E序的阻?br />AfxBeginThread(Server,NULL);
…?br />UINT Server(LPVOID lpVoid)
{
…?br />int nLen=sizeof(SOCKADDR);
pView->newskt=accept(pView->sock,(LPSOCKADDR)& pView->sockin,(LPINT)& nLen);
…?
WSAAsyncSelect(pView->newskt,pView->m_hWnd,WM_SOCKET_MSG,FD_READ|FD_CLOSE);
return 1;
}

  q里之所以把accept()攑ֈ一个线E中L因ؓ在执行到该函数时如没有客戯接服务器的请求到来,服务器就会停在accept语句上等待连接请求的到来Q这势必会引L序的dQ虽然也可以通过讄套接字ؓ非阻塞方式在没有客L待时可以使acceptQ)函数调用立即q回Q但q种轮询套接字的方式会CPU处于忙等待方式,从而降低程序的q行效率大大费pȝ资源。考虑到这U情况,套接字讄为阻塞工作方式,qؓ其单独开辟一个子U程Q将光塞控制在子线E范围内而不会造成整个应用E序的阻塞。对于网l事件的响应昄要采取异步选择机制Q只有采取这U方式才可以在由|络Ҏ所引v的不可预知的|络事g发生时能马上在进E中做出及时的响应处理,而在没有|络事g到达时则可以处理其他事gQ这U效率是很高的,而且完全W合Windows所标榜的消息触发原则。前面那D代码中的WSAAsyncSelect()函数便是实现|络事g异步选择的核心函数?br />
  通过W四个参数注册应用程序感兴取的网l事Ӟ在这里通过FD_READ|FD_CLOSE指定了网l读和网l断开两种事gQ当q种事g发生时变会发出由W三个参数指定的自定义消息WM_SOCKET_MSGQ接收该消息的窗口通过W二个参数指定其句柄。在消息处理函数中可以通过Ҏ息参C字节q行判断而区别出发生的是何种|络事gQ?br />
void CNetServerView::OnSocket(WPARAM wParam,LPARAM lParam)
{
int iReadLen=0;
int message=lParam & 0x0000FFFF;
switch(message)
{
case FD_READ://M件发生。此时有字符到达Q需要进行接收处?br />char cDataBuffer[MTU*10];
//通过套接字接收信?br />iReadLen = recv(newskt,cDataBuffer,MTU*10,0);
//信息保存到文g
if(!file.Open("ServerFile.txt",CFile::modeReadWrite))
file.Open("E:ServerFile.txt",CFile::modeCreate|CFile::modeReadWrite);
file.SeekToEnd();
file.Write(cDataBuffer,iReadLen);
file.Close();
break;
case FD_CLOSE://|络断开事g发生。此时客h关闭或退出?br />…?/q行相应的处?br />break;
default:
break;
}
}

  在这里需要实现对自定义消息WM_SOCKET_MSG的响应,需要在头文件和实现文g中分别添加其消息映射关系Q?br />
  头文Ӟ

//{{AFX_MSG(CNetServerView)
//}}AFX_MSG
void OnSocket(WPARAM wParam,LPARAM lParam);
DECLARE_MESSAGE_MAP()

  实现文gQ?br />
BEGIN_MESSAGE_MAP(CNetServerView, CView)
//{{AFX_MSG_MAP(CNetServerView)
//}}AFX_MSG_MAP
ON_MESSAGE(WM_SOCKET_MSG,OnSocket)
END_MESSAGE_MAP()

  在进行异步选择使用WSAAsyncSelect()函数Ӟ有以下几炚w要引L别的注意Q?br />
  1Q?q箋使用两次WSAAsyncSelect()函数Ӟ只有W二ơ设|的事g有效Q如Q?br />
WSAAsyncSelect(s,hwnd,wMsg1,FD_READ);
WSAAsyncSelect(s,hwnd,wMsg2,FD_CLOSE);

  q样只有当FD_CLOSE事g发生时才会发送wMsg2消息?br />
  2Q可以在讄q异步选择后通过再次调用WSAAsyncSelect(s,hwnd,0,0);的Ş式取消在套接字上所讄的异步事件?br />
  3QWindows Sockets DLL在一个网l事件发生后Q通常只会l相应的应用E序发送一个消息,而不能发送多个消息。但通过使用一些函数隐式地允许重发此事件的消息Q这样就可能再次接收到相应的消息?br />
  4Q在调用qclosesocket()函数关闭套接字之后不会再发生FD_CLOSE事g?br />
  以上基本完成了服务器方的E序设计Q下面对于客L的实现则要简单多了,在用socket()创徏完套接字之后只需通过调用connect()完成同服务器的连接即可,剩下的工作同服务器完全一P用send()/recv()发?接收收据Q用closesocket()关闭套接字:

sockin.sin_family=AF_INET; //地址?br />sockin.sin_addr.S_un.S_addr=IPaddr; //指定服务器的IP地址
sockin.sin_port=m_Port; //指定q接的端口号
int nConnect=connect(sock,(LPSOCKADDR)&sockin,sizeof(sockin));

  本文采取的是可靠的面向连接的式套接字。在数据发送上有write()、writev()和send(){三个函数可供选择Q其中前两种分别用于~冲发送和集中发送,而send()则ؓ可控~冲发送,q且q可以指定传输控制标志ؓMSG_OOBq行带外数据的发送或是ؓMSG_DONTROUTEd控制选项。在信宿地址的网l号部分指定数据发送需要经q的|络接口Q其可以不l过本地d机制直接发送出厅R这也是其同write()函数的真正区别所在。由于接收数据系l调用和发送数据系l调用是一一对应的,因此对于数据的接Ӟ在此不再赘述Q相应的三个接收函数分别为:read()、readv()和recv()。由于后者功能上的全面,本文在实C选择了send()-recv()函数对,在具体编E中应当视具体情늚不同灉|选择适当的发?接收函数寏V?br />
  结Q?/b>TCP/IP协议是目前各|络操作pȝ主要的通讯协议Q也?Internet的通讯协议Q本文通过Windows Sockets API实现了对ZTCP/IP协议的面向连接的式套接字网l通讯E序的设计,q过异步通讯和多U程{手D|高了E序的运行效率,避免了阻塞的发生?br />


? 2007-01-31 11:32 发表评论
]]>
TCP/IP Winsock~程要点http://www.shnenglu.com/eday/archive/2007/01/31/18203.html??Wed, 31 Jan 2007 02:53:00 GMThttp://www.shnenglu.com/eday/archive/2007/01/31/18203.html
  1、快速通信

  Winsock的Nagle法降低小数据报的发送速度Q而系l默认是使用Nagle法,使用

int setsockopt(

SOCKET s,

int level,

int optname,

const char FAR *optval,

int optlen

);函数关闭?

  例子Q?

SOCKET sConnect;

sConnect=::socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

int bNodelay = 1;

int err;

err = setsockopt(

sConnect,

IPPROTO_TCP,

TCP_NODELAY,

(char *)&bNodelay,

sizoeof(bNodelay));//不采用g时算?

if (err != NO_ERROR)

TRACE ("setsockopt failed for some reason\n");;

  2、SOCKET的SegMentSize和收发缓?

  TCPSegMentSize是发送接受时单个数据报的最大长度,pȝ默认?460Q收发缓冲大ؓ8192?

  在SOCK_STREAM方式下,如果单次发送数据超q?460Q系l将分成多个数据报传送,在对Ҏ受到的将是一个数据流Q应用程序需要增加断帧的判断。当然可以采用修Ҏ册表的方式改?460的大,但MicrcoSoft认ؓ1460是最x率的参数Q不修改?

  在工控系l中Q徏议关闭Nagle法Q每ơ发送数据小?460个字节(推荐1400Q,q样每次发送的是一个完整的数据报,减少ҎҎ据流的断帧处理?

  3、同步方式中减少断网时connect函数的阻塞时?

  同步方式中的断网时connect的阻塞时间ؓ20U左叻I可采用gethostbyaddr事先判断到服务主机的路径是否是通的Q或者先ping一下对方主机的IP地址?

  A、采用gethostbyaddrd旉不管成功与否?U左叟?

  例子Q?

LONG lPort=3024;

struct sockaddr_in ServerHostAddr;//服务L地址

ServerHostAddr.sin_family=AF_INET;

ServerHostAddr.sin_port=::htons(u_short(lPort));

ServerHostAddr.sin_addr.s_addr=::inet_addr("192.168.1.3");

HOSTENT* pResult=gethostbyaddr((const char *) &

(ServerHostAddr.sin_addr.s_addr),4,AF_INET);

if(NULL==pResult)

{

int nErrorCode=WSAGetLastError();

TRACE("gethostbyaddr errorcode=%d",nErrorCode);

}

else

{

TRACE("gethostbyaddr %s\n",pResult->h_name);;

}

  B、采用PING方式旉U?U左?

  暂略

4、同步方式中解决recvQsendd问题

  采用select函数解决Q在收发前先查读写可用状态?

  A、读

  例子Q?

TIMEVAL tv01 = {0, 1};//1ms钟gq?实际?-10毫秒

int nSelectRet;

int nErrorCode;

FD_SET fdr = {1, sConnect};

nSelectRet=::select(0, &fdr, NULL, NULL, &tv01);//查可ȝ?

if(SOCKET_ERROR==nSelectRet)

{

nErrorCode=WSAGetLastError();

TRACE("select read status errorcode=%d",nErrorCode);

::closesocket(sConnect);

goto 重新q接Q客hQ,或服务线E退出(服务方);

}

if(nSelectRet==0)//时发生Q无可读数据

{

l箋查读状态或向对方主动发?

}

else

{

L?

}

  B、写

TIMEVAL tv01 = {0, 1};//1ms钟gq?实际?-10毫秒

int nSelectRet;

int nErrorCode;

FD_SET fdw = {1, sConnect};

nSelectRet=::select(0, NULL, NULL,&fdw, &tv01);//查可写状?

if(SOCKET_ERROR==nSelectRet)

{

nErrorCode=WSAGetLastError();

TRACE("select write status errorcode=%d",nErrorCode);

::closesocket(sConnect);

//goto 重新q接Q客hQ,或服务线E退出(服务方);

}

if(nSelectRet==0)//时发生Q缓冲满或网l忙

{

//l箋查写状态或查读状?

}

else

{

//发?

}

  5、改变TCP收发~冲区大?

  pȝ默认?192Q利用如下方式可改变?

SOCKET sConnect;

sConnect=::socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

int nrcvbuf=1024*20;

int err=setsockopt(

sConnect,

SOL_SOCKET,

SO_SNDBUF,//写缓Ԍȝ冲ؓSO_RCVBUF

(char *)&nrcvbuf,

sizeof(nrcvbuf));

if (err != NO_ERROR)

{

TRACE("setsockopt Error!\n");

}

在设|缓冲时Q检查是否真正设|成功用

int getsockopt(

SOCKET s,

int level,

int optname,

char FAR *optval,

int FAR *optlen

);

  6、服务方同一端口多IP地址的bind和listen

  在可靠性要求高的应用中Q要求用双|和多网l通道Q再服务方很Ҏ实现Q用如下方式可徏立客户对本机所有IP地址在端?024下的h服务?

SOCKET hServerSocket_DS=INVALID_SOCKET;

struct sockaddr_in HostAddr_DS;//服务器主机地址

LONG lPort=3024;

HostAddr_DS.sin_family=AF_INET;

HostAddr_DS.sin_port=::htons(u_short(lPort));

HostAddr_DS.sin_addr.s_addr=htonl(INADDR_ANY);

hServerSocket_DS=::socket( AF_INET, SOCK_STREAM,IPPROTO_TCP);

if(hServerSocket_DS==INVALID_SOCKET)

{

AfxMessageBox("建立数据服务器SOCKET p|!");

return FALSE;

}

if(SOCKET_ERROR==::bind(hServerSocket_DS,(struct

sockaddr *)(&(HostAddr_DS)),sizeof(SOCKADDR)))

{

int nErrorCode=WSAGetLastError ();

TRACE("bind error=%d\n",nErrorCode);

AfxMessageBox("Socket Bind 错误!");

return FALSE;

}

if(SOCKET_ERROR==::listen(hServerSocket_DS,10))//10个客?

{

AfxMessageBox("Socket listen 错误!");

return FALSE;

}

AfxBeginThread(ServerThreadProc,NULL,THREAD_PRIORITY_NORMAL);

  在客h要复杂一些,q接断后Q重联不成功则应换下一个IP地址q接。也可采用同时连接好后备用的方式?

  7、用TCP/IP Winsock实现变种Client/Server

  传统的Client/Server为客户问、服务答Q收发是成对出现的。而变U的Client/Server是指在连接时有客户和服务之分Q徏立好通信q接后,不再有严格的客户和服务之分,M斚w可主动发送,需要或不需要回{看应用而言Q这U方式在工控行业很有用,比如RTDB作ؓI/O Server的客P但I/O Server也可d向RTDB发送开关状态变位、随即事件等信息。在很大E度上减了|络通信负荷、提高了效率?

  采用1-6的TCP/IP~程要点Q在Client和Server方均已接收优先,适当控制时序p实现?br />

? 2007-01-31 10:53 发表评论
]]>
ZVisual C++的Winsock API研究http://www.shnenglu.com/eday/archive/2007/01/31/18202.html??Wed, 31 Jan 2007 02:51:00 GMThttp://www.shnenglu.com/eday/archive/2007/01/31/18202.html
  微Y为VC定义了WinsockcdCAsyncSocketcdz于CAsyncSocket 的CSocketc,它们单易用,读者朋友当然可以用这些类来实现自q|络E序Q但是ؓ了更好的了解Winsock API~程技术,我们q里探讨怎样使用底层的API函数实现单的 Winsock |络应用E式设计Q分别说明如何在Server端和Client端操作SocketQ实现基于TCP/IP的数据传送,最后给出相关的源代码?br />
  在VC中进行WINSOCK的API~程开发的时候,需要在目中用下面三个文Ӟ否则会出现编译错误?br />
  1QWINSOCK.H: q是WINSOCK API的头文gQ需要包含在目中?br />
  2QWSOCK32.LIB: WINSOCK APIq接库文件。在使用中,一定要把它作ؓ目的非~省的连接库包含到项目文件中厅R?

  3QWINSOCK.DLL: WINSOCK的动态连接库Q位于WINDOWS的安装目录下?br />
  一、服务器端操?socketQ套接字Q?/b>

  1)在初始化阶段调用WSAStartup()

  此函数在应用E序中初始化Windows Sockets DLL Q只有此函数调用成功后,应用E序才可以再调用其他Windows Sockets DLL中的API函数。在E式中调用该函数的Ş式如下:WSAStartup((WORD)((1<<8|1)Q(LPWSADATAQ?amp;WSAData)Q其?1<<8|1)表示我们用的是WinSocket1.1版本QWSAata用来存储pȝ传回的关于WinSocket的资料?br />
  2)建立Socket

  初始化WinSock的动态连接库后,需要在服务器端建立一个监听的SocketQؓ此可以调用Socket()函数用来建立q个监听的SocketQƈ定义此Socket所使用的通信协议。此函数调用成功q回Socket对象Q失败则q回INVALID_SOCKET(调用WSAGetLastError()可得知原因,所有WinSocket 的函数都可以使用q个函数来获取失败的原因)?br />
SOCKET PASCAL FAR socket( int af, int type, int protocol )
参数: af:目前只提?PF_INET(AF_INET)Q?br />typeQSocket 的类?(SOCK_STREAM、SOCK_DGRAM)Q?br />protocolQ通讯协定(如果使用者不指定则设?)Q?br />
如果要徏立的是遵从TCP/IP协议的socketQ第二个参数type应ؓSOCK_STREAMQ如为UDPQ数据报Q的socketQ应为SOCK_DGRAM?br />
  3)l定端口

  接下来要为服务器端定义的q个监听的Socket指定一个地址及端口(PortQ,q样客户端才知道待会要连接哪一个地址的哪个端口,为此我们要调用bind()函数Q该函数调用成功q回0Q否则返回SOCKET_ERROR?br />int PASCAL FAR bind( SOCKET s, const struct sockaddr FAR *name,int namelen );

?敎ͼ sQSocket对象名;
nameQSocket的地址|q个地址必须是执行这个程式所在机器的IP地址Q?br />namelenQname的长度;

  如果使用者不在意地址或端口的|那么可以讑֮地址为INADDR_ANYQ及Port?QWindows Sockets 会自动将其设定适当之地址及Port (1024 ?5000之间的?。此后可以调用getsockname()函数来获知其被设定的倹{?br />
  4Q监?br />
  当服务器端的Socket对象l定完成之后,服务器端必须建立一个监听的队列来接收客L的连接请求。listen()函数使服务器端的Socket q入监听状态,q设定可以徏立的最大连接数(目前最大值限制ؓ 5, 最gؓ1)。该函数调用成功q回0Q否则返回SOCKET_ERROR?br />
int PASCAL FAR listen( SOCKET s, int backlog );
?敎ͼ sQ需要徏立监听的SocketQ?br />backlogQ最大连接个敎ͼ

  服务器端的Socket调用完listenQ)后,如果此时客户端调用connectQ)函数提出q接甌的话QServer 端必d调用accept() 函数Q这h务器端和客户端才正式完成通信E序的连接动作。ؓ了知道什么时候客L提出q接要求Q从而服务器端的Socket在恰当的时候调用accept()函数完成q接的徏立,我们p使用WSAAsyncSelectQ)函数Q让pȝd来通知我们有客L提出q接h了。该函数调用成功q回0Q否则返回SOCKET_ERROR?br />
int PASCAL FAR WSAAsyncSelect( SOCKET s, HWND hWnd,unsigned int wMsg, long lEvent );
参数Q?sQSocket 对象Q?br />hWnd Q接收消息的H口句柄Q?br />wMsgQ传l窗口的消息Q?br />lEventQ被注册的网l事Ӟ也即是应用程序向H口发送消息的|\事gQ该gؓ下列值FD_READ、FD_WRITE、FD_OOB、FD_ACCEPT、FD_CONNECT、FD_CLOSE的组合,各个值的具体含意为FD_READQ希望在套接字S收到数据时收到消息;FD_WRITEQ希望在套接字S上可以发送数据时收到消息QFD_ACCEPTQ希望在套接字S上收到连接请求时收到消息QFD_CONNECTQ希望在套接字S上连接成功时收到消息QFD_CLOSEQ希望在套接字S上连接关闭时收到消息QFD_OOBQ希望在套接字S上收到带外数据时收到消息?

  具体应用ӞwMsg应是在应用程序中定义的消息名Uͼ而消息结构中的lParam则ؓ以上各种|络事g名称。所以,可以在窗口处理自定义消息函数中用以下结构来响应Socket的不同事Ӟ  

switch(lParam) 
  {case FD_READ:
    …  
  break;
case FD_WRITE?br />    ?br />  break;
    ?br />}

  5Q服务器端接受客L的连接请?br />
  当Client提出q接hӞServer 端hwnd视窗会收到Winsock Stack送来我们自定义的一个消息,q时Q我们可以分析lParamQ然后调用相关的函数来处理此事g。ؓ了服务器端接受客户端的q接hQ就要用accept() 函数Q该函数新徏一Socket与客L的Socket盔R,原先监听之Socketl箋q入监听状态,{待他h的连接要求。该函数调用成功q回一个新产生的Socket对象Q否则返回INVALID_SOCKET?br />
SOCKET PASCAL FAR accept( SCOKET s, struct sockaddr FAR *addr,int FAR *addrlen );
参数QsQSocket的识别码Q?br />addrQ存放来q接的客L的地址Q?br />addrlenQaddr的长?/td>

  6Q结?socket q接

  l束服务器和客户端的通信q接是很单的Q这一q程可以由服务器或客h的Q一端启动,只要调用closesocket()可以了Q而要关闭Server端监听状态的socketQ同样也是利用此函数。另外,与程序启动时调用WSAStartup()憨数相对应,E式l束前,需要调?WSACleanup() 来通知Winsock Stack释放Socket所占用的资源。这两个函数都是调用成功q回0Q否则返回SOCKET_ERROR?br />
int PASCAL FAR closesocket( SOCKET s );
?敎ͼsQSocket 的识别码Q?br />int PASCAL FAR WSACleanup( void );
?敎ͼ ?/td>

二、客LSocket的操?br />
  1Q徏立客L的Socket

  客户端应用程序首先也是调用WSAStartup() 函数来与Winsock的动态连接库建立关系Q然后同栯用socket() 来徏立一个TCP或UDP socketQ相同协定的 sockets 才能盔R,TCP ?TCPQUDP ?UDPQ。与服务器端的socket 不同的是Q客L的socket 可以调用 bind() 函数Q由自己来指定IP地址及portLQ但是也可以不调?bind()Q而由 Winsock来自动设定IP地址及portL?br />
  2Q提接申?br />
  客户端的Socket使用connect()函数来提Z服务器端的Socket建立q接的申P函数调用成功q回0Q否则返回SOCKET_ERROR?br />
int PASCAL FAR connect( SOCKET s, const struct sockaddr FAR *name, int namelen );
?敎ͼsQSocket 的识别码Q?br />nameQSocket惌q接的对方地址Q?br />namelenQname的长?/td>

  三、数据的传?/b>

  虽然ZTCP/IPq接协议Q流套接字)的服务是设计客户?服务器应用程序时的主标准,但有些服务也是可以通过无连接协议(数据报套接字Q提供的。先介绍一下TCP socket 与UDP socket 在传送数据时的特性:Stream (TCP) Socket 提供双向、可靠、有ơ序、不重复的资料传送。Datagram (UDP) Socket 虽然提供双向的通信Q但没有可靠、有ơ序、不重复的保证,所以UDP传送数据可能会收到无次序、重复的资料Q甚臌料在传输q程中出现遗漏。由于UDP Socket 在传送资料时Qƈ不保证资料能完整地送达ҎQ所以绝大多数应用程序都是采用TCP处理SocketQ以保证资料的正性。一般情况下TCP Socket 的数据发送和接收是调用send() 及recv() q两个函数来达成Q?UDP Socket则是用sendto() 及recvfrom() q两个函敎ͼq两个函数调用成功发挥发送或接收的资料的长度Q否则返回SOCKET_ERROR?br />
int PASCAL FAR send( SOCKET s, const char FAR *buf,int len, int flags );
参数QsQSocket 的识别码
bufQ存放要传送的资料的暂存区
len bufQ的长度
flagsQ此函数被调用的方式

  对于Datagram Socket而言Q若?datagram 的大超q限Ӟ则将不会送出M资料Qƈ会传回错误倹{对Stream Socket aQBlocking 模式下,若是传送系l内的储存空间不够存放这些要传送的资料Qsend()会被block住,直到资料送完为止Q如果该Socket被设定ؓ Non-Blocking 模式Q那么将视目前的output bufferI间有多,送出多少资料Qƈ不会?block 住。flags 的值可设ؓ 0 ?MSG_DONTROUTE?MSG_OOB 的组合?br />
int PASCAL FAR recv( SOCKET s, char FAR *buf, int len, int flags );
参数QsQSocket 的识别码
bufQ存放接收到的资料的暂存?br />len bufQ的长度
flagsQ此函数被调用的方式

  对Stream Socket aQ我们可以接收到目前input buffer内有效的资料Q但其数量不过len的大?br />
  四、自定义的CMySocketcȝ实现代码Q?/font>

  Ҏ上面的知识,我自定义了一个简单的CMySocketc,下面是我定义的该cȝ部分实现代码Q?br />
//////////////////////////////////////
CMySocket::CMySocket() : file://cȝ构造函?br />{
 WSADATA wsaD;
 memset( m_LastError, 0, ERR_MAXLENGTH );
 // m_LastError是类内字W串变量,初始化用来存放最后错误说明的字符Ԍ
 // 初始化类内sockaddr_inl构变量Q前者存攑֮L地址Q后者对应于服务器端地址;
 memset( &m_sockaddr, 0, sizeof( m_sockaddr ) );
 memset( &m_rsockaddr, 0, sizeof( m_rsockaddr ) );
 int result = WSAStartup((WORD)((1<<8|1)Q?&wsaD);//初始化WinSocket动态连接库;
 if( result != 0 ) // 初始化失败;
 { set_LastError( "WSAStartup failed!", WSAGetLastError() );
  return;
 }
}

//////////////////////////////
CMySocket::~CMySocket() { WSACleanup(); }//cȝ析构函数Q?br />////////////////////////////////////////////////////
int CMySocket::Create( void )
 {// m_hSocket是类内Socket对象Q创Z个基于TCP/IP的Socket变量QƈDl该变量Q?br />  if ( (m_hSocket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP )) == INVALID_SOCKET )
  {
   set_LastError( "socket() failed", WSAGetLastError() );
   return ERR_WSAERROR;
  }
  return ERR_SUCCESS;
 }
///////////////////////////////////////////////
int CMySocket::Close( void )//关闭Socket对象Q?br />{
 if ( closesocket( m_hSocket ) == SOCKET_ERROR )
 {
  set_LastError( "closesocket() failed", WSAGetLastError() );
  return ERR_WSAERROR;
 }
 file://重置sockaddr_in l构变量Q?br /> memset( &m_sockaddr, 0, sizeof( sockaddr_in ) );
 memset( &m_rsockaddr, 0, sizeof( sockaddr_in ) );
 return ERR_SUCCESS;
}
/////////////////////////////////////////
int CMySocket::Connect( char* strRemote, unsigned int iPort )//定义q接函数Q?br />{
 if( strlen( strRemote ) == 0 || iPort == 0 )
  return ERR_BADPARAM;
 hostent *hostEnt = NULL;
 long lIPAddress = 0;
 hostEnt = gethostbyname( strRemote );//Ҏ计算机名得到该计机的相兛_容;
 if( hostEnt != NULL )
 {
  lIPAddress = ((in_addr*)hostEnt->h_addr)->s_addr;
  m_sockaddr.sin_addr.s_addr = lIPAddress;
 }
 else
 {
  m_sockaddr.sin_addr.s_addr = inet_addr( strRemote );
 }
 m_sockaddr.sin_family = AF_INET;
 m_sockaddr.sin_port = htons( iPort );
 if( connect( m_hSocket, (SOCKADDR*)&m_sockaddr, sizeof( m_sockaddr ) ) == SOCKET_ERROR )
 {
  set_LastError( "connect() failed", WSAGetLastError() );
  return ERR_WSAERROR;
 }
 return ERR_SUCCESS;
}
///////////////////////////////////////////////////////
int CMySocket::Bind( char* strIP, unsigned int iPort )//l定函数Q?br />{
 if( strlen( strIP ) == 0 || iPort == 0 )
  return ERR_BADPARAM;
 memset( &m_sockaddr,0, sizeof( m_sockaddr ) );
 m_sockaddr.sin_family = AF_INET;
 m_sockaddr.sin_addr.s_addr = inet_addr( strIP );
 m_sockaddr.sin_port = htons( iPort );
 if ( bind( m_hSocket, (SOCKADDR*)&m_sockaddr, sizeof( m_sockaddr ) ) == SOCKET_ERROR )
 {
  set_LastError( "bind() failed", WSAGetLastError() );
  return ERR_WSAERROR;
 }
 return ERR_SUCCESS;
}
//////////////////////////////////////////
int CMySocket::Accept( SOCKET s )//建立q接函数QS为监听Socket对象名;
{
 int Len = sizeof( m_rsockaddr );
 memset( &m_rsockaddr, 0, sizeof( m_rsockaddr ) );
 if( ( m_hSocket = accept( s, (SOCKADDR*)&m_rsockaddr, &Len ) ) == INVALID_SOCKET )
 {
  set_LastError( "accept() failed", WSAGetLastError() );
  return ERR_WSAERROR;
 }
 return ERR_SUCCESS;
}
/////////////////////////////////////////////////////
int CMySocket::asyncSelect( HWND hWnd, unsigned int wMsg, long lEvent )
file://事g选择函数Q?br />{
 if( !IsWindow( hWnd ) || wMsg == 0 || lEvent == 0 )
  return ERR_BADPARAM;
 if( WSAAsyncSelect( m_hSocket, hWnd, wMsg, lEvent ) == SOCKET_ERROR )
 {
  set_LastError( "WSAAsyncSelect() failed", WSAGetLastError() );
  return ERR_WSAERROR;
 }
 return ERR_SUCCESS;
}
////////////////////////////////////////////////////
int CMySocket::Listen( int iQueuedConnections )//监听函数Q?br />{
 if( iQueuedConnections == 0 )
  return ERR_BADPARAM;
 if( listen( m_hSocket, iQueuedConnections ) == SOCKET_ERROR )
 {
  set_LastError( "listen() failed", WSAGetLastError() );
  return ERR_WSAERROR;
 }
 return ERR_SUCCESS;
}
////////////////////////////////////////////////////
int CMySocket::Send( char* strData, int iLen )//数据发送函敎ͼ
{
 if( strData == NULL || iLen == 0 )
  return ERR_BADPARAM;
 if( send( m_hSocket, strData, iLen, 0 ) == SOCKET_ERROR )
 {
  set_LastError( "send() failed", WSAGetLastError() );
  return ERR_WSAERROR;
 }
 return ERR_SUCCESS;
}
/////////////////////////////////////////////////////
int CMySocket::Receive( char* strData, int iLen )//数据接收函数Q?br />{
 if( strData == NULL )
  return ERR_BADPARAM;
 int len = 0;
 int ret = 0;
 ret = recv( m_hSocket, strData, iLen, 0 );
 if ( ret == SOCKET_ERROR )
 {
  set_LastError( "recv() failed", WSAGetLastError() );
  return ERR_WSAERROR;
 }
 return ret;
}
void CMySocket::set_LastError( char* newError, int errNum )
file://WinSock API操作错误字符串设|函敎ͼ
{
 memset( m_LastError, 0, ERR_MAXLENGTH );
 memcpy( m_LastError, newError, strlen( newError ) );
 m_LastError[strlen(newError)+1] = '\0';
}

  有了上述cȝ定义Q就可以在网l程序的服务器和客户端分别定义CMySocket对象Q徏立连接,传送数据了。例如,Z在服务器和客L发送数据,需要在服务器端定义两个CMySocket对象ServerSocket1和ServerSocket2Q分别用于监听和q接Q客L定义一个CMySocket对象ClientSocketQ用于发送或接收数据Q如果徏立的q接数大于一Q可以在服务器端再定义CMySocket对象Q但要注意连接数不要大于五?br />
  ׃Socket API函数q有许多Q如获取q端服务器、本地客h的IP地址、主机名{等Q读者可以再此基上对CMySocket补充完善Q实现更多的功能?br />

? 2007-01-31 10:51 发表评论
]]>
Windows Socket1.1 E序设计http://www.shnenglu.com/eday/archive/2007/01/30/18190.html??Tue, 30 Jan 2007 14:07:00 GMThttp://www.shnenglu.com/eday/archive/2007/01/30/18190.html一、简?br />
  Windows Sockets 是从 Berkeley Sockets 扩展而来的,其在l承 Berkeley Sockets 的基上,又进行了新的扩充。这些扩充主要是提供了一些异步函敎ͼq增加了W合WINDOWS消息驱动Ҏ的|络事g异步选择机制?br />
  Windows Sockets׃部分l成Q开发组件和q行lg?br />
  开发组ӞWindows Sockets 实现文档、应用程序接?API)引入库和一些头文g?br />
  q行lgQWindows Sockets 应用E序接口的动态链接库(WINSOCK.DLL)?

  二、主要扩充说?/b>

  1、异步选择机制Q?br />
  Windows Sockets 的异步选择函数提供了消息机制的|络事g选择Q当使用它登记网l事件发生时Q应用程序相应窗口函数将收到一个消息,消息中指CZ发生的网l事Ӟ以及与事件相关的一些信息?br />
  Windows Sockets 提供了一个异步选择函数 WSAAsyncSelect()Q用它来注册应用E序感兴的|络事gQ当q些事g发生Ӟ应用E序相应的窗口函数将收到一个消息?br />
  函数l构如下Q?/p>
int PASCAL FAR WSAAsyncSelect(SOCKET s,HWND hWnd,unsigned int wMsg,long lEvent);

  参数说明Q?br />
   hWndQ窗口句?br />
   wMsgQ需要发送的消息

   lEventQ事Ӟ以下Z件的内容Q?br />

|含义Q?/td>
FD_READ期望在套接字上收到数据(卌准备好)时接到通知
FD_WRITE期望在套接字上可发送数据(卛_准备好)时接到通知
FD_OOB期望在套接字上有带外数据到达时接到通知
FD_ACCEPT期望在套接字上有外来q接时接到通知
FD_CONNECT期望在套接字q接建立完成时接到通知
FD_CLOSE期望在套接字关闭时接到通知

  例如Q我们要在套接字d备好或写准备好时接到通知Q语句如下:

rc=WSAAsyncSelect(s,hWnd,wMsg,FD_READ|FD_WRITE);

  如果我们需要注销对套接字|络事g的消息发送,只要?lEvent 讄?

  2、异步请求函?br />
  ?Berkeley Sockets 中请求服务是d的,WINDOWS SICKETS 除了支持q一cd数外Q还增加了相应的异步h函数(WSAAsyncGetXByY();)?

  3、阻塞处理方?br />
  Windows Sockets Z实现当一个应用程序的套接字调用处于阻塞时Q能够放弃CPU让其它应用程序运行,它在调用处于d时便q入一个叫“HOOK”的例程Q此例程负责接收和分配WINDOWS消息Q得其它应用程序仍然能够接收到自己的消息ƈ取得控制权?br />
  WINDOWS 是非抢先的多d环境Q即若一个程序不d攑ּ其控制权Q别的程序就不能执行。因此在设计Windows Sockets E序Ӟ管pȝ支持d操作Q但q是反对E序员用该操作。但׃ SUN 公司下的 Berkeley Sockets 的套接字默认操作是阻塞的QWINDOWS 作ؓUL?SOCKETS 也不可避免对q个操作支持?br />
  在Windows Sockets 实现中,对于不能立即完成的阻塞操作做如下处理QDLL初始化→循环操作。在循环中,它发送Q?WINDOWS 消息Qƈ查这?Windows Sockets 调用是否完成Q在必要Ӟ它可以放弃CPU让其它应用程序执行(当然使用线E的CPU׃会有q个ȝ了^_^Q。我们可以调?WSACancelBlockingCall() 函数取消此阻塞操作?br />
  ?Windows Sockets 中,有一个默认的d处理例程 BlockingHook() 单地获取q发?WINDOWS 消息。如果要对复杂程序进行处理,Windows Sockets 中还?WSASetBlockingHook() 提供用户安装自己的阻塞处理例E能力;与该函数相对应的则是 SWAUnhookBlockingHook()Q它用于删除先前安装的Q何阻塞处理例E,q新安装默认的处理例程。请注意Q设计自qd处理例程Ӟ除了函数 WSACancelBlockingHook() 之外Q它不能使用其它?Windows Sockets API 函数。在处理例程中调?WSACancelBlockingHook()函数取消处于阻塞的操作Q它结束阻塞@环?/p>

  4、出错处?br />
  Windows Sockets Z和以后多U程环境QWINDOWS/UNIXQ兼容,它提供了两个出错处理函数来获取和讄当前U程的最q错误号。(WSAGetLastEror()和WSASetLastError()Q?/p>

  5、启动与l止

  使用函数 WSAStartup() ?WSACleanup() 启动和终止套接字?br />
三、Windows Sockets|络E序设计核心

  我们l于可以开始真正的 Windows Sockets |络E序设计了。不q我们还是先看一看每?Windows Sockets |络E序都要涉及的内宏V让我们一步步慢慢走?/p>

  1、启动与l止

  在所?Windows Sockets 函数中,只有启动函数 WSAStartup() 和终止函?WSACleanup() 是必M用的?br />
  启动函数必须是第一个用的函数Q而且它允许指?Windows Sockets API 的版本,q获?SOCKETS的特定的一些技术细节。本l构如下Q?br />

int PASCAL FAR WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);

  其中 wVersionRequested 保证 SOCKETS 可正常运行的 DLL 版本Q如果不支持Q则q回错误信息?br />我们看一下下面这D代码,看一下如何进?WSAStartup() 的调?br />

WORD wVersionRequested;// 定义版本信息变量
WSADATA wsaData;//定义数据信息变量
int err;//定义错误号变?br />wVersionRequested = MAKEWORD(1,1);//l版本信息赋?br />err = WSAStartup(wVersionRequested, &wsaData);//l错误信息赋?br />if(err!=0)
{
return;//告诉用户找不到合适的版本
}
//认 Windows Sockets DLL 支持 1.1 版本
//DLL 版本可以高于 1.1
//pȝq回的版本号始终是最低要求的 1.1Q即应用E序与DLL 中可支持的最低版本号
if(LOBYTE(wsaData.wVersion)!= 1|| HIBYTE(wsaData.wVersion)!=1)
{
WSACleanup();//告诉用户找不到合适的版本
return;
}
//Windows Sockets DLL 被进E接受,可以q入下一步操?/td>

  关闭函数使用ӞM打开q已q接?SOCK_STREAM 套接字被复位Q但那些已由 closesocket() 函数关闭的但仍有未发送数据的套接字不受媄响,未发送的数据仍将被发送。程序运行时可能会多ơ调?WSAStartuo() 函数Q但必须保证每次调用时的 wVersionRequested 的值是相同的?/p>

  2、异步请求服?br />
  Windows Sockets 除支?Berkeley Sockets 中同步请求,q增加了了一cd步请求服务函?WSAAsyncGerXByY()。该函数是阻塞请求函数的异步版本。应用程序调用它Ӟ?Windows Sockets DLL 初始化这一操作q返回调用者,此函数返回一个异步句柄,用来标识q个操作。当l果存储在调用者提供的~冲区,q且发送一个消息到应用E序相应H口。常用结构如下:

HANDLE taskHnd;
char hostname="rs6000";
taskHnd = WSAAsyncBetHostByName(hWnd,wMsg,hostname,buf,buflen);

  需要注意的是,׃ Windows 的内存对像可以设|ؓ可移动和可丢弃,因此在操作内存对象是Q必M?WIindows Sockets DLL 对象是可用的?

  3、异步数据传?br />
  使用 send() ?sendto() 函数来发送数据,使用 recv() 或recvfrom() 来接收数据。Windows Sockets 不鼓q户用阻塞方式传输数据,因ؓ那样可能会阻塞整?Windows 环境。下面我们看一个异步数据传输实例:

  假设套接?s 在连接徏立后Q已l用了函数 WSAAsyncSelect() 在其上注册了|络事g FD_READ ?FD_WRITEQƈ?wMsg gؓ UM_SOCKQ那么我们可以在 Windows 消息循环中增加如下的分支语句Q?/p>

case UM_SOCK:
switch(lParam)
{
case FD_READ:
len = recv(wParam,lpBuffer,length,0);
break;
case FD_WRITE:
while(send(wParam,lpBuffer,len,0)!=SOCKET_ERROR)
break;
}
break;

  4、出错处?br />
  Windows 提供了一个函数来获取最q的错误?WSAGetLastError()Q推荐的~写方式如下Q?

len = send (s,lpBuffer,len,0);
of((len==SOCKET_ERROR)&&(WSAGetLastError()==WSAWOULDBLOCK)){...}


? 2007-01-30 22:07 发表评论
]]>
Winsocket~程之套接字原理http://www.shnenglu.com/eday/archive/2007/01/30/18189.html??Tue, 30 Jan 2007 14:04:00 GMThttp://www.shnenglu.com/eday/archive/2007/01/30/18189.html一、客h/服务器模?br />
  在TCP/IP|络中两个进E间的相互作用的L模式是客h/服务器模?Client/Server model)。该模式的徏立基于以下两点:1、非对等作用Q?、通信完全是异步的。客h/服务器模式在操作q程中采取的是主动请C方式:

  首先服务器方要先启动QƈҎL提供相应服务Q(q程如下Q?br />
  1、打开一通信通道q告知本C机,它愿意在某一个公认地址上接收客戯求?br />
  2、等待客戯求到达该端口?br />
  3、接收到重复服务hQ处理该hq发送应{信受?br />
  4、返回第二步Q等待另一客户h

  5、关闭服务器?br />
  客户方:

  1、打开一通信通道Qƈq接到服务器所在主机的特定端口?br />
  2、向服务器发送服务请求报文,{待q接收应{;l箋提出h…?br />
  3、请求结束后关闭通信通道q终止?/p>

  二、基本套接字

  Z更好说明套接字编E原理,l出几个基本的套接字Q在以后的篇q中会给出更详细的用说明?br />
  1、创建套接字——socket()

  功能Q用前创徏一个新的套接字

  格式QSOCKET PASCAL FAR socket(int af,int type,int procotol);

  参数Qaf: 通信发生的区?br />
  type: 要徏立的套接字类?br />
  procotol: 使用的特定协?/p>

  2、指定本地地址——bind()

  功能Q将套接字地址与所创徏的套接字可pv来?br />
  格式Qint PASCAL FAR bind(SOCKET s,const struct sockaddr FAR * name,int namelen);

  参数Qs: 是由socket()调用q回的ƈ且未作连接的套接字描q符Q套接字P?br />
  其它Q没有错误,bind()q回0Q否则SOCKET_ERROR

  地址l构说明Q?br />
struct sockaddr_in
{
short sin_family;//AF_INET
u_short sin_port;//16位端口号Q网l字节顺?br />struct in_addr sin_addr;//32位IP地址Q网l字节顺?br />char sin_zero[8];//保留
}

  3、徏立套接字q接——connect()和accept()

  功能Q共同完成连接工?br />
  格式Qint PASCAL FAR connect(SOCKET s,const struct sockaddr FAR * name,int namelen);

  SOCKET PASCAL FAR accept(SOCKET s,struct sockaddr FAR * name,int FAR * addrlen);

  参数Q同?/p>

  4、监听连接——listen()

  功能Q用于面向连接服务器Q表明它愿意接收q接?br />
  格式Qint PASCAL FAR listen(SOCKET s, int backlog);

  5、数据传输——send()与recv()

  功能Q数据的发送与接收

  格式Qint PASCAL FAR send(SOCKET s,const char FAR * buf,int len,int flags);

  int PASCAL FAR recv(SOCKET s,const char FAR * buf,int len,int flags);

  参数Qbuf:指向存有传输数据的缓冲区的指针?

  6、多路复用——select()

  功能Q用来检一个或多个套接字状态?br />
  格式Qint PASCAL FAR select(int nfds,fd_set FAR * readfds,fd_set FAR * writefds,
fd_set FAR * exceptfds,const struct timeval FAR * timeout);

  参数Qreadfds:指向要做L的指针

     writefds:指向要做写检的指针

     exceptfds:指向要检是否出错的指针

     timeout:最大等待时?/p>

  7、关闭套接字——closesocket()

  功能Q关闭套接字s

  格式QBOOL PASCAL FAR closesocket(SOCKET s);

三、典型过E图

  2.1 面向q接的套接字的系l调用时序图



  2.2 无连接协议的套接字调用时序图



   2.3 面向q接的应用程序流E图




? 2007-01-30 22:04 发表评论
]]>
Winsocket~程之TCP/IP体系l构http://www.shnenglu.com/eday/archive/2007/01/30/18188.html??Tue, 30 Jan 2007 13:59:00 GMThttp://www.shnenglu.com/eday/archive/2007/01/30/18188.html一、TCP/IP 体系l构与特?/strong>

  1、TCP/IP体系l构

  TCP/IP协议实际上就是在物理|上的一l完整的|络协议。其中TCP是提供传输层服务Q而IP则是提供|络层服务。TCP/IP包括以下协议Q(l构如图1.1Q?/p>


(?.1)

  IPQ?|间协议(Internet Protocol) 负责L间数据的路由和网l上数据的存储。同时ؓICMPQTCPQ   UDP提供分组发送服务。用戯E通常不需要涉及这一层?br />
  ARPQ?地址解析协议(Address Resolution Protocol)
   此协议将|络地址映射到硬件地址?br />
  RARPQ?反向地址解析协议(Reverse Address Resolution Protocol)
   此协议将g地址映射到网l地址

  ICMPQ?|间报文控制协议(Internet Control Message Protocol)
   此协议处理信兛_L的差错和传送控制?br />
  TCPQ?传送控制协?Transmission Control Protocol)
   q是一U提供给用户q程的可靠的全双工字节流面向q接的协议。它要ؓ用户q程提供虚电路服务,qؓ数据可靠传输建立查。(注:大多数网l用L序用TCPQ?br />
  UDPQ?用户数据报协?User Datagram Protocol)
   q是提供l用戯E的无连接协议,用于传送数据而不执行正确性检查?br />
  FTPQ?文g传输协议(File Transfer Protocol)
   允许用户以文件操作的方式Q文件的增、删、改、查、传送等Q与另一L怺通信?br />
  SMTPQ?单邮件传送协?Simple Mail Transfer Protocol)
   SMTP协议为系l之间传送电子邮件?br />
  TELNETQ终端协?Telnet Terminal Procotol)
   允许用户以虚l端方式讉Kq程L

  HTTPQ?文本传输协?Hypertext Transfer Procotol)
  
  TFTP: 单文件传输协?Trivial File Transfer Protocol)

  2、TCP/IP特点

  TCP/IP协议的核心部分是传输层协?TCP、UDP)Q网l层协议(IP)和物理接口层Q这三层通常是在操作pȝ内核中实现。因此用户一般不涉及。编E时Q编E界面有两种形式Q一、是由内核心直接提供的系l调用;二、用以库函数方式提供的各种函数。前者ؓ核内实现Q后者ؓ核外实现。用h务要通过核外的应用程序才能实玎ͼ所以要使用套接?socket)来实现?br />
  ?.2是TCP/IP协议核心与应用程序关pd?br />

(?.2)

  二、专用术?/b>

  1、套接字

  套接字是|络的基本构件。它是可以被命名和寻址的通信端点Q用中的每一个套接字都有其类型和一个与之相q听q程。套接字存在通信区域Q通信区域又称地址)中。套接字只与同一区域中的套接字交换数据(跨区域时Q需要执行某和{换进E才能实玎ͼ。WINDOWS 中的套接字只支持一个域——网际域。套接字hcd?br />
  WINDOWS SOCKET 1.1 版本支持两种套接字:套接字(SOCK_STREAM)和数据报套接?SOCK_DGRAM)

  2、WINDOWS SOCKETS 实现

  一个WINDOWS SOCKETS 实现是指实现了WINDOWS SOCKETS规范所描述的全部功能的一套Y件。一般通过DLL文g来实?/p>

  3、阻塞处理例E?br />
  d处理例程(blocking hook,d钩子)是WINDOWS SOCKETS实现Z支持d套接字函数调用而提供的一U机制?/p>

  4、多址q播QmulticastQ多点传送或l播Q?br />
  是一U一对多的传输方式,传输发v者通过一ơ传输就信息传送到一l接收者,与单点传?br />(unicast)和广?Broadcast)相对应?/p>

? 2007-01-30 21:59 发表评论
]]>
99þþƷѿһ| 91Ʒɫ۾þ| ޾ƷĻþò| ޾þþһ| ɫ꼤þۺ| һþþƷһ| 91Ʒ91þþþþ| þþƷ޾Ʒ| þþþþëƬѲ| þ99Ʒþþþþ9| ɫۺϾþþþ| 99þùۺϾƷˮ | ˺ݺۺϾþ88| ˾ƷþѶ| ޹ۺϾþ| Ʒxxxxˮ޹Ʒþһ | þþþƵ| þþƷ99þþ| պŷۺϾþӰԺDs| Ʒʾþþþ999Ұ| þþþŮʦһ| þþþùһëƬ| ޾ƷþþþĻ69| ƷþþþóѶ| ޹Ʒþþþվ | ޾ƷþþþþͼƬ| þþƷav| Ʒþþþþù| þþƷh| þAv뾫Ʒϵ| þþþþþƷѿSSS| ҹƷþþþþӰ777 | þûɫƵ| Ʒþˬۺ | þ¾Ʒ| þõӰ| һþ㽶| ޹Ʒ˾þ| ɫɫۺϾþҹҹ| ޹Ʒþ| ŷ޹Ʒþ|