/*
一、使用winsock的一般步骤
1.包含winsock头文件
#include<winsock2.h> 其头文件包含了windows.h
2.链接winsock导入库
2.1 配置属性-》链接器-》输入-》附加依赖项-》导入库名
2.2 预处理命令
#pragma comment(lib,"ws2_32.lib")
3. 加载winsock动态链接库(调用winsock函数的前提)
需要使用wsastartup()函数,
返回值为一整数,0:成功, 失败时:
#define WSAEFAULT 10014L
// MessageId: WSAEINVAL
// MessageText:
// An invalid argument was supplied.
#define WSAEINVAL 10022L
// MessageId: WSAEMFILE
// MessageText:
// Too many open sockets.
#define WSAEINPROGRESS 10036L
// MessageId: WSAEALREADY
// MessageText:
// An operation was attempted on a non-blocking socket that already had an operation in progress.
#define WSAEPROCLIM 10067L
// MessageId: WSAEUSERS
// MessageText:
// Ran out of quota.
#define WSASYSNOTREADY 10091L
// MessageId: WSAVERNOTSUPPORTED
// MessageText:
// The Windows Sockets version requested is not supported.
#define WSAVERNOTSUPPORTED 10092L
// MessageId: WSANOTINITIALISED
// MessageText:
// Either the application has not called WSAStartup, or WSAStartup failed.
WSAStartup(_In_ WORD wVersionRequested,_Out_ LPWSADATA lpWSAData);
wVersionRequested:双字节类型,指明winsock库的版本号,高位字节:副版本 低位字节:主版本
常使用宏 MAKEWORD(a, b) 加载版本,a:副版本 b:主版本
lpWSAData:返回winsock版本的详细信息,为一LPWSADATA结构体指针
typedef struct WSAData {
WORD wVersion; //winsock版本号
WORD wHighVersion; //winsock库支持最高的winsock版本号
#ifdef _WIN64
unsigned short iMaxSockets;
unsigned short iMaxUdpDg;
char FAR * lpVendorInfo;
char szDescription[WSADESCRIPTION_LEN+1];
char szSystemStatus[WSASYS_STATUS_LEN+1];
#else
char szDescription[WSADESCRIPTION_LEN+1]; //加载winsock库说明
char szSystemStatus[WSASYS_STATUS_LEN+1]; //状态和配置信息
unsigned short iMaxSockets; //同时打开的winsock的最大数
unsigned short iMaxUdpDg; //可发送udp数据报的最大字节数
char FAR * lpVendorInfo; //厂商信息
#endif
} WSADATA, FAR * LPWSADATA;
4.注销winsock动态链接库
使用函数wsacleanup(void);
函数无参,成功返回0, 失败时返回socket_error 在winsock2.h中定义,对应值为-1
二、网络字节顺序(涉及储存时的大端顺序,小端顺序)
本地:主机字节序
网络收发:网络字节序
u_short htons(_In_ u_short hostshort);->主机字节序转为网络字节序
hostshort:待转换主机字节序无符号短整型
返回值:网络字节序的无符号短整型,调用失败返回sockrt_error,错误信息由wsagetlasterror()函数获取。
u_short ntohs(_In_ u_short netshort);->网络字节序转为主机字节序
netshort:待转换网络字节序数据
返回值:主机字节序数据,调用失败返回sockrt_error,错误信息由wsagetlasterror()函数获取。
u_long htonl(_In_ u_long hostlong);//上面两个是16位数据,此处为32位数据
u_long ntohl(_In_ u_long netlong);
三、网络地址表示
1.地址结构
struct in_addr
{
union {
struct { UCHAR s_b1,s_b2,s_b3,s_b4; } S_un_b; //常用
struct { USHORT s_w1,s_w2; } S_un_w;
ULONG S_addr;
} S_un;
#define s_addr S_un.S_addr // can be used for most tcp & ip code
#define s_host S_un.S_un_b.s_b2 // host on imp
#define s_net S_un.S_un_b.s_b1 // network
#define s_imp S_un.S_un_w.s_w2 // imp
#define s_impno S_un.S_un_b.s_b4 // imp #
#define s_lh S_un.S_un_b.s_b3 // logical host
} IN_ADDR, *PIN_ADDR, FAR *LPIN_ADDR;
struct sockaddr_in
{
short sin_family; //地址族,ip协议对应为af_inet
USHORT sin_port;//16位端口号,网络字节序
IN_ADDR sin_addr; //32位ip地址,网络字节序
CHAR sin_zero[8]; //保留位
} SOCKADDR_IN, *PSOCKADDR_IN;
struct sockaddr
{
u_short sa_family; //协议地址族
CHAR sa_data[14]; // Up to 14 bytes of direct address.
} SOCKADDR, *PSOCKADDR, FAR *LPSOCKADDR;
2.地址转换函数
unsigned long inet_addr(_In_z_ const char FAR * cp);->点分十进制ip地址转换为32位无符号整数。
char FAR * inet_ntoa(_In_ struct in_addr in);->结构变量中的长整型ip地址转换为点分十进制形式。
INT inet_pton(
_In_ INT Family,
_In_ PCSTR pszAddrString,
_When_(Family == AF_INET, _Out_writes_bytes_(sizeof(IN_ADDR)))
_When_(Family == AF_INET6, _Out_writes_bytes_(sizeof(IN6_ADDR)))
PVOID pAddrBuf
);->点分十进制转换为32位网络字节序无符号长整型
PCSTR inet_ntop(
_In_ INT Family,
_In_ const VOID * pAddr,
_Out_writes_(StringBufSize) PSTR pStringBuf,
_In_ size_t StringBufSize
);
四、网络配置信息
1.主机名与ip地址
int gethostname(_Out_writes_bytes_(namelen) char FAR * name,_In_ int namelen);->查询本地计算机名字
name:缓冲区,存放主机名字。
namelen:缓冲区大小
struct hostent FAR *gethostbyname(_In_z_ const char FAR * name);-》根据主机名/域名查询ip地址,不支持ipv6
name:所要查询的计算机名字/域名
struct hostent
{
char FAR * h_name; /* official name of host
char FAR * FAR * h_aliases; /* alias list
short h_addrtype; /* host address type
short h_length; /* length of address
char FAR * FAR * h_addr_list; /* list of addresses
#define h_addr h_addr_list[0] /* address, for backward compat
};
INT getaddrinfo(
_In_opt_ PCSTR pNodeName, //指向主机名的指针
_In_opt_ PCSTR pServiceName, //指定查询端口号的服务名称或字符串形式的端口号
_In_opt_ const ADDRINFOA * pHints,
_Outptr_ PADDRINFOA * ppResult
);-》根据主机名/域名查询ip地址,支持ipv6,#include"ws2tcpip.h"
struct hostent FAR * gethostbyaddr(
_In_reads_bytes_(len) const char FAR * addr, //指向网络字节序ip地址的指针
_In_ int len, //地址的长度
_In_ int type //ip地址对应的类型
);-》根据ip查询相关信息
2.服务器查询
struct servent FAR *getservbyname(
_In_z_ const char FAR * name, //指向服务名的指针
_In_opt_z_ const char FAR * proto //指向协议名的指针(可选)
);->根据给定的服务名和所使用的协议名获取服务的端口号信息
struct servent
{
char FAR * s_name; /* official service name 服务名
char FAR * FAR * s_aliases; /* alias list 服务别名队列
short s_port; /* port # 连接该服务的端口号,以网络字节序排序
char FAR * s_proto; /* protocol to use 使用该服务的所用的传输协议名
};
struct servent FAR * getservbyport(
_In_ int port, //给定的端口号,以网络字节序排序
_In_opt_z_ const char FAR * proto //指向传输协议名的指针,可以为空。
);-》根据给定的协议和端口号查询服务名称等信息
*/
//例:查询本机名称及ip(c++ vs2017)
#include<iostream>
#include"WinSock2.h"//包含winsock库头文件
#include"ws2tcpip.h"
#pragma comment(lib,"ws2_32.lib")//链接winsock导入库
using namespace std;
int main()
{
//加载winsock DLL
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
cout << "加载winsock DLL失败..." << endl;
return 0;
}
char hostname[256];
if (gethostname(hostname, sizeof(hostname)))
{
cout << "获取主机名字失败..." << endl;
WSACleanup();
return 0;
}
cout << "本机名字为:" << hostname << endl;
//根据主机名字查询主机ipv4地址
struct addrinfo hints, *p_addrinfo, *p;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;//ipv4地址
unsigned int retval = getaddrinfo(hostname, NULL, &hints, &p_addrinfo);
if (retval != 0)
{
cout << "get addrinfo failed with error: " << int(retval) << endl;
WSACleanup();
return 1;
}
//输出ip地址
p = p_addrinfo;
cout << "本机ip地址为: ";
char ipaddr[20];
in_addr addr;
while (p != NULL)
{
addr = ((sockaddr_in*)(p->ai_addr))->sin_addr;
cout << inet_ntop(AF_INET, (void*)&addr, ipaddr, 20) << endl;
p = p->ai_next;
}
WSACleanup();
return 0;
}