return0;
}
根据获取错误信息值,可以知道错误原因,并进行相应的处理。
3、寻址:
想要进行通信就需要知道彼此的地址,一般来说这个地址由IP和端口号来决定。
在Winsock中使用SOCKADDR_IN结构来指定地址信息:
structsockaddr_in{
shortsin_family;
u_shortsin_port;
structin_addrsin_addr;
charsin_zero[8];
};
sin_family通常大多用的是都是AF_INET,代表TCP/IP协议族。
sin_port用于标示TCP或UDP通信端口,部分端口是为一些服务保留的,如FTP和HTTP使用要注意;必须要采用网络数据格式,普通数字可以用htons()函数转换成网络数据格式的数字
sin_adr字段把地址(例如是IPv4地址)作为一个4字节的量来存储起来,它是u_long类型,且是网络字节顺序的。
可以使用inet_addr来处理点分法表示的IP地址,必要时可以用htonl()函数转换成网络数据格式的数字;
sin_zero只充当填充项,以使SOCKADDR_IN结构和SOCKADDR结构长度一样。
没有实际意义,只是为了跟SOCKADDR结构在内存中对齐
以下简单的使用SOCKADDR_IN来指定地址:
//创建一个地址
intserverPort=5150;
charFARserverIP[]="192.168.1.102";//本机ip,不知道就ipconfig
SOCKADDR_INserverAddr;
serverAddr.sin_family=AF_INET;
serverAddr.sin_port=htons(serverPort);//convertsau_shortfromhosttoTCP/IPnetwork
byteorder(whichisbig-endian).
serverAddr.sin_addr.s_addr=inet_addr(serverIP);
intserverAddr_size=static_cast(sizeof(serverAddr));
IN_ADDR结构:
TheIPaddresscomponentofthisstructureisoftypeIN_ADDR.TheIN_ADDRstructureisdefinedinWindowsSocketsheaderfileWINSOCK.Hasfollows:
struct in_addr{
union {
struct{
unsigned char s_b1,//AnIPv4addressformattedasfouru_chars
s_b2,
s_b3,
s_b4;
} S_un_b;
struct {
unsigned short s_w1,//AnIPv4addressformattedastwou_shorts.
s_w2;
} S_un_w;
unsignedlong S_addr;//AnIPv4addressformattedasau_long.
}S_un;
};
以下例子来自于MSDN:
Thefollowingexampledemonstratestheuseofthesockaddrstructure.
//Declarevariables
SOCKETListenSocket;
Structsockaddr_insaServer;
hostent*localHost;
char*localIP;
//Createalisteningsocket,//创建socket
ListenSocket=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
//Getthelocalhostinformation
localHost=gethostbyname("");//该函数可以从主机名数据库中得到对应的“主机”。
返回一个指向hostent结构的指针,它可以标识一个“主机”列表
localIP=inet_ntoa(*(structin_addr*)*localHost->h_addr_list);//convertsan(Ipv4)InternetnetworkaddressintoastringinInternetstandarddottedformat.
//Setupthesockaddrstructure
saServer.sin_family=AF_INET;
saServer.sin_addr.s_addr=inet_addr(localIP);//convertsastringcontainingan(Ipv4)InternetProtocoldottedaddressintoaproperaddress(unsignedlong)fortheIN_ADDRstructure
saServer.sin_port=htons(5150);//convertsau_shortfromhosttoTCP/IPnetworkbyteorder(whichisbig-endian).
//Bindthelisteningsocketusingthe
//informationinthesockaddrstructure
bind(ListenSocket,(SOCKADDR*)&saServer,sizeof(saServer));
有时作为一个连接通信的服务端来说,在设置监听socket的地址结构时sin_addr.s_addr的值可以是htonl(INADDR_ANY),INADDR_ANY允许将socket绑定到系统中所有可用的接口,以便传到任意接口的连接(端口必须正确)都可以被监听socket接受。
4、创建socket套接字:
套接字是通信时为传输提供的句柄,Winsock的操作都是基于套接字实现的。
创建一个套接字有socket和WSASocket方法:
SOCKETWSAAPIsocket(
INintaf,//协议的地址族,使用IPv4来描述Winsock,设置为AF_INET
INinttype,//套接字类型,TCP/IP设置为SOCK_STREAM,UDP/IP设置为SOCK_DGRAM
INintprotocol//用于给定地址族和类型具有多重入口的传送限定,TCP设置为IPPROTO_TCP,UDP设置为IPPROTO_UDP
);
如果创建成功,函数会返回一个有效的SOCKET,否则会返回INVALID_SOCKET,可以用WSAGetLastError()函数获得错误信息。
5、连接通信实现过程:
连结通信是基于TCP/IP实现的,进行数据传输前,通信双方要进行连接。
服务端:
初始化Winsock后,创建一个监听socket和一个要接受连接的地址结构;
使用bind将监听socket与地址结构进行关联;
intWSAAPIbind(INSOCKETs,//一个用于监听的socket
INconststructsockaddrFAR*name,//指向进行绑定的sockaddr地址结构
INintnamelen//进行绑定的sockaddr地址结构的大小
);
使用listen将bind成功的监听socket状态设置为监听状态;
intWSAAPIlisten(
INSOCKETs,//一个用于监听的socket,已经进行bind
INintbacklog//允许挂起连接的队列的最大长度,超过这个长度后,再有连接将会失败
);
使用accept接受通过监听socket获取的连接,成功后将返回的新的连接socket进行保存以便数据传输;
SOCKETWSAAPIaccept(
INSOCKETs,//处于监听模式的socket
OUTstructsockaddrFAR*addr,//指向一个地址结构,用来接收连接后获得对方地址信息
INOUTintFAR*addrlen//指向一个整数,表示参数2指向地址结构的大小
);
客户端:
初始化Winsock后,创建一个监听socket和一个要连接的服务器地址结构;
使用connect将socket和服务器地址结构进行初始化连接,成功后将使用socket进行数据传输;
intWSAAPIconnect(
INSOCKETs,//要建立连接的socket
INconststructsockaddrFAR*name,//指向保存要建立连接信息的地址结构
INintnamelen//参数2指向地址结构的大小
);
连接成功后,使用send、recv来进行数据传输;返回已经发送的数据长度
intWSAAPIsend(INSOCKETs,//进行连接的socket
INconstcharFAR*buf,//指向发送数据的缓冲区
INintlen,//发送数据的字符数
INintflags//一个标志位,可以是0、MSG_DONTROUTE、MSG_OOB还可以是他们的或运算结果
);
intWSAAPIrecv(
INSOCKETs,//进行连接的socket
OUTcharFAR*buf,//指向接受数据的缓冲区
INintlen,//准备接受数据字节数或缓冲区的长度
INintflags//可以是0、MSG_PEEK、MSG_OOB还可以是他们的或运算结果
);
连接结束后,使用shutdown和closesocket来断开连接和释放资源;
intWSAAPIshutdown(
INSOCKETs,//要关闭的socket
INinthow//关闭标志:
SD_RECEIVE、SD_SEND、SD_BOTH
);
6、无连接通信实现过程:
无连接通信是基于UDP/IP实现的,UDP不能确保可靠的数据传输,但能将数据发送到多个目标,或者接受多个源的数据。
初始化Winsock后,可以创建socket和用以进行通信任意地址结构;注意创建socket时type参数必须是SOCK_STREAM,protocol参数必须是IPPROTO_UDP。
//sendto和recvfrom一般用于UDP协议中,但是如果在TCP中connect函数调用后也可以用.
使用recvfrom通过socket和通信的地址结构接受数据;
使用sendto通过socket和通信的地址结构发送数据;
intWSAAPIrecvfrom(INSOCKETs,
OUTcharFAR*buf,
INintlen,
INintflags,
OUTstructsockaddrFAR*from,
INOUTintFAR*fromlen
);
intWSAAPIsendto(INSOCKETs,
INconstcharFAR*buf,
INintlen,
INintflags,
INconststructsockaddrFAR*to,
INinttolen
);
同样通信结束后,使用shutdown和closesocket来断开连接和释放资源
上述使用函数都有多个版本,而且相关的一些标志位参数可以提供设置选项,另外,返回的错误处理等也有待于详细研究;
7、select函数:
select()用于确定一个或多个套接口的状态。
对每一个套接口,调用者可查询它的可读性、可写性及错误状态信息。
intWSAAPIselect(
INintnfds,//指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,在Windows中值无所谓。
INOUTfd_setFAR*readfds,//可选指针,指向一组等待可读性检查的套接字。
INOUTfd_setFAR*writefds,//可选指针,指向一组等待可写性检查的套接字。
INOUTfd_setFAR*exceptfds,//可选指针,指向一组等待错误检查的套接字。
INconststructtimevalFAR*timeout//select()最多等待时间,对阻塞操作则为NULL。
);
//用fd_set结构来表示一组等待检查的套接口。
在调用返回时,这个结构存有满足一定条件的套接口组的子集:
typedefstructfd_set{
u_intfd_count;//set元素数目
SOCKETfd_array[FD_SETSIZE];//保存