Winsock IO模型.docx
- 文档编号:3664122
- 上传时间:2022-11-24
- 格式:DOCX
- 页数:86
- 大小:266.62KB
Winsock IO模型.docx
《Winsock IO模型.docx》由会员分享,可在线阅读,更多相关《Winsock IO模型.docx(86页珍藏版)》请在冰豆网上搜索。
WinsockIO模型
【网络编程】之一、初识WinSocket
Winsock是Windows下的网络编程接口,它是由Unix下的BSDSocket发展而来,是一个与网络协议无关的编程接口。
Winsock在常见的Windows平台上有两个主要的版本,即Winsock1和Winsock2。
编写与Winsock1兼容的程序你需要引用头文件WINSOCK.H,如果编写使用Winsock2的程序,则需要引用WINSOCK2.H此外还有一个MSWSOCK.H头文件,它是专门用来支持在Windows平台上高性能网络程序扩展功能的。
使用WINSOCK.H头文件时,同时需要库文件WSOCK32.LIB,使用WINSOCK2.H时,则需要WS2_32.LIB,如果使用MSWSOCK.H中的扩展API,则需要MSWSOCK.LIB。
正确引用了头文件,并链接了对应的库文件,你就构建起编写WINSOCK网络程序的环境了。
OK,下面我们来看看具体用法:
初始化winsocket
每个Winsock程序必须使用WSAStartup载入合适的Winsock动态链接库,如果载入失败,WSAStartup将返回SOCKET_ERROR,这个错误就是WSANOTINITIALISED,WSAStartup的定义如下:
[cpp] viewplain copy
1.int WSAStartup(
2. WORD wVersionRequested,
3. LPWSADATA lpWSAData
4.);
wVersionRequested指定了你想载入的Winsock版本,其高字节指定了次版本号,而低字节指定了主版本号。
你可以使用宏MAKEWORD(x,y)来指定版本号,这里x代表主版本,而y代表次版本。
lpWSAData是一个指向WSAData结构的指针,WSAStartup会向该结构中填充其载入的Winsock动态链库的信息。
lpWSAData是一个指向WSAData结构的指针,WSAStartup会向该结构中填充其载入的Winsock动态链库的信息。
[cpp] viewplain copy
1.typedef struct WSAData
2.{
3. WORD wVersion; //wVersion为你将使用的Winsock版本号
4. WORD wHighVersion;//wHighVersion为载入的Winsock动态库支持的最高版本,
5.注意,它们的高字节代表次版本,低字节代表主版本。
6. char szDescription[WSADESCRIPTION_LEN + 1];
7. char szSystemStatus[WSASYS_STATUS_LEN + 1];//上面两个变量由特定版本的Winsock设置,实际上没有太大用处。
8. unsigned short iMaxSockets;//表示最大数量的并发Sockets,其值依赖于可使用的硬件资源。
9. unsigned short iMaxUdpDg;// iMaxUdpDg表示数据报的最大长度
10. char FAR * lpVendorInfo;//是为Winsock实现而保留的制造商信息,
11.这个在Windows平台上并没有什么用处.
12.} WSADATA, * LPWSADATA;
当你使用完Winsock接口后,要调用下面的函数对其占用的资源进行释放:
intWSACleanup(void);
如果调用该函数失败也没有什么问题,因为操作系统为自动将其释放,对应于每一个WSAStartup调用都应该有一个WSACleanup调用.
错误处理:
Winsock函数调用失败大多会返回SOCKET_ERROR(实际上就是-1),你可以调用WSAGetLastError得到错误的详细信息:
[cpp] viewplain copy
1.int WSAGetLastError (void);
对该函数的调用将返回一个错误码,其码值在WINSOCK.H或WINSOCK2.H(根据其版本)中已经定义,这些预定义值都以WSAE开头.同时你还可以使用WSASetLastError来自定义错误码值.
代码示例:
下面是来自msdn的示例:
[cpp] viewplain copy
1.#define WIN32_LEAN_AND_MEAN
2.
3.#include
4.#include
5.#include
6.#include
7.
8.// Need to link with Ws2_32.lib
9.#pragma comment(lib, "ws2_32.lib")
10.
11.
12.int __cdecl main()
13.{
14.
15. WORD wVersionRequested;
16. WSADATA wsaData;
17. int err;
18.
19./* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */
20. wVersionRequested = MAKEWORD(2, 2);
21.
22. err = WSAStartup(wVersionRequested, &wsaData);
23. if (err !
= 0) {
24. /* Tell the user that we could not find a usable */
25. /* Winsock DLL. */
26. printf("WSAStartup failed with error:
%d\n", err);
27. return 1;
28. }
29.
30./* Confirm that the WinSock DLL supports 2.2.*/
31./* Note that if the DLL supports versions greater */
32./* than 2.2 in addition to 2.2, it will still return */
33./* 2.2 in wVersion since that is the version we */
34./* requested. */
35.
36. if (LOBYTE(wsaData.wVersion) !
= 2 || HIBYTE(wsaData.wVersion) !
= 2) {
37. /* Tell the user that we could not find a usable */
38. /* WinSock DLL. */
39. printf("Could not find a usable version of Winsock.dll\n");
40. WSACleanup();
41. return 1;
42. }
43. else
44. printf("The Winsock 2.2 dll was found okay\n");
45.
46.
47./* The Winsock DLL is acceptable. Proceed to use it. */
48.
49./* Add network programming using Winsock here */
50.
51./* then call WSACleanup when done using the Winsock dll */
52.
53. WSACleanup();
54.
55.}
这里只是初识winsocket!
大概了解一下!
【网络编程】之二、socketAPI学习
套接字API函数:
1、socket函数:
The socket functioncreatesasocketthatisboundtoaspecifictransportserviceprovider.
2、bind函数:
The bind functionassociatesalocaladdresswithasocket.(一般用于服务器)
3、listen函数:
The listen functionplacesasocketinastateinwhichitislisteningforanincomingconnection.(一般用于服务器)
4、connect函数:
The connect functionestablishesaconnectiontoaspecifiedsocket.(用户客户端)
5、accept函数:
The accept functionpermitsanincomingconnectionattemptonasocket.(用于服务器)
6、send和recv,sendto和recvfrom函数:
sendandrecvdata!
下面我们就来挨个讲解:
socket函数
函数原型是:
[cpp] viewplain copy
1.SOCKET WSAAPI socket(
2. __in int af,
3. __in int type,
4. __in int protocol
5.);
socket函数来创建一个能够进行网络通信的套接字!
第一个参数af指的是给创建的套接字制定一个地址族!
这些值已经在windows头文件中给你定义好了!
一般形式是AF_或者PF_!
MSDN中有一句话是NotethatthevaluesfortheAF_addressfamilyandPF_protocolfamilyconstantsareidentical(forexample, AF_INETand PF_INET),soeitherconstantcanbeused.
AF_INET:
表示IPV4网络协议;
AF_INET6:
表示IPV6网络协议;
AF_UNIX:
表示本地套接字(使用一个文件)
第二个参数type指的是要创建的套接字的类型,他的值也是在头文件中一定定义好的!
SOCK_STREAM:
TCP进行传输
SOCK_DGRAM:
UDP进行传输
SOCK_RAW:
在网络层上的原始套接字,这个socket类型使用ICMP公共协议
SOCK_SEQPACKET:
可靠地连续数据包服务
最后一个参数protocol指定的是实际使用的传输协议!
他的值也是定义在 Winsock2.h and Wsrm.h headerfiles.。
如果在这里设为0的话,那么我们就是使用前两个参数指定的缺省协议!
如果函数失败,就返回INVALID_SOCKET。
也就是-1,否则,函数会返回一个代表新分配的描述符的整数。
bind函数:
函数原型是:
[cpp] viewplain copy
1.int bind(
2. __in SOCKET s,
3. __in const struct sockaddr *name,
4. __in int namelen
5.);
他为一个套接字分配地址,你用socket函数创建了套接字后,你只是指定了协议,并没有分配地址,只是用默认的地址和端口号。
在他进行其他操作之前,服务器必须先要用bind函数为他绑定上地址和端口号!
而客户端就不要这样做了。
第一个参数s是一个socket的描述符,你要把你生成的套接字传进来!
注意,这个socket是未绑定的。
第二个参数是一指向sockaddr类型的指针。
有两个简单的结构:
[cpp] viewplain copy
1.struct sockaddr {
2. ushort sa_family;
3. char sa_data[14];
4.};
5.
6.struct sockaddr_in {
7. short sin_family;// rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px; ">置AF_INET
8. u_short sin_port;// rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px; ">指明端口号
9. struct in_addr sin_addr;
10. char sin_zero[8];
11.};
其中sa_family是地址族。
对TCP/IP的协议族我们通常传入AF_INET。
在用TCP/IP进行绑定的时候,我们通常使用下面的sockaddr_in结构。
下面说一下sin_addr:
sin_addr结构体中只有一个唯一的字段s_addr,表示IP地址,该字段是一个整数,一般用函数inet_addr()把字符串形式的IP地址转换成unsigned long型的整数值后再置给s_addr。
我们也可以把htonl(INADDR_ANY)置给s_addr,这样做的好处是不论哪个网段上的客户程序都能与该服务程序通信;
用0来填充sin_zero数组,目的是让sockaddr_in结构的大小与sockaddr结构的大小一致。
上面还有最后一个参数namelen没有说,相信应该知道了,这是用来指定socketaddr的长度的。
[cpp] viewplain copy
1.struct sockaddr_in saddr;
2.saddr.sin_family = AF_INET;
3.saddr.sin_port = htons(8888);
4.saddr.sin_addr.s_addr = htonl(INADDR_ANY);
listen函数:
函数原型是:
[cpp] viewplain copy
1.int listen(
2. __in SOCKET s,
3. __in int backlog
4.);
服务器调用listen函数,使得套接字socket处于监听状态!
监听链接请求!
只能在可靠数据流中使用,如:
SOCK_STREAM,SOCK_SEQPACKET;并且处于监听状态的流套接字s将维护一个客户连接请求队列,该队列最多容纳backlog个客户连接请求。
函数调用成功返回0,如果失败就返回SOCKET_ERROR;
connect函数:
函数原型是:
[cpp] viewplain copy
1.int connect(
2. __in SOCKET s,
3. __in const struct sockaddr *name,
4. __in int namelen
5.);
客户端调用connect函数使得sockets与监听于name所指定的计算机的特定端口上的服务Socket进行连接。
对于一些无连接的UDP协议,链接默认发送和接收数据的主机由给定的地址确定。
函数成功返回0,失败则返回-1(SOCKET_ERROR);
[cpp] viewplain copy
1.struct sockaddr_in daddr;
2.memset((void *)&daddr,0,sizeof(daddr));
3.daddr.sin_family=AF_INET;
4.daddr.sin_port=htons(8888);
5.daddr.sin_addr.s_addr=inet_addr("127.0.0.1");
6.connect(socket,(struct sockaddr *)&daddr,sizeof(daddr));
accept函数:
函数原型是:
[cpp] viewplain copy
1.SOCKET accept(
2. __in SOCKET s,
3. __out struct sockaddr *addr,
4. __inout int *addrlen
5.);
服务器通过调用accept函数从处于监听listen状态的socket的客户队列中取出排在最前面的请求!
并且创建一个新的套接字,并且从监听队列中移除这个链接!
函数调用成功就返回套接字描述符,否则失败就返回INVALID_SOCKET -1;
s:
监听的套接字描述符;
addr:
指向sockaddr结构的指针。
客户机地址信息。
addrlen:
确定客户机结构的大小;
[cpp] viewplain copy
1.struct sockaddr_in SocketAddr;
2.int addrlen;
3.addrlen=sizeof(SocketAddr);
4.ServerSocket=accept(Socket,(struct sockaddr *)&SocketAddr,&addrlen);
send和recv函数:
函数原型是:
[cpp] viewplain copy
1.int send(
2. __in SOCKET s,
3. __in const char *buf,
4. __in int len,
5. __in int flags
6.);
不论是客户还是服务器应用程序都用send函数来向TCP链接的另一端发送数据。
客户程序一般用send函数向服务器发送请求,而服务器则通常用send函数来向客户程序发送应答;
s:
指定发送端套接字描述符;
buf:
一个存放着应用程序要发送数据的缓冲区;
len:
实际要发送的数据的字节数;
flags:
标志位,影响函数行为,一般置为0;
注意:
每一个除send外的Socket函数在执行的最开始总要先等待套接字的发送缓冲中的数据被协议传送完毕才能继续,如果在等待时出现网络错误,那么该Socket函数就返回SOCKET_ERROR
[cpp] viewplain copy
1.int recv(
2. __in SOCKET s,
3. __out char *buf,
4. __in int len,
5. __in int flags
6.);
不论是客户还是服务器应用程序都用recv函数从TCP连接的另一端接收数据;
s:
指定接收端套接字描述符;
buf:
指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据;
len:
指明buf的长度;
flags:
标志位,影响函数的行为,一般为0;
如果recv函数出错就会返回SOCKET_ERROR,如果recv函数在等待协议接受数据时网络中断了,就会返回0;
send分为阻塞和非阻塞:
阻塞模式下,如果正常的话,会直到把你所需要发送的数据发完再返回;
非阻塞模式下,会根据你的socket在底层的可用缓冲区的大小来将你的缓冲区当中的数据拷贝过去,有多大的缓冲区就拷贝多少。
缓冲区满了就立即返回。
这个时候的返回值只表示拷贝到缓冲区多少数据,但是并不代表发送多少数据。
同时剩下的部分需要你再次调用send才会再一次拷贝到底层缓冲区;
【网络编程】之三、socket网络编程
前面两篇看socket的入门一些知识还有一些常用的API,现在来解释一些概念,还有就是tcp和udp客户端服务器基本模式!
阻塞和非阻塞
套接字可以处于阻塞模式或者非阻塞模式,调用任何一个阻塞模式的函数都会产生同样的结果。
(用或长或短的时间等待操作的完成) 当套接字处于非阻塞模式的时候,API函数的调用会立即返回,但是大多会失败,返回WSAEWOULDBLOCK错误。
意味着请求操作在调用期间没有时间完成。
举个例子,阻塞就是当你拨打我的电话的时候,我不在,于是你拿电话等我回来,这期间不能再用电话了。
非阻塞就是你拨打我的电话,但是我不在,这时你挂断了电弧啊,待会再打,至于我是不是在,只有打了电话才知道。
在32位系统中,可以采用多线程的方法,在不同的线程中使用套接字,这样即使某个线程中的套接字被阻塞也不会影响应用程序的其他操作,同时也不会再阻塞线程上耗费CPU时间。
字节序
不同的计算机中用不同的字节顺序存储数据。
任何从winsock函数对IP地址和端口号的引用和传送给winsock函数的IP地址和端口号均是按照网络顺序组织的,这也包括了sockaddr_in这个数据结构中的IP地址域和端口域,但是不包括sin_family域。
一个应用程序通常用与’时间‘服务对应的端口来和服务器连接,而服务器提供某种机制来通知用户使用另一端口,因此getservbyname函数返回的端口号已经是网络顺序了。
可以直接用来组成一个地址,不需要进行转换。
如果用户输入一个数,而制定使用这一端口号,应用程序则必须在使用它建立地址以前把它从主机顺序转换成网络顺序。
相应的,如果应用程序希望显示包含于某一地址中的端口号,则这一端口号就必须在被显示前从网络地址顺序转换到主机顺序。
广播
数据报套接字可以用来向许多系统支持的网络发送广播数据包。
网络上必须先要支持广播功能。
发送广播数据包的能力被限制于那些用显示标记了允许广播的套接字中,分为以下情况:
1、一个应用程序希望在本地网络中找到一个资源,而应用程序对该资源的地
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Winsock IO模型 IO 模型