Winsock 函数简介

1、WSAStartup函数用于初始化Winsock [声明] nt WSAStarup(WORD wVersionRequested,LPWSADATA lpWSAData); [参数] wVersionRequested - 要求使用Winsock的最低版本号 lpWSAData - Winsock的详细资料 [返回值] 当函数成功调用时返回0 失败时返回非0的值

2、socket函数用于生成socket(soket Descriptor) [声明] SOCKET socket(int af,int type,int protocol); [参数] af - 地址家族(通常使用:AF_INET) type - socket的掷?br> SOCK_STREAM : 用于TCP协议 SOCK_DGRAM : 用于UDP协议 protocol - 所使用的协议 [返回值] 当函数成功调用时返回一个新的SOCKET(Socket Descriptor) 失败时返回INVALID_SOCKET.

3、inet_addr函数把好象"xxx.xxx.xxx.xxx"的10进制的IP地址转换为32位整数表示方法 [声明] unsigned long inet_addr ( const char FAR *cp ); [参数] cp - 指向用"xxx.xxx.xxx.xxx"的10进制来表示的IP地址字符串的指针 [返回值] 当函数成功调用时返回用32位整数表示的IP地址(按网络字节排列顺序) 失败时返回INADDR_NONE.

4、gethostbyname函数可以从主机名获取主机资料. [声明] struct hostent FAR * gethostbyname ( const char FAR *name ); [参数] name - 指向主机名字符串的指针 [返回值] 当函数成功调用时返回主机信息失败时返回NULL(空值)

5、Bind函数指定本地IP地址所使用的端口号时候使用 [声明] int bind ( SOCKET s , const struct sockaddr FAR *addr , int namelen ); [参数] s - 指向用Socket函数生成的Socket Descriptor addr - 指向Socket地址的指针 namelen - 该地址的长度. [返回值] 当函数成功调用时返回0 调用失败时返回 SOCKET_ERROR

6、connect函数用于与服务器建立连接,发出连接请求,必须在参数中指定服务器的IP地址和端口号 [声明] int connect (SOCKET s , const struct sockaddr FAR *name , int namelen ); [参数] s - 指向用Socket函数生成的Socket Descriptor name - 指向服务器地址的指针 namelen - 该地址的长度. [返回值] 当函数成功调用时返回0 调用失败时返回 SOCKET_ERROR

7、select函数可以用于调查一个或多个SOCKET的状态. [声明] int select ( int nfds , fd_set FAR *readfds , fd_set FAR *writefds , fd_set FAR *exceptfds , const struct timeval FAR *timeout ); [参数] nfds - 在WINDOWS SOCKET API 中该参数可以忽略,通常赋予NILL值 readfds - 由于接受的SOCKET设备的指针 writefds - 用于发送数据的SOCKET设备的指针 exceptfds - 检查错误的状态 timeout - 超时设定 [返回值] 返回大于0的值时,表示与条件相符的SOCKET数返回0表示超时失败时返回SOCKET_ERROR 8、recv函数利用Socket进行接受数据. [声明] int recv ( SOCKET s , char FAR *buf , int len , int flags ); [参数] s - 指向用Socket函数生成的Socket Descriptor buf - 接受数据的缓冲区(数组)的指针 len - 缓冲区的大小 flag - 调用方式(MSG_PEEK 或 MSG_OOB) [返回值] 成功时返回收到的字节数. 如果连接被中断则返回0 失败时返回 SOCKET_ERROR

9、sendto函数利用Socket进行发送数据. [声明] int sendto ( SOCKET s , const char FAR *buf , int len , int flags , const struct sockaddr FAR *to , int token ); [参数] s - 指向用Socket函数生成的Socket Descriptor buf - 接受数据的缓冲区(数组)的指针 len - 缓冲区的大小 flag - 调用方式(MSG_DONTROUTE , MSG_OOB) to - 指向发送方SOCKET地址的指针 token - 发送方SOCKET地址的大小 [返回值] 成功时返回已经发送的字节数. 失败时返回SOCKET_ERROR Winsock开发网络通信程序的经典入门

 

对于许多初学者来说,网络通信程序的开发,普遍的一个现象就是觉得难以入手。许多概念,诸如:同步(Sync)/异步(Async),阻塞(Block)/非阻塞(Unblock)等,初学者往往迷惑不清,只知其所以而不知起所以然。 同步方式指的是发送方不等接收方响应,便接着发下个数据包的通信方式;而异步指发送方发出数据后,等收到接收方发回的响应,才发下一个数据包的通信方式。 阻塞套接字是指执行此套接字的网络调用时,直到成功才返回,否则一直阻塞在此网络调用上,比如调用recv()函数读取网络缓冲区中的数据,如果没有数据到达,将一直挂在recv()这个函数调用上,直到读到一些数据,此函数调用才返回;而非阻塞套接字是指执行此套接字的网络调用时,不管是否执行成功,都立即返回。比如调用recv()函数读取网络缓冲区中数据,不管是否读到数据都立即返回,而不会一直挂在此函数调用上。

 

在实际Windows网络通信软件开发中,异步非阻塞套接字是用的最多的。平常所说的C/S(客户端/服务器)结构的软件就是异步非阻塞模式的。 对于这些概念,初学者的理解也许只能似是而非,我将用一个最简单的例子说明异步非阻塞Socket的基本原理和工作机制。目的是让初学者不仅对 Socket异步非阻塞的概念有个非常透彻的理解,而且也给他们提供一个用Socket开发网络通信应用程序的快速入门方法。操作系统是Windows 98(或NT4.0),开发工具是Visual C++6.0。

MFC提供了一个异步类CAsyncSocket,它封装了异步、非阻塞Socket的基本功能,用它做常用的网络通信软件很方便。但它屏蔽了 Socket的异步、非阻塞等概念,开发人员无需了解异步、非阻塞Socket的原理和工作机制。因此,建议初学者学习编网络通信程序时,暂且不要用 MFC提供的类,而先用Winsock2 API,这样有助于对异步、非阻塞Socket编程机制的理解。

      为了简单起见,服务器端和客户端的应用程序均是基于MFC的标准对话框,网络通信部分基于Winsock2 API实现。先做服务器端应用程序。用MFC向导做一个基于对话框的应用程序SocketSever,注意第三步中不要选上Windwos Sockets选项。在做好工程后,创建一个SeverSock,将它设置为异步非阻塞模式,并为它注册各种网络异步事件,然后与自定义的网络异步事件联系上,最后还要将它设置为监听模式。在自定义的网络异步事件的回调函数中,你可以得到各种网络异步事件,根据它们的类型,做不同的处理。

 

下面将详细介绍如何编写相关代码。在SocketSeverDlg.h文件的类定义之前增加如下定义:

#define NETWORK_EVENT WM_USER+166 file://定义网络事件 SOCKET ServerSock;

file://服务器端Socket 在类定义中增加如下定义: class CSocketSeverDlg : CDialog { public: SOCKET ClientSock[CLNT_MAX_NUM];

file://存储与客户端通信的Socket的数组 /*各种网络异步事件的处理函数*/ void OnClose(SOCKET CurSock); file://对端Socket断开 void OnSend(SOCKET CurSock);

file://发送网络数据包 void OnReceive(SOCKET CurSock);

file://网络数据包到达 void OnAccept(SOCKET CurSock);

file://客户端连接请求 BOOL InitNetwork();

 file://初始化网络函数 void OnNetEvent(WPARAM wParam, LPARAM lParam);

file://异步事件回调函数 … }; 在SocketSeverDlg.cpp文件中增加消息映射,其中OnNetEvent是异步事件回调函数名: ON_MESSAGE(NETWORK_EVENT,OnNetEvent) 定义初始化网络函数,在SocketSeverDlg.cpp文件的OnInitDialog()中调此函数即可。 BOOL CSocketSeverDlg::InitNetwork() { WSADATA wsaData; //初始化TCP协议 BOOL ret = WSAStartup(MAKEWORD(2,2), &wsaData); if(ret != 0) { MessageBox("初始化网络协议失败!"); return FALSE; } //创建服务器端套接字 ServerSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(ServerSock == INVALID_SOCKET) { MessageBox("创建套接字失败!");

closesocket(ServerSock);

WSACleanup(); return FALSE; } //绑定到本地一个端口上 sockaddr_in localaddr; localaddr.sin_family = AF_INET; localaddr.sin_port = htons(8888); //端口号不要与其他应用程序冲突

localaddr.sin_addr.s_addr = 0;

if(bind(ServerSock ,(struct sockaddr*)&localaddr,sizeof(sockaddr)) = = SOCKET_ERROR) { MessageBox("绑定地址失败!");

closesocket(ServerSock);

WSACleanup();

 return FALSE; } //将SeverSock设置为异步非阻塞模式,并为它注册各种网络异步事件,其中m_hWnd //为应用程序的主对话框或主窗口的句柄

if(WSAAsyncSelect(ServerSock, m_hWnd, NETWORK_EVENT, FD_ACCEPT | FD_CLOSE | FD_READ | FD_WRITE) == SOCKET_ERROR) { MessageBox("注册网络异步事件失败!");

WSACleanup();

return FALSE; } listen(ServerSock, 5);

 file://设置侦听模式 return TRUE; }

 

下面定义网络异步事件的回调函数 void CSocketSeverDlg::OnNetEvent(WPARAM wParam, LPARAM lParam) { //调用Winsock API函数,得到网络事件类型 int iEvent = WSAGETSELECTEVENT(lParam); //调用Winsock API函数,得到发生此事件的客户端套接字 SOCKET CurSock= (SOCKET)wParam;

switch(iEvent) {

case FD_ACCEPT: //客户端连接请求事件

     OnAccept(CurSock); break;

case FD_CLOSE: //客户端断开事件: OnClose(CurSock); break;

 case FD_READ: //网络数据包到达事件 OnReceive(CurSock); break;

case FD_WRITE: //发送网络数据事件 OnSend(CurSock); break; default: break; } }

 

     以下是发生在相应Socket上的各种网络异步事件的处理函数,其中OnAccept传进来的参数是服务器端创建的套接字,OnClose()、 OnReceive()和OnSend()传进来的参数均是服务器端在接受客户端连接时新创建的用与此客户端通信的Socket。

void CSocketSeverDlg::OnAccept(SOCKET CurSock) { //接受连接请求,并保存与发起连接请求的客户端进行通信Socket //为新的socket注册异步事件,注意没有Accept事件 }

void CSocketSeverDlg::OnClose(SOCET CurSock) { //结束与相应的客户端的通信,释放相应资源 }

void CSocketSeverDlg::OnSend(SOCET CurSock) { //在给客户端发数据时做相关预处理 }

void CSocketSeverDlg::OnReceive(SOCET CurSock) { //读出网络缓冲区中的数据包 } 用同样的方法建立一个客户端应用程序。初始化网络部分,不需要将套接字设置为监听模式。注册异步事件时,没有FD_ACCEPT,但增加了 FD_CONNECT事件,因此没有OnAccept()函数,但增加了OnConnect()函数。向服务器发出连接请求时,使用connect()函数,连接成功后,会响应到OnConnect()函数中。下面是OnConnect()函数的定义,传进来的参数是客户端Socket和服务器端发回来的连接是否成功的标志。 void CSocketClntDlg::OnConnect(SOCKET CurSock, int error) { if(0 = = error) { if(CurSock = = ClntSock) MessageBox("连接服务器成功!"); } } 定义OnReceive()函数,处理网络数据到达事件; 定义OnSend()函数,处理发送网络数据事件; 定义OnClose()函数,处理服务器的关闭事件。 以上就是用基于Windows消息机制的异步I/O模型做服务器和客户端应用程序的基本方法。另外还可以用事件模型、重叠模型或完成端口模型,读者可以参考有关书籍。在实现了上面的例子后,你将对Winsock编网络通信程序的机制有了一定的了解。接下来你可以进行更精彩的编程, 不仅可以在网上传输普通数据,而且还以传输语音、视频数据,你还可以自己做一个网络资源共享的服务器软件,和你的同学在实验室的局域网里可以共同分享你的成果。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章