windows_socket编程实战.pdf

上传人:qwe****56 文档编号:70014368 上传时间:2023-01-14 格式:PDF 页数:10 大小:423.28KB
返回 下载 相关 举报
windows_socket编程实战.pdf_第1页
第1页 / 共10页
windows_socket编程实战.pdf_第2页
第2页 / 共10页
点击查看更多>>
资源描述

《windows_socket编程实战.pdf》由会员分享,可在线阅读,更多相关《windows_socket编程实战.pdf(10页珍藏版)》请在得力文库 - 分享文档赚钱的网站上搜索。

1、windowswindowswindowswindowssocketsocketsocketsocket 编程实战编程实战Socket 中一个比较重要的结构体:hostent,应该用好这个结构体。只允许复制应用程序一个备份,不允许应用程序自己修改,只能由系统修改,在调用任何 socket 结构之前,都应该 copy 自己需要的信息。通讯编程详见 MSDN 中 Windows CE document-ApplicationDevelop-Communication Services-Windows Sockets 章节。1 winsock 的启动和终止WSAStartup():使用 winsoc

2、k 之前,必须对其进行初始化(VC 中项目创建时不包括 socket 的时候),将其加载,否则,将返回 SOCKET_ERROR 错误,错误信息为 WSANOTINITIALIZED。用 WSAStartup函数可加载 WinSock 库函数声明:int WSAStartup(WORD wVersionRequested,LPWSADATA lpWSAData);/成功返0,否则返非0参数说明:wVersionRequested:WinSock 库的版本号,高位指定副版本,低位指定主版本,可通过 MAKEWORD(X,Y)宏指定,X 为低位,Y 为高位。例:wVersionRequested=

3、MAKEWORD(1,2);lpWSAData:接收 WinSock 实现细节的 LPWSADATA 结构。Typedef struct WSAData WORDwVersion;/设置成准备使用的 WinSock 版本WORDwHighVersion;/存放的是现有的 WinSock 库的最高版本,与 wVersionRequested 参数相同charszDescriptionWSADESCRIPTION_LEN+1;charszSystemStatusWSASYSSTATUS_LEN+1;unsignedshortiMaxSockets;unsignedshortiMaxUdpDg;ch

4、ar FAR*lpVendorInfo;WSADATA,FAR*LPWSADATA;则一个程序要使用 1.2 版本的 WinSock,程序为:#include afxsock.hWORD wVersionRequested;WSADATA wsaData;int err;wVersionRequested=MAKEWORD(2,2);err=WSAStartup(wVersionRequested,&wsaData);if(err!=0)AfxMessageBox(socket初始化失败!);return;if(LOBYTE(wsaData.wVersion)!=2|HIBYTE(wsaDat

5、a.wVersion)!=2)WSACleanup();AfxMessageBox(socket创建版本错误!);return;各 windows 平台支持的 WinSock 最新版本平台WinSock 版本Win951.1Win982.2NT4.02.2Win20002.2Win CE1.1使用完 WinSock 释放所使用的资料:int WSACleanup(void);/成功返回0,否则返回 SOCKET_ERROR示例代码:WSACleanup();2 创建套接字Socket()和 WSASocket()网络通信必须得创建套接字,创建一个套接字,使用下面的函数:SOCKET Socke

6、t(int af,int type,int protocol);或者:SOCKETWSASocket(intaf,inttype,intprotocol,LPWSAPROTOCOL_INFOlpProtocolinfo,GROUP g,DWORD dwFlags)注:WSASocket 为 Socket 的 WinSock2 版本,其原理与方式与原函数类似。调用成功则返回 SOCKET 类型的套接字句柄,否则返回 INVALID_SOCKET 错误参数说明:af:协议簇,是常值,在 windowsSocket 网络程序中,其只能为 AF_INET,以下所有内容皆针对于 AF_INET 协议所言

7、协议簇常值协议簇常值协议簇注释AF_INET网际协议AF_IPXIPX/SPX 协议AF_NETBIOSNetBIOS 协议AF_APPLETALKAppleTalkAF_TAMATMAF_IRDAInfrared SocketsType:套接字的类型,常值。下列为 AFINET 协议簇支持的套接字类型:AFAFAFAFINETINETINETINET 协议簇支持的套接字类型协议簇支持的套接字类型套接字类型注释所用的通信协议SOCK_STREAM字节流套接字TCP 协议SOCK_DGRAM数据报套接字UDP 协议SOCK_RAW原始套接字ICMP,IGMP 等协议,此时 protocol 参数

8、需进行设置例如创建一个 TCP 套接字:s=Socket(AF_INET,SOCK_STREAM,0);3 指定本机地址bind()创建套接字后:服务器端:必须将其绑定到一个已知地址客户端:不必绑定,内核会为其选择地址。Int bind(SOCKET s,conststruct sockaddr FAR*name,int namelen)参数说明:s:等待客户进行连接的套接字name:指向 struct sockaddr 类型的缓冲区的指针,在调用 bind 前,必须先对其进行填充。内容为本地地址信息。Sockaddr 结构体定义:struct sockaddru_shortsa_family

9、;charsa_data14;常使用的是另一种结构体类型的 Socket 地址类型sockaddr_in:struct sockaddr_in shortsin_family;/协议簇,必为AF_INETu_shortsin_port;/端口号,使用前应用htons函数转换。structin_addrsin_addr;/IP地址,使用前应用htonl函数转换charsin_zero8;namelen:name 参数所指的缓冲区的长度,可用 sizeof 函数取得。使用如下代码为一个已创建的套接字绑定地址:SOCKET s;Structsockaddr_inlocaladdr;s=socket(

10、AF_INET,SOCK_STREAM,0);localaddr.sin_family=AF_INET;localaddr.sin_port=htons(5555);localaddr.sin_addr.s_addr=htonl(INADDR_ANY);/自动选取本地地址进行填充bind(s,(SOCKADDR*)&localaddr,sizeof(localaddr);4 建立套接字连接connect()和 WSAConnect()函数原型:int connect(SOCKET s,conststruct sockaddr FAR*name,int namelen)参数说明:s:上面连接服务

11、器的套接字name:一个指向struct sockaddr 类型的缓冲区的指针,调用connect前,必须对其进行缓冲。namelen:name 参数所指的缓冲区的长度,可用 sizeof 函数取得。WSAConnect()为 connect的 WinSock2 版本:intWSAConnect(SOCKET s,const struct sockaddr FAR*name,int namelen,LPWSABUFlpCallerData,LPWSABUF lpCalleeData,LPQOS lpSQOS,LPQOS lpGQOS);调用方法与connect类似。使用如下代码与地址为03.0

12、3.03.1,端口为5555的TCP服务器建立联系:SOCKET s;struct sockaddr_inremoteaddr;s=socket(AF_INET,SOCK_STREAM,0);remoteaddr.sin_family=AF_INET;remoteaddr.sin_port=htons(5555);remoteaddr.sin_addr.s_addr=inet_addr(“03.03.03.1”);/inet_addr将字符串形式的IP地址转换为正确的IN_ADDR结构的IP地址connect(s,(SOCKADDR*)&remoteaddr,sizeof(remoteaddr

13、);如果成功的与服务器建立连接,connect函数返回0,否则返回SOCKET_ERROR错误.5 监听连接listen()指示一个套接字等候进入连接,listen仅仅被TCP服务器调用。当函数Socket创建一个套接字时,它被假设一个主动套接字,即它是一个将调用connect发起连接的客户套接字,函数listen将未连接的套接字转换为被动套接字,指示内核应接收指向此套接字的连接请求。此时套接字将从CLOSED状态转换到LISTEN状态,套接字进入监听模式。函数原型为:int listen(SOCKET s,int backlog);参数说明:s:要监听的已绑定但却没有连接的套接字backlo

14、g:内核为此套接字排队的最大连接个数,通过指定backlog值我们可以对同时可处理的连接数进行限制,多于则被丢弃。基层协议本身对backlog有最大限制,如果设定大于它则被用最接近的合法数值代替。调用成功则返回0,不成功则返回SOCKET_ERROR错误。Listen()通常在bind()之后,accept之前调用,如下:(省略了错误检测代码)bind(s,(SOCKADDR*)&localaddr,sizeof(localaddr);listen(s,5);accept(.);6 接受连接请求accept()和 WSAAccept()accept()函数由TCP服务器调用,它从已完成连接队列

15、头返回下一个已完成连接。在阻塞方式下,如果队列已完成队列为空,则导致进程进入睡眠状态函数原型:SOCKET accept(SOCKET s,struct sockaddr FAR*addr,int FAR*addrlen);SOCKET WSAAccept(SOCKET s,struct sockaddr FAR*addr,LPINT addrlen,LPCONDITIONPROC lpfnCondition,DWORD dwCallbackData);WSAAceept是accept的WinSock2版本。参数说明:s:指定一个处于监听连接状态的套接字addr:指向一个 struct soc

16、kaddr类型的缓冲区,用来接收连接方的地,可以置NULL。addlen:整形指针,在调用时它所指的空间存放addr指向的缓冲区长度。在调用返回后,里面是返回的连接方的地址的确切长度。如果我们对客户的身份不想知道,则可将addr和addrlen参数置NULL.例如:newsock=accept(s,NULL,NULL);如果调用成功,则accept返回一个由内核生成的全新的套接字,注意,这个套接字和原来的bind的套接字不为同一个,即,一端至少得两个套接字,一个套接字为windows套接字,由socket创建,负责绑定,监听,一个为C/S间通讯的套接字,代表与客户机的连接,由accept创建,

17、而原来的套接字状态不变,仍处于监听状态,等待客户的连接请求。如果调用失败,则返回一个INVALID_SOCKET的错误。7 数据发送send()和 sendto()函数原型:int send(SOCKET s,constchar FAR*buf,int len,int flags);int sendto(SOCKET s,constchar FAR*buf,int len,int flags,const struct sockaddr FAR*to,inttolen);参数描述:s:发送端套接字描述符。Buf:发送数据的缓冲区地址。Len:实际要发送数据的字节数,应小于或者等于套接字s的缓冲区

18、长度,否则报错。Flags:发送数据方式(MSG_DONTROUTE:指定数据不交给路由选择;MSG_OOB:表示要发送带外数据,一般置0)to:接收方sockaddr 结构形式的地址。Tolen:地址结构的长度。说明:send函数并不传送数据,只是协议传的,send只是把buf中的数据copy到s的发送缓冲区的剩余空间里。Copy成功则返回拷贝的实际长度,否则任何错误都返回SOCKET_ERROR错误。注:sendto一般用于数据报(UDP).8 数据接收recv()和 recvfrom()recv()用于流套接字,TCP协议。和recvfrom()用于数据报,UDP协议。int recv(

19、SOCKET s,char FAR*buf,int len,int flags);int recvfrom(SOCKET s,char FAR*buf,int len,int flags,struct sockaddr FAR*from,int FARfromlen);参数说明:s:接收端套接字描述符。Buf:用来存放接收数据的缓冲区地址。Len:接收数据缓冲区的长度。Flags:处理数据方式(MSG_OOB:读取套接字上的带外数据;MSG_PEEK:查看输入数据,数据将被复制到缓冲区去,但不从输入队列中清除,一般置0)From:recv函数返回的发送方sockaddr 结构形式的地址。fom

20、len:地址结构的长度。说明:recv把接收缓冲区中的数据copy到buf中,返回实际copy的字节数,任何出错则返回SOCKET_RROR。Recvfom用于数据报协议。9 I/O 多路复用Select()指示多个事件中任何一个的发生,并且仅在一个或者多个事件发生或者经过某指定的时间后才唤醒进程。可以检测一个或者多个套接字的状态,满足给定状态的套接字集合由fd_set结构体指示,当select返回时,fd_set结构体被更新,同时select函数返回满足条件的套接字数目。int select(int nfds,fd_set FAR*readfds,fd_set FAR*writefds,fd

21、_set FAR*exceptfds,conststruct timeval FAR*timeout);参数说明:nfds:可忽略,系统为兼容性而设readfds,:指向套接字集合,select函数对该集合内的套接字是否可读进行检测。可选参数。Writefds:指向套接字集合,select函数对该集合内的套接字是否可写进行检测。可选参数。Exceptfds:指向套接字集合,select函数对该集合内的套接字是否出错进行检测。可选参数。Timeout:select等待超时的最大时间,timeval 结构类型的结构指针。如果设为NULL,则为阻塞操作。timeval 结构体定义:struct ti

22、meval longtv_sec;/secondslongtv_usec;/andmicroseconds;fd_set结构体的定义:typedef struct fd_set u_intfd_count;/how many are SET?SOCKETfd_arrayFD_SETSIZE;/an array of SOCKETs fd_set;常用一组宏定义操作fd_set类型的描述符集合:FD_CLR(s,*set):从集合中删除描述符s;FD_ISSET(s,*set):判断描述符s是否是集合中的一个元素,是则返回非0,不是则返回0;FD_SET(s,*set):增加描述符s到集合中。F

23、D_ZERO(*set):清空集合。10 释放连接closesocket()和 shutdown()通讯任务完成后,必须关闭连接以释放套接字句柄的资源。函数原型:int closesocket(SOCKET s);int shutdown(SOCKET s,int how);参数说明:s:要关闭的套接字句柄how:关闭时的操作选择(SD_RECEIVE:表示不再接收数据;SD_SEND:表示不再发送数据;SD_BOTH:表示取消连接两端的收发操作)说明:1.函数操作成功则返回0,否则返回SOCKET_ERROR2.closesocket有可能造成数据丢失,因为有可能有几个套接字描述符指向同一个

24、套接字数据结构。套接字数据结构中有一个字段专门存放该结构的被引用次数。当次数为1时系统则清除该结构,大于1时系统仅清除套接字描述符表中对应的表项,并次数递减1。Shutdown则可能做到“从容关闭”,因此尽可能选用shutdown函数。11 其它 API 函数11.1 getpeername()说明:获得与套接字相连的端地址信息。函数原型:调用成功则int getpeername(SOCKET s,struct sockaddr FAR*name,int FAR*namelen);参数说明:s:标识一已连接的套接字描述符name:接收端地址结构的名字namelen:一个指向名字结构的指针。注:

25、成功则返回0,否则返回SOCKET_ERROR。11.2 getsockname()说明:获得一个套接字的本地名字。函数原型:int getsockname(SOCKET s,struct sockaddr FAR*name,int FAR*namelen);参数说明:s:标识一已连接的套接字描述符name:接收端地址的名字结构。namelen:名字缓冲区长度。注:1.成功则返回0,否则返回SOCKET_ERROR。2.其用于一个已捆绑或者已连接的套接字s,本地址将被返回。当未绑定就connect()时,唯有此函数可以获知系统内定的本地地址。在返回时,namelen参数包含了名字的实际字节数。

26、3.若一个套接字与INADDR_ANY捆绑,即该套接字可以用任意主机的地址,此时除非用OnConnect或者accept来连接,否则getsockname不会返回主机地址的任何信息,所以INADDR_ANY主要用于多个主机环境中,单主机环境不应该使用此参数。11.3 gethostbyaddr()函数原型:struct HOSTENT FAR*gethostbyaddr(constchar FAR*addr,int len,int type)参数说明:addr:指向网络字节顺序地址的指针。len:地址的长度,在AF_INET类型地址中为4。type:地址类型,应为AF_INET。struct

27、HOSTENT结构:struct hostent char FAR*h_name;/正规的主机名字。char FAR*FAR*h_aliases;/一个以空指针结尾的可选主机名队列。shorth_addrtype;/返回地址的类型:AF_INET。shorth_length;/每个地址的长度(字节数),对AF_INET域为4char FAR*FAR*h_addr_list;/以空指针结尾的主机地址列表,返回地址以网络顺序排列。;注:gethostbyaddr()函数成功则返回对应于给定地址的包含主机名字和地址信息的hostent结构指针,这个结构由windows socket实现分配,应用程序

28、不应该试图修改或者释放其任何部分,每线程仅有这个结构的拷贝,所以应用程序在发出其它的socket API调用前,应该把自己所需的信息拷贝下来。失败返回空指针。11.4 Gethostbyname()根据名称取得运行代码的机器IP:一个例子:PHOSTENT hostinfo;CString ip;char name255;if(hostinfo=gethostbyname(name)!=NULL)ip=inet_ntoa(*(struct in_addr*)*hostinfo-h_addr_list);11.5 文件下载函数:来自于MSDN的web Workshop文档HRESULT URLD

29、ownloadToFile(LPUNKNOWN pCaller,/调用者:若为组件,则为组件接口地址,不为组件,则置NULLLPCTSTR szURL,/下载的URL地址,不能置NULLLPCTSTR szFileName,/下载的文件保存的文件名DWORD dwReserved,/0LPBINDSTATUSCALLBACK lpfnCB/调用者的IBindStatusCallback函数接口地址,可为NULL);12 在程序中显示 SOCKET 错误信息程序员自行分配信息缓冲区的方法WSAShowError()TCHAR errMsg2048=0;FormatMessage(FORMAT_M

30、ESSAGE_FROM_SYSTEM,GetModuleHandle(TEXT(ws2_32.dll),WSAGetLastError(),NULL,errMsg,sizeof(errMsg)/sizeof(TCHAR),NULL);MessageBox(errMsg,TEXT(ws2_32.dll模块中关于此错误的详细说明:),MB_ICONERROR);/AfxMessageBox(errMsg);/或者用系统对话框来显示/SetDlgItemText(IDC_STATIC1,errMsg);/或者用静态文本框显示/MessageBox(ghMainWnd,errMsg,TEXT(ws2_

31、32.dll),MB_ICONERROR);/上一行例子为原摘录,无法调试通过系统动态分配信息缓冲区的方法:LPVOID lpErrMsg;FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_IGNORE_INSERTS,GetModuleHandle(TEXT(ws2_32.dll),WSAGetLastError(),MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),(LPTSTR)&lpErrMsg,sizeof(lpErrMsg),N

32、ULL);MessageBox(lpErrMsg,TEXT(ws2_32.dll模块中关于此错误的详细说明:),MB_ICONERROR);LocalFree(lpErrMsg);/释放不再使用的空间13 初始化 Sokcet IP 地址的一个例子sockaddr_incli;/创建 IP 地址结构体对象clicli.sin_addr.s_addr=inet_addr(ipaddress);/ipaddress为一编辑控件的Cedit类型的字段,预先用CtrlName.GetWindowText(ipaddress,nMaxLength)来取得其值/inet_addr把字符串形式的地址转为什么

33、socket可以识别的IN_ADDR形式,即IN_ADDR是最高效的地址cli.sin_family=AF_INET;/windows socket必须使用AF_INET,无其它选择cli.sin_port=htons(5000);/把端口从主机h(ost)向to网络n(et)顺序s(equence)转换SOCKET clisock;/创建一个socket对象clisock=socket(AF_INET,SOCK_STREAM,0);/socket初始化14 通讯程序中应该注意的长度问题在socket程序中,当使用到长度时,尽量使用sizeof()函数,少使用具体的数值;同时,应该注意凡是双方要接收的量,应该保持一致,否则极易出现没有任何问题,但就是输出不正确的问题比如:客户端缓存发送数据的字符串长度 100,而接收端缓存字符串的长度不是 100,则哪怕发送的信息长度只有 10,在信息显示上,也是不正确的,往往带有乱码字符。应该切实注意这个问题。

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 技术资料 > 其他杂项

本站为文档C TO C交易模式,本站只提供存储空间、用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。本站仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知得利文库网,我们立即给予删除!客服QQ:136780468 微信:18945177775 电话:18904686070

工信部备案号:黑ICP备15003705号-8 |  经营许可证:黑B2-20190332号 |   黑公网安备:91230400333293403D

© 2020-2023 www.deliwenku.com 得利文库. All Rights Reserved 黑龙江转换宝科技有限公司 

黑龙江省互联网违法和不良信息举报
举报电话:0468-3380021 邮箱:hgswwxb@163.com