SOCKET socket(int family,int type,int protocol)? |
SOCKRET s;?br />s=socket(AF_INET,SOCK_DGRAM,0);?br />或s=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP)?/td> |
int bind(SOCKET s,struct sockaddr_in*name,int namelen)?/td> |
//讄有关的全局变量?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; } { |
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) |
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> |
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> |
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; } |
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); } |
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|!"); } |
![]() 面向q接的流式套接字~程程C意? |
socket()->bind()->listen->accept()->recv()/send()->closesocket() |
socket()->connect()->send()/recv()->closesocket() |
sock=socket(AF_INET,SOCK_STREAM,0); |
sockin.sin_family=AF_INET; sockin.sin_addr.s_addr=0; sockin.sin_port=htons(USERPORT); bind(sock,(LPSOCKADDR)&sockin,sizeof(sockin))); |
//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; } |
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; } } |
//{{AFX_MSG(CNetServerView) //}}AFX_MSG void OnSocket(WPARAM wParam,LPARAM lParam); DECLARE_MESSAGE_MAP() |
BEGIN_MESSAGE_MAP(CNetServerView, CView) //{{AFX_MSG_MAP(CNetServerView) //}}AFX_MSG_MAP ON_MESSAGE(WM_SOCKET_MSG,OnSocket) END_MESSAGE_MAP() |
WSAAsyncSelect(s,hwnd,wMsg1,FD_READ); WSAAsyncSelect(s,hwnd,wMsg2,FD_CLOSE); |
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)); |
int setsockopt( SOCKET s, int level, int optname, const char FAR *optval, int optlen );函数关闭? |
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");; |
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);; } |
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? } |
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 { //发? } |
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 ); |
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); |
int PASCAL FAR listen( SOCKET s, int backlog ); ?敎ͼ sQ需要徏立监听的SocketQ?br />backlogQ最大连接个敎ͼ |
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上收到带外数据时收到消息? |
switch(lParam) {case FD_READ: … break; case FD_WRITE?br /> ?br /> break; ?br />} |
SOCKET PASCAL FAR accept( SCOKET s, struct sockaddr FAR *addr,int FAR *addrlen ); 参数QsQSocket的识别码Q?br />addrQ存放来q接的客L的地址Q?br />addrlenQaddr的长?/td> |
int PASCAL FAR closesocket( SOCKET s ); ?敎ͼsQSocket 的识别码Q?br />int PASCAL FAR WSACleanup( void ); ?敎ͼ ?/td> |
int PASCAL FAR connect( SOCKET s, const struct sockaddr FAR *name, int namelen ); ?敎ͼsQSocket 的识别码Q?br />nameQSocket惌q接的对方地址Q?br />namelenQname的长?/td> |
int PASCAL FAR send( SOCKET s, const char FAR *buf,int len, int flags ); 参数QsQSocket 的识别码 bufQ存放要传送的资料的暂存区 len bufQ的长度 flagsQ此函数被调用的方式 |
int PASCAL FAR recv( SOCKET s, char FAR *buf, int len, int flags ); 参数QsQSocket 的识别码 bufQ存放接收到的资料的暂存?br />len bufQ的长度 flagsQ此函数被调用的方式 |
////////////////////////////////////// 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'; } |
二、主要扩充说?/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)){...} |
首先服务器方要先启动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图
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>