Winsock网络编程快速入门
一、基本知识
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)。大多数系统都是小端存储;
用下面的方式可以检测是否为大端存储:
print?
bool IsBig_endian()
{
unsigned short test = 0x1122;
if ( *( (unsigned char*)&test ) == 0x11 )
{
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() 可以获得具体信息值,例如:
print?
if ( SOCKET_ERROR == WSACleanup() )
{
cout << "WSACleanup error " << WSAGetLastError() << endl;
return 0;
}
根据获取错误信息值,可以知道错误原因,并进行相应的处理。
3、寻址:
想要进行通信就需要知道彼此的地址,一般来说这个地址由IP和端口号来决定。在Winsock中使用SOCKADDR_IN结构来指定地址信息:
print?
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方法:
print?
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与地址结构进行关联;
print?
int
WSAAPI
bind(
IN SOCKET s, //一个用于监听的socket
IN const struct sockaddr FAR * name, //指向进行绑定的地址结构
IN int namelen //进行绑定的地址结构的大小
);
使用listen将bind成功的监听socket状态设置为监听状态;
print?
int
WSAAPI
listen(
IN SOCKET s, //一个用于监听的socket,已经进行bind
IN int backlog //允许挂起连接的队列的最大长度,超过这个长度后,再有连接将会失败
);
使用accept接受通过监听socket获取的连接,成功后将返回的新的连接socket进行保存以便
补充:综合编程 , 其他综合 ,