WinSock网络编程笔记: 获得本地IP地址

每次写网络程序都必须编写代码载入和释放winsock库,为了以后方便使用,我们将封装一个CInitSock类来管理Winsock库:

// initsock.h文件

#include <winsock2.h>
#pragma comment(lib, "WS2_32")	// 链接到WS2_32.lib

class CInitSock		
{
public:
	CInitSock(BYTE minorVer = 2, BYTE majorVer = 2)
	{
		// 初始化WS2_32.dll
		WSADATA wsaData;
		WORD sockVersion = MAKEWORD(minorVer, majorVer);
		if(::WSAStartup(sockVersion, &wsaData) != 0)
		{
			exit(0);
		}
	}
	~CInitSock()
	{	
		::WSACleanup();	
	}
};

WSADATA   这个结构被用来存储 被WSAStartup函数调用后返回的 Windows Sockets 数据。它包含Winsock.dll 执行的数据。

WSAStartup    为了在应用程序当 中调用任何一个Winsock API函数,首先第一件事情就是必须通过WSAStartup函数完成对Winsock服务的初始化,因此需要调用WSAStartup函数。使用 Socket的程序在使用Socket之前必须调用WSAStartup函数。该函数的第一个参数指明程序请求使用的Socket版本,其中高位字节指明 副版本、低位字节指明主版本;操作系统利用第二个参数返回请求的Socket的版本信息。当一个应用程序调用WSAStartup函数时,操作系统根据请求的Socket版本来搜索相应的 Socket库,然后绑定找到的Socket库到该应用程序中。以后应用程序就可以调用所请求的Socket库中的其它Socket函数了。

WSACleanup   应用程序或DLL在使用Windows Sockets服务之前必须要进行一次成功的WSAStartup()调 用.当它完成了Windows Sockets的使用后,应用程序或DLL必须调用WSACleanup()将其从Windows Sockets的实现中注销,并且该实现释放为应用程序或DLL分配的任何资源.任何打开的并已建立连接的SOCK_STREAM类型套接口在调用 WSACleanup()时会重置; 而已经由closesocket()关闭却仍有要发送的悬而未决数据的套接口则不会受影响- 该数据仍要发送.   对应于一个任务进行的每一次WSAStartup()调用,必须有一个 WSACleanup()调用.只有最后的WSACleanup()做实际的清除工作;前面的调用仅仅将Windows Sockets DLL中的内置引用计数递减.一个简单的应用程序为确保WSACleanup()调用了足够的次数,可以在一个循环中不断调用WSACleanup()直 至返回WSANOTINITIALISED.


#include"../common/initsock.h" //添加该头文件
#include<iostream>
#include<cstdio>

using namespace std;

CInitSock initsock; //初始化

int main()
{
	in_addr addr;
	char HostName[256];                    //存放主机名
	int k = ::gethostname(HostName, 256);  //返回本地主机的标准主机名
	if (k)                                
	{
		k = GetLastError();
	}
	cout << "当前主机名为: " << HostName << endl;

	hostent *pHost = ::gethostbyname(HostName);//是host entry的缩写,该结构记录主机的信息,包括主机名、别名、地址类型、地址长度和地址列表。之所以主机的地址是一个列表的形式,原因是当一个主机有多个网络接口时,自然有多个地址。
	for (int i = 0;; i++)
	{
		char *p = pHost->h_addr_list[i];
		if (p == NULL)
		{
			break;
		}
		memcpy(&addr.S_un.S_addr, p, pHost->h_length);//由p指向地址为起始地址的连续pHost->h_length个字节的数据复制到以addr.S_un.S_addr指向地址为起始地址的空间内。
		
		char *IP = ::inet_ntoa(addr);                 //将网络地址转换成“.”点隔的字符串格式
		cout << "本机IP地址为: " << IP << endl;
	}
	
}

部分函数介绍:

gethostname()   

返回本地主机的标准主机名。
int PASCAL FAR gethostname(char FAR *name, int namelen);   
name: 一个指向将要存放主机名的缓冲区指针。   
namelen:缓冲区的长度。
hostent      是host entry的缩写,该结构记录主机的信息,包括主机名、别名、地址类型、地址长度和地址列表。之所以主机的地址是一个列表的形式,原因是当一个主机有多个网络接口时,自然有多个地址。

#define h_addr h_addr_list[0]    h_addr_list中的第一地址。

struct hostent {   
     char *h_name;       //地址的正式名称 
     char **h_aliases;   //空字节-地址的预备名称的指针。
     int h_addrtype;   //地址类型; 通常是AF_INET。
     int h_length;       //地址的比特长度。
     char **h_addr_list;     //零字节-主机网络地址指针。网络字节顺序。
};
memcpy
extern void *memcpy(void *destin, void *source, unsigned n);
由source指向地址为起始地址的连续n个字节的数据复制到以destin指向地址为起始地址的空间内。
inet_ntoa()    功能是将网络地址转换成“.”点隔的字符串格式。

函数原型: char FAR* PASCAL FAR inet_ntoa( struct in_addr in);
MSDN上本函数的原型描述为:unsigned long inet_addr( __in const char *cp);
in:一个表示Internet主机地址的结构。

注释:
本函数将一个用in参数所表示的Internet地址结构转换成以“.” 间隔的诸如“a.b.c.d”的字符串形式。请注意inet_ntoa()返回的字符串存放在WINDOWS套接口实现所分配的内存中。应用程序不应假设该内存是如何分配的。在同一个线程的下一个WINDOWS套接口调用前,数据将保证是有效。

返回值:
若无错误发生,inet_ntoa()返回一个字符指针。否则的话,返回NULL。其中的数据应在下一个WINDOWS套接口调用前复制出来。
struct   in_addr的定义如下:

struct   in_addr{
      union {       
                 struct{  
                        unsigned  char   s_b1,s_b2,s_b3, s_b4; 
                  }  S_un_b;   
  
                 struct  {   
                           unsigned  short  s_w1,s_w2;       
                 }  S_un_w;    

                  unsigned long  S_addr;  
       } S_un; 
}; 

 参考博客:https://blog.csdn.net/loverooney/article/details/23298643

原文地址:https://www.cnblogs.com/Romantic-Chopin/p/12451014.html