一、基本知识

1、Winsock,一种标准API,一种网络编程接口,用于两个或多个应用程序(或进程)之间通过网络进行数据通信。具有两个版本:

Winsock 1:

Windows CE平台支持。

头文件:WinSock.h

库:wsock32.lib

Winsock 2:

部分平台如Windows CE貌似不支持。通过前缀WSA可以区别于Winsock 1版本。个别函数如WSAStartup、WSACleanup、WSARecvEx、WSAGetLastError都属于Winsock 1.1规范的函数;

头文件:WinSock2.h

库:ws2_32.lib

mswsock.h用于编程扩展,使用时必须链接mswsock.dll。

2、网络协议:

IP (Internet Protocol)  网际协议,无连接协议;

TCP (Transmission Control Protocol)  传输控制协议;

UDP (User Datagram Protocol)  用户数据协议;

FTP (File Transfer Protocol)  文件传输协议;

HTTP (Hypertext Transfer Protocol)  超文本传输协议;

3、字节存储顺序:

big_endian:大端存储,存储顺序从高位到低位,地址指向最高有效字节。在网络中将IP和端口指定为多字节时使用大端存储,也称为网络字节顺序(network_byte)。貌似MAC OS使用的是大端存储方式;

little_endian:小端存储,存储顺序从低位到高位,地址指向最低有效字节。本地主机存储IP和端口制定的多字节时使用,也称为主机字节顺序(host_byte)。大多数系统都是小端存储;

用下面的方式可以检测是否为大端存储:

bool IsBig_endian()

{

unsigned short test = 0x1122;

if ( *( (unsigned char*)&test ) == 0x11 ) //force unsigned short pointer to ussigned char pointer

{

return true;

}

else

{

return false;

}

}

此外有很多函数可以用来进行 主机字节和网络字节之间的转换,如:

u_long htonl( u_long hostlong );

int WSAHtonl( SOCKET s, u_long hostlong, u_long FAR *lpnetlong );

而有时网络IP是点分法表示的,如:192.168.0.1,使用函数unsigned long inet_addr( const char FAR *cp ); 可以将点分法的IP字符串作为一个网络字节顺序的32位u_long返回。

二、快速了解

1、Winsock初始化:

首先确保包含对应版本的头文件,然后保证链接对应的库文件(可以在代码中使用#pragma comment(lib, "WS2_32"),或在编译器项目属性中链接器->输入->附加依赖项中添加ws2_32.lib);

通过调用WSAStartup函数来实现加载Winsock库:

print?

int

WSAAPI

WSAStartup(

IN WORD wVersionRequested,

OUT LPWSADATA lpWSAData

);

其中参数wVersionRequested用来指定加载Winsock库的版本,高位字节为次版本,低位字节为主版本,使用宏MAKEWORD(x,y)来生成一个WORD;

参数lpWSAData是指向WASDATA结构指针,加载的版本库信息将会填充这个结构,详细内容自查。

在使用Winsock后需要释放资源,并取消应用程序挂起的Winsock操作。使用int WASCleanup();

2、错误处理:

如果已经加载了Winsock库,则调用Winsock函数出错后,通常会返回SOCKET_ERROR,而通过使用函数int WSAGetLastError() 可以获得具体信息值,例如:

if ( SOCKET_ERROR == WSACleanup() )

{

cout << "WSACleanup error " << WSAGetLastError() << endl;

return 0;

}

根据获取错误信息值,可以知道错误原因,并进行相应的处理。

3、寻址:

想要进行通信就需要知道彼此的地址,一般来说这个地址由IP和端口号来决定。在Winsock中使用SOCKADDR_IN结构来指定地址信息:

struct sockaddr_in {

short   sin_family;

u_short sin_port;

struct  in_addr sin_addr;

char    sin_zero[8];

};

sin_family字段通常设置为AF_INET,表示Winsock此时正在使用IP地址族;

sin_port用于标示TCP或UDP通信端口,部分端口是为一些服务保留的,如FTP和HTTP使用要注意;

sin_adr字段把地址(例如是IPv4地址)作为一个4字节的量来存储起来,它是u_long类型,且是网络字节顺序的。可以使用inet_addr来处理点分法表示的IP地址;

sin_zero只充当填充项,以使SOCKADDR_IN结构和SOCKADDR结构长度一样。

以下简单的使用SOCKADDR_IN来指定地址:

print?

//创建一个地址

int serverPort      = 5150;

char FAR serverIP[] = "192.168.1.102"; //本机ip,不知道就ipconfig

SOCKADDR_IN serverAddr;

serverAddr.sin_family   = AF_INET;

serverAddr.sin_port = htons( serverPort );

serverAddr.sin_addr.s_addr  = inet_addr( serverIP );

int serverAddr_size = static_cast<int>( sizeof(serverAddr) );

有时作为一个连接通信的服务端来说,在设置监听socket的地址结构时sin_addr.s_addr的值可以是htonl( INADDR_ANY ),INADDR_ANY允许将socket绑定到系统中所有可用的接口,以便传到任意接口的连接(端口必须正确)都可以被监听socket接受。

4、socket套接字:

套接字是通信时为传输提供的句柄,Winsock的操作都是基于套接字实现的。创建一个套接字有socket和WSASocket方法:

SOCKET

WSAAPI

socket(

IN int af,       //协议的地址族,使用IPv4来描述Winsock,设置为AF_INET

IN int type,     //套接字类型,TCP/IP设置为SOCK_STREAM,UDP/IP设置为SOCK_DGRAM

IN int protocol      //用于给定地址族和类型具有多重入口的传送限定,TCP设置为IPPROTO_TCP,UDP设置为IPPROTO_UDP

);

如果创建成功,函数会返回一个有效的SOCKET,否则会返回INVALID_SOCKET,可以用WSAGetLastError()函数获得错误信息。

5、连接通信实现过程:

连结通信是基于TCP/IP实现的,进行数据传输前,通信双方要进行连接。

服务端:

初始化Winsock后,创建一个监听socket和一个要接受连接的地址结构;

使用bind将监听socket与地址结构进行关联;

int

WSAAPI

bind(

IN SOCKET s,               //一个用于监听的socket

IN const struct sockaddr FAR * name,   //指向进行绑定的地址结构

IN int namelen             //进行绑定的地址结构的大小

);

使用listen将bind成功的监听socket状态设置为监听状态;

int

WSAAPI

listen(

IN SOCKET s,     //一个用于监听的socket,已经进行bind

IN int backlog       //允许挂起连接的队列的最大长度,超过这个长度后,再有连接将会失败

);

使用accept接受通过监听socket获取的连接,成功后将返回的新的连接socket进行保存以便数据传输;

print?

SOCKET

WSAAPI

accept(

IN SOCKET s,         //处于监听模式的socket

OUT struct sockaddr FAR * addr,  //指向一个地址结构,用来接受连接后获得对方地址信息

IN OUT int FAR * addrlen     //指向一个整数,表示参数2指向地址结构的大小

);

客户端:

初始化Winsock后,创建一个监听socket和一个要连接的服务器地址结构;

使用connect将socket和服务器地址结构进行初始化连接,成功后将使用socket进行数据传输;

print?

int

WSAAPI

connect(

IN SOCKET s,            //要建立连接的socket

IN const struct sockaddr FAR * name,    //指向保存要建立连接信息的地址结构

IN int namelen          //参数2指向地址结构的大小

);

连接成功后,使用send、recv来进行数据传输;

print?

int

WSAAPI

send(

IN SOCKET s,       //进行连接的socket

IN const char FAR * buf,   //指向发送数据的缓冲区

IN int len,        //发送数据的字符数

IN int flags       //一个标志位,可以是0、MSG_DONTROUTE、MSG_OOB还可以是他们的或运算结果

);         //返回已经发送的数据长度

int

WSAAPI

recv(

IN SOCKET s,       //进行连接的socket

OUT char FAR * buf,    //指向接受数据的缓冲区

IN int len,        //准备接受数据字节数或缓冲区的长度

IN int flags       //可以是0、MSG_PEEK、MSG_OOB还可以是他们的或运算结果

);         //返回已接受的数据长度

连接结束后,使用shutdown和closesocket来断开连接和释放资源;

print?

int

WSAAPI

shutdown(

IN SOCKET s,   //要关闭的socket

IN int how //关闭标志:SD_RECEIVE、SD_SEND、SD_BOTH

);

6、无连接通信实现过程:

无连接通信是基于UDP/IP实现的,UDP不能确保可靠的数据传输,但能将数据发送到多个目标,或者接受多个源的数据。

初始化Winsock后,可以创建socket和用以进行通信任意地址结构;

使用recvfrom通过socket和通信的地址结构接受数据;

使用sendto通过socket和通信的地址结构发送数据;

print?

int

WSAAPI

recvfrom(

IN SOCKET s,

OUT char FAR * buf,

IN int len,

IN int flags,

OUT struct sockaddr FAR * from,

IN OUT int FAR * fromlen

);

int

WSAAPI

sendto(

IN SOCKET s,

IN const char FAR * buf,

IN int len,

IN int flags,

IN const struct sockaddr FAR * to,

IN int tolen

);

同样通信结束后,使用shutdown和closesocket来断开连接和释放资源

上述使用函数都有多个版本,而且相关的一些标志位参数可以提供设置选项,另外,返回的错误处理等也有待于详细研究;

7、select函数:

select()用于确定一个或多个套接口的状态。对每一个套接口,调用者可查询它的可读性、可写性及错误状态信息。

print?

int

WSAAPI

select(

IN int nfds,         //指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,在Windows中值无所谓。

IN OUT fd_set FAR * readfds, //可选指针,指向一组等待可读性检查的套接字。

IN OUT fd_set FAR * writefds,    //可选指针,指向一组等待可写性检查的套接字。

IN OUT fd_set FAR *exceptfds,    //可选指针,指向一组等待错误检查的套接字。

IN const struct timeval FAR * timeout    //select()最多等待时间,对阻塞操作则为NULL。

);

//用fd_set结构来表示一组等待检查的套接口。在调用返回时,这个结构存有满足一定条件的套接口组的子集:

typedef struct fd_set {

u_int fd_count;               //其中set元素数目

SOCKET  fd_array[FD_SETSIZE]; //保存set元素的数组

} fd_set;

fd_set set;

FD_ZERO(&set);      /*将set清零使集合中不含任何fd*/   

FD_SET(fd, &set);       /*将fd加入set集合*/   

FD_CLR(fd, &set);       /*将fd从set集合中清除*/   

FD_ISSET(fd, &set);     /*测试fd是否在set集合中*/

select的返回值:

select()调用返回处于就绪状态并且已经包含在fd_set结构中的描述字总数;

如果超时则返回0;否则的话,返回SOCKET_ERROR错误,通过WSAGetLastError获取相应错误代码。

当返回位0时,所有描述符集清0;

当返回为-1时,不修改任何描述符集;

当返回为非0时,在3个描述符集里,依旧是1的位就是准备好的描述符。这也就是为什么,每次用select后都要用FD_ISSET的原因。

参考:http://www.2cto.com/kf/201111/111568.html

Winsock解析的更多相关文章

  1. VC.DNS解析(winsock)

    1.尝试了 gethostbyname(...) 和 Qt598中的qhostinfo::fromname(...),都可以解析出来IP,但是有一个问题:域名指向的IP改变了,这两种方式需要等较长时间 ...

  2. TCP协议疑难杂症全景解析

    说明: 1).本文以TCP的发展历程解析容易引起混淆,误会的方方面面2).本文不会贴大量的源码,大多数是以文字形式描述,我相信文字看起来是要比代码更轻松的3).针对对象:对TCP已经有了全面了解的人. ...

  3. 可以Ping通和DNS解析,但打不开网页的解决办法

    一. 网络故障表现为: 1.Ping地址正常,能ping通任何本来就可以ping通地址,如网关.域名. 2.能DNS解析域名. 3.无法打开网页,感觉是网页打开的一瞬间就显示无网络连接. 4.只需要连 ...

  4. 【转载】TCP协议疑难杂症全景解析

    说明: 1).本文以TCP的发展历程解析容易引起混淆,误会的方方面面2).本文不会贴大量的源码,大多数是以文字形式描述,我相信文字看起来是要比代码更轻松的3).针对对象:对TCP已经有了全面了解的人. ...

  5. Poco库网络模块例子解析1-------字典查询

    Poco的网络模块在Poco::Net名字空间下定义   下面是字典例子解析 #include "Poco/Net/StreamSocket.h" //流式套接字 #include ...

  6. Winsock编程基础介绍 .

    相信很多人都对网络编程感兴趣,下面我们就来介绍,在网络编程中应用最广泛的编程接口Winsock API. 使用Winsock API的编程,应该了解一些TCP/IP的基础知识.虽然你可以直接使用Win ...

  7. Winsock网络编程笔记(4)----基本的理论知识

    前面的笔记记录了Winsock的入门编程,领略了Winsock编程的乐趣..但这并不能算是掌握了Winsock,加深理论知识的理解才会让后续学习更加得心应手..因此,这篇笔记将记录一些有关Winsoc ...

  8. Delphi下的WinSock编程

    一.定址        要通过Winsock建立通信,必须了解如何利用指定的协议为工作站定址.Winsock 2引入了几个新的.与协议无关的函数,它们可和任何一个地址家族一起使用:但是大多数情况下,各 ...

  9. Winsock API编程介绍

    相信很多人都对网络编程感兴趣,下面我们就来介绍,在网络编程中应用最广泛的编程接口Winsock API. 使用Winsock API的编程,应该了解一些TCP/IP的基础知识.虽然你可以直接使用Win ...

随机推荐

  1. Java中类的设计技巧

    1)  一定要将数据设计为私有: 不要破坏封装性.有时需要编写一个访问器或更改器方法,但是最好还是保持实例域的私有性.数据的表示形式可能会改变,但他们的使用方式却不会经常发生变化.当数据保持私有时,他 ...

  2. sql 置顶功能的查询

    sql中有置顶的需求,文章很多条,分页查询,要求置顶的在最前面: 只需要使用: order by 置顶字段 即可

  3. ASP.NET 的ClientIDMode属性

    在ASP.NET 4.0之前我们总是要为控件的ClientID头疼,比如明明一个叫lblName的Label放在一个叫做grd的GridView里面后,在页面上改Label的ID就变成了诸如grd_c ...

  4. (面试题)html中创建模态窗口的方法有哪些?

    一.创建模态和非模态对话框除了alert(""); confirm(""); prompt("");之外还有创建模态对话框:vReturnV ...

  5. Android资源文件 - 使用资源存储字符串 颜色 尺寸 整型 布尔值 数组

    一. Android资源文件简介 1. Android应用资源的作用 (1) Android项目中文件分类 在Android工程中, 文件主要分为下面几类 : 界面布局文件, Java src源文件, ...

  6. nmap 端口扫描工具

    nmap工具介绍 一.简介 nmap :也就是Network Mapper,最早是Linux下的网络扫描和嗅探工具包. nmap是一个网络连接端扫描软件,用来扫描网上电脑开放的网络连接端.确定哪些服务 ...

  7. wpf 添加滚动条 ScrollViewer

    在WPF中有些控件没有滚动条,微软提供了控件ScrollViewer,这个控件是设置滚动条 <ScrollViewer Name="scrollViewer1" /> ...

  8. Eclipse中如何安装和使用GrepCode插件 (转)

    GrepCode(GC)Eclipse插件允许Eclipse用户在Eclipse IDE中搜索由GrepCode提供的工厂类.本教程介绍如何安装和使用插件.使用Eclipse3.5(Galileo)的 ...

  9. Rational Rose 2003 下载及破解方法

    FROM:http://blog.csdn.net/fenglibing/archive/2007/08/17/1747693.aspx 这么好的东西,不拿来出分享,我对不起原作者呀.但是我这里不知道 ...

  10. 【Unity】第8章 GUI开发

    分类:Unity.C#.VS2015 创建日期:2016-04-27 一.简介 前面的章节中实际上已经多次使用了GUI,只不过用法都比较简单,这一章系统地介绍Unity 5.x自带的GUI(称为Uni ...