winsock编程技术简介Winsock简介-windows网络编程Winsock是一种标准API.主要用于网络通信.这些API函数有Winsock1Winsock2版本.通过前缀WSA区分.1.1Winsock头文件及库文件使用Winsock2规范时应包含winsock2.h连接的库为WS2_32.LIB使用Winsock1规范时.应包含winsock.h连接的库为WSOCK32.LIB另外mswsock.h用于微软专用编程扩展.连接的库为MSWSOCK.DLL1.2Winsock的初始化每个Winsock应用程序都必须加载合适的WinsockDLL版本.函数如下intWSAStartup{WORDwVersionRequested,LPWSADATA1pWSAData,//指向WSAData结构(见下边)的指针.本函数将填充这个结构体.WORDwVersionRequested,LPWSADATA1pWSAData);其中WSAData结构如下:typedefstructWSAData{WORDwVersion;//WSAAtartup函数会将这个字段设置为将要使用的Winsock版本.WORDwHighVersion;//现有Winsock库的最高版本.charszDescription[WSADESCRTIPTION_LEN+1];charszSystemStatus[WSASYS_STATUS_LEN+1];unsignedshortiMaxSockets;//客同时打开的最大套接字数.取决与可用的物理资源.unsignedshortiMaxUdpDg;//数据报最大长度charFAR*1pVendorInfo;//保留字段}WSADATA,*LPWSADATA;从windows98向后的平台都支持Winsock2.2版本.但WindowsCE支持的是Winsock1.1版本.如果WSAStartup()的第一个参数wVersionRequested指定的版本比当前平台上的最新版本还新.则函数就会失败.在使用Winsock接口编程完成后.要intWSACleanup(void);来释放所有由Winsock分配的资源.错误检查和处理对Winsock函数来说返回错误是很常见的.失败时最常见的返回值是SOCKET_ERROR(值为-1).检查到错误后.可以用intWSAGetLastError(void);函数返回int.这是在WINSOCK1.h或Winsock2.h中定义的常量值.来获得一段代码专门说明错误.与WSAGetLastError函数对应的有个WSASetLastError().它可以手动设置一个错误代码让WSAGetLastError去获取.例如以下的函数://初始化winsockif(WSAStartup(0x1010,&wsaData)!=0){printf(wsastartuperror\n);exit(1);}//因为这个函数调用时Winsock还没有加载.所以不能用WSAGetLastError()来确定错误.//当应用程序结束调用WSACleanup之后,设置Winsock通信代码if(WSACleanup()==SOCKETERROR){printf(WSACleanupfa工ledwitherror%d\n,WSAGetLastError());}协议寻址应用程序通过SOCKADDR_IN结构来指定IP地址和服务器端口信息:structsockaddr_in{shortsin_family;//设置为AF_INET表示Winsock正使用IP地址族u_shortsin_port;//通信端口structin_addrsin_addr;//这个字段用sin_addr(4字节)保存IP地址charsin_zero[8];//这个字段是填充项.使这个结构体大小和SOCKADDR结构的一样.};在unsignedlong和点分十进制字符串之间转换IP地址的表示可以用下边的函数:unsignedlonginet_addr(constcharFAR*cp);//cp为点分十进制字符串创建套接字套接字是SOCKET类型.创建套接字的函数是socket和WSASocket.SOCKETsocket(intaf,inttype,intprotocol);其中:af是协议的地址族.如IPv4对应的是AF_INET,type是协议的套接字类型.如果是TCP/IP则是SOCK_STREAM。如果是UDP/IP则是SOCK_DGRAM。protocol用于给定的地址和套接字类型具有多重入口时.对具体的传送做限定.对于TCP应将该字段设为IPPROTO_TCP对于UDP则设为IPPROTO_UDPSOCKETPASCALFARsocket(intaf,inttype,intprotocol);af:一个地址描述。目前仅支持AF_INET格式,也就是说ARPAInternet地址格式。type:新套接口的类型描述。protocol:套接口所用的协议。如调用者不想指定,可用0。将套接字和地址结构绑定bind(s,(SOCKADDR*)&tcpaddr,sizeof(tcpaddr));注意第2个参数的类型有转换.这个函数如果出错会返回SOCKET_ERROR.常见的错误是:WSAEADDRINUSE(表示本地这个端口和IP已被另一个进程绑定).WSAEFAULT(表示该套接字s已被绑定过了)。现在套接字已被绑定到本地一个端口上了.接着要让他进入监听模式.使用函数listen:intlisten(SOCKETs,intbacklog);其中:s是已被bind过的套接字.backlog则是指定被搁置的连接队列的最大长度.当连接的客户数大于这里的最大长长度并且并且服务进程没有来得及处理.则多出的连接请求会失败.这个函数相关的错误.常见的有WSAEINVAL(套接字在listen之前没有bind).现在可以让服务器接受连接.使用函数accept,:SOCKETaccept(SOCKETs,structsockaddrFAR*addr,intFAR*addrlen);其中s是处于监听模式的套接字.addr是SOCKADDR_IN对象的地址.addrlen是第2个参数的长度.这个函数取出被搁置的连接队列中的第一个连接请求.对它服务.函数执行后第2个参数和第3个参数指向的变量会被函数设置为客户IPv4地址信息和该信息的长度.并且.accept函数返回一个新的套接字.对该客户的所有后续操作(如数据收发等)都用这个新套接字来完成.而原来的套接字s则继续处于监听模式.如果这个函数出错.会返回INVALID_SOCKET.1.创建套接字.2.建立SOCKADDR地址结构.(指定要连接到的服务器的IP地址和端口号)3.用connect或WSAConnect初始化客户与服务器的连接.其中.前两步和服务器端建立连接的一样.第3步使用的函数如:intconnect(SOCKETs,conststructsockaddrFAR*name,intnamelen);其中:s就是将要和服务器建立连接的套接字.name是服务器的地址结构(SOCKADDR_IN).namelen是name的长度.如果函数失败.一般返回的错误有:WSAECONNREFUSED表示服务器没有监听这个端口WSAETIMEDOUT一般表示主机不可到达.TCP客户端的状态:每个套接字的初始状态都是CLOSED.客户机初始化一个连接.向服务器发出连接请求SYN包时(第1次握手).客户机套接字的状态为SYN_SENT.然后客户机等待一个从服务器端返回的SYN_ACK包应答(第2次握手).如果收不到这个SYN_ACK包.客户机会超时.回到CLOSED状态.如果收到.则客户机再发送一个ACK包给服务器作为响应(第3次握手).客户机套接字状态变为ESTABLISHED.TCP服务端的状态:一开始套接字的初始状态都是CLOSED.套接字和本地接口(IP)端口绑定之后(通过bind函数).再通过lesten函数进入侦听状态.这时服务器套接字状态为LISTEN.当服务器收到客户机的连接请求时.要发送一个SYN_ACK包回应客户机(第2次握手).这时状态为SYN_RCVD.然后客户机再返回ACK包(第3次握手).服务器收到后.状态变为ESTABLISHED.关闭套接字intclosesocket(SOCKETs);函数执行后.会释放套接字描述符.所以之后对s的调用会失败(产生WSAENOTSOCK)错误.如果没有对s的其他引用.那所有与s关联的资源都会被释放.包括丢弃所有队列中的数据.ping程序原理它主要的功能是用来检测网络的连通情况和分析网络速度。Ping程序的实现方法是:主机向远程计算机发出ICMP回应请求以后,远程计算机会处理这个请求,然后生成一条回应应答消息,再通过网络传回给发送主机;假如由于某些原因不能抵达目标主机,就会生成对应的ICMP错误消息由那个路径上某处的一个路由器返回.如果是与远程主机的连接没问题,只是远程主机已经关机,便需要由自己的程序来执行超时检查.#includestdio.h#includestdlib.h#includewindows.h#includeprocess.h#includewinsock.h#pragmacomment(lib,Ws2_32.lib);#defineSEND_SIZE32#definePACKET_SIZE4096#defineICMP_ECHO8#defineICMP_ECHOREPLY0structicmp{unsignedcharicmp_type;//消息类型unsignedcharicmp_code;//代码unsignedshorticmp_cksum;//校验和unsignedshorticmp_id;//用来惟一标识此请求的ID号,通常设置为进程IDunsignedshorticmp_seq;//序列号unsignedlongicmp_data;//时间戳};structip{unsignedcharip_hl:4;//首部长度unsignedcharip_v:4;//ip版本号unsignedcharip_tos;//服务类型unsignedshortip_len;//总长度unsignedshortip_id;//标识unsignedshortip_off;//标志位unsignedcharip_ttl;//生存时间unsignedcharip_p;//协议(TCP或其他)unsignedshortip_sum;//ip首部校验和unsignedlongip_src;//源ip地址unsignedlongip_dst;//目的ip地址};unsignedcharsendpacket[PACKET_SIZE];unsignedcharrecvpacket[PACKET_SIZE];structsockaddr_indest_addr;//指明目的主机信息structsockaddr_infrom_addr;//指明源主机信息intsockfd;intpid;//函数声明unsignedshortcal_chksum(unsignedshort*addr,intlen);intpack(intpack_no);intunpack(unsignedchar*buf,intlen);voidsend_packet(void);voidrecv_packet(void);voidmain(){intargc;char*argv[2];charstr[15];printf(请输入所要ping的主机ip地址或域名,按回车键结束:);scanf(%s,&str);//输入目的ipprintf(\n);argc=2;argv[0]=-t;//ping命令指定的计算机直到中断argv[1]=str;structh