网络编程(一)

 

网络编程(一)

分类: VC++ 重温笔记   43人阅读  评论(1)  收藏  举报

在进行网络编程之前,先把网络编程相关的重要的知识点梳理一下,但其中最最基本的网络基础知识如TCP/IP之类的知识点,还请读者自行去查阅相关的书籍

1.网络网络字节序

        世界上有很多不同的种类不同的计算机,不同种类的计算在存放多字节的时候的存放顺序不同。如Intel 的 Cpu的计算机,这些地址按照“低位先存”,即这些地址字节,低位在左高字节在右。这种存放方顺序称为“little endian”顺序。而有些AMD的Unix的机器,他们的存放方式完全与intel的相反,采用网络字节序。他们按照“高位先存”,即高字节在左,低字节在右的顺序。

实际在网络编程中,大多都是用网络字节序(如TCP/IP就是采用的网络字节序 )。所以我们平时在编程时注意将 “little endian”顺序转换为网络字节序。

2.套接字的类型

    套接字主要分为3类,简单说下。

    1)流式套接字(SOCK_STREAM)----用于TCP

    2)数据报套接字(SOCK_DGRAM) -----用于UDP

    3)原始套接字(SOCK_RAW) ------原始套接字只能读取内核没有处理过的IP数据包,平时我基本没有用到.

     (注意:如果你要访问其他协议的话,就必须要用原始套接字.)

3.套接字的初始化.

     在套接字使用前必须,先进性初始化,在请求到适合自己或需求的版本后,我们方能进行相关方面的操作.

    首先我们来看一下一个结构体,这个结构体保存的Socket相关的信息

  1. typedef struct WSAData {    
  2.            WORD wVersion;  //表示想获取的Socket的版本号码.低字节表示主版本号,高字节表示副版本号码  
  3.            WORD wHighVersion;  //windows支持的最高的sockets的版本号码  
  4.            char szDescription[WSADESCRIPTION_LEN+1];//空终止,保存了socket的实现描述(基本不用)  
  5.            char szSystemStatus[WSASYS_STATUS_LEN+1];//空终止,保存的系统的状态或者配置信息(基本不用)  
  6.            unsigned short iMaxSockets;    //Socket最大可以打开的数目,在WinSock2 以后的版本忽略掉  
  7.            unsigned short iMaxUdpDg;      //Socket数据报最大的长度,在winSock2以后的版本忽略掉  
  8.            char FAR* lpVendorInfo;   //为某些厂商预留的字段,在win32下不需要使用  
  9.           } WSADATA,  *LPWSADATA;  

Winsock 初始化函数

  1. int WSAStartup(  
  2.   __in          WORD wVersionRequested,//我们需要的版本  
  3.   __out         LPWSADATA lpWSAData   //返回初始化的信息  
  4. );  

其中版本号是一个WORD类型,我们可以通过一个宏WORD MAKEWORD(BYTE bLow, BYTE bHigh); 来生成一个字bLow低字节,bHigh高字节

在用完socket的时候,需要调用int WSACleanup(void); 终止Socket。

 

接着再来看一个结构体hostent ,该结构体是用来储存我们指定的主机的相关信息的。(如:主机名,IP地址等等)

  1. typedef struct hostent {    
  2.         char FAR* h_name; //主机的名称   
  3.         char FAR  FAR** h_aliases; //地址预备名称   
  4.         short h_addrtype; //地址类型 通常我们设为AF_INET(PF_INET)windows只支持这一区域的   
  5.         short h_length;//每个地址的字节长度   
  6.         char FAR  FAR** h_addr_list;//地址列表指针,在老版本中你可能会看到h_addr,两者是一样的  
  7.      } HOSTENT,  *PHOSTENT,  FAR *LPHOSTENT;  

说明:如果你像我一样是一个新时代程序袁,一出生就是32位横行的时代,那么你可能会对FAR感到好奇。其实FAR是16位时代的产物,在32位下已经没有了类似于

near , far 之类的指针,都是一样的。就把他忽略掉就可以了。

struct hostent* gethostbyname()  通过主机名得到 主机相关的信息。

我们通过gethostbyname得到的HOSTENT,应用程序不应该试图修改或释放我们得到的任何部分,这样极有可能导致出错。

 下面我们实现一个获取自己主机名和IP的小程序,代码如下:

  1. void CGetNetInfoDlg::OnBnClickedOk()  
  2. {  
  3.     //初始化Socket  
  4.     WORD wdVersion = MAKEWORD(2,2);   //版本号  
  5.     WSADATA wsData;  
  6.     memset(&wsData, 0, sizeof(WSADATA));  //存放请求socket的数据  
  7.   
  8.     if(::WSAStartup(wdVersion, &wsData)!= 0)  
  9.     {  
  10.         MessageBox(_T("初始化Socket出错!"),_T("Error"),MB_OK||MB_ICONERROR);  
  11.         return ;  
  12.     }  
  13.   
  14.     //查看版本号是否和我们的请求的一致  
  15.     if(LOBYTE(wsData.wVersion)!=2||HIBYTE(wsData.wVersion)!=2)  
  16.     {  
  17.         MessageBox(_T("请求的版本不正确!"),_T("版本出错"),MB_OK||MB_ICONERROR);  
  18.         WSACleanup(); //关闭Socket  
  19.     }  
  20.     //获得主机名  
  21.     char hostName[MAX_PATH];  
  22.     memset(hostName, 0 , MAX_PATH*sizeof(char));  
  23.     if(::gethostname(hostName,MAX_PATH)==SOCKET_ERROR)  
  24.     {  
  25.         CString str;  
  26.         str.Format(_T("错误号:%d"), WSAGetLastError());  
  27.         MessageBox(str, _T("获取主机名错误"), MB_OK||MB_ICONERROR);  
  28.         return;  
  29.     }  
  30.   
  31.     CString strHostName(hostName);  
  32.   
  33.     //获取主机的IP  
  34.     LPHOSTENT lpHost;  
  35.     lpHost = ::gethostbyname(hostName);  
  36.     CString strIpAddr;  
  37.     struct in_addr ip_addr;//描述IPV4的结构体  
  38.     if(lpHost!=NULL) //获取成功  
  39.     {  
  40.         memmove(&ip_addr, lpHost->h_addr_list[0],4);  
  41.         strIpAddr = CString(inet_ntoa(ip_addr));  
  42.     }  
  43.     CEdit* pEdit = NULL;  
  44.   
  45.     pEdit = (CEdit*)GetDlgItem(IDC_ET_HOSTNAME);  
  46.     pEdit->SetWindowTextW(strHostName);  
  47.   
  48.     CIPAddressCtrl* pIPCtrl;  
  49.     pIPCtrl = (CIPAddressCtrl*)GetDlgItem(IDC_IP_ADDR1);  
  50.   
  51.     DWORD dwIp = ntohl(inet_addr(inet_ntoa(ip_addr)));  
  52.     pIPCtrl->SetAddress(dwIp);  
  53.     WSACleanup();  
  54.     //CDialogEx::OnOK();  
  55. }  


 

好了第一个超级简单的小程序,就这样搞定了。

下面我们再写一个稍微复杂点的程序,获取局域网内的所有计算机的IP。

我们 将采用枚举的方法利用下面三个函数来完成

//启动网络资源的枚举

  1. DWORD WNetOpenEnum(  
  2.   __in          DWORD dwScope,  
  3.   __in          DWORD dwType,  
  4.   __in          DWORD dwUsage,  
  5.   __in          LPNETRESOURCE lpNetResource,  
  6.   __out         LPHANDLE lphEnum  
  7. );  
  8.   
  9.   
  10. //2.进行枚举  
  1. DWORD WNetEnumResource(  
  2.   __in          HANDLE hEnum,  
  3.   __in_out      LPDWORD lpcCount,  
  4.   __out         LPVOID lpBuffer,  
  5.   __in_out      LPDWORD lpBufferSize  
  6. );  
  7.   
  8. //3.结束枚举  
  1. DWORD WNetCloseEnum(__in    ANDLE hEnum);  
  2.   
  3. code:  

 在void CGetNetInfoDlg::OnBnClickedOk()的末尾添加如下代码

  1.           CStringArray iparray;  
  2. CStringArray namearray;  
  3. FindAllComputer(iparray, namearray);  
  4.   
  5. CString ip = _T("");  
  6. CString name = _T("");  
  7.   
  8. forint i = 0; i < iparray.GetSize(); i++ )  
  9. {  
  10.     ip = iparray.GetAt(i);  
  11.     name = namearray.GetAt(i);  
  12.     int m_nCurrentSel = m_ListCtrl.InsertItem(0xffff,_T(""));  
  13.     m_ListCtrl.SetItem( m_nCurrentSel, 0, LVIF_TEXT, ip, NULL, 0, 0, 0);  
  14.     m_ListCtrl.SetItem( m_nCurrentSel, 1, LVIF_TEXT, name, NULL, 0, 0, 0);  
  15. }  
  16.   
  17. int error = ::GetLastError();  

在OnInitDailog中初始化List控件

  1.     m_ListCtrl.InsertColumn(0, _T("局域网内计算机IP"), LVCFMT_LEFT, 200);  
  2.     m_ListCtrl.InsertColumn(1, _T("局域网内计算机名称"), LVCFMT_LEFT, 200);  
  3.    

添加如下两个函数

  1. void CGetNetInfoDlg::FindAllComputer(CStringArray& MyList, CStringArray& MyListName)  
  2. {     
  3.     MyList.RemoveAll();  
  4.     CString StrTemp;      
  5.     struct hostent *host;  
  6.     struct in_addr *ptr;      
  7.     DWORD dwScope = RESOURCE_CONTEXT;  
  8.     NETRESOURCE *NetResource = NULL;  
  9.     HANDLE hEnum;  
  10.     WNetOpenEnum( dwScope, NULL, NULL, NULL, &hEnum );  
  11.     WSADATA wsaData;  
  12.       
  13.     WSAStartup(MAKEWORD(1,1),&wsaData);  
  14.       
  15.     if ( hEnum )      
  16.     {  
  17.         DWORD Count = 0xFFFFFFFF;  
  18.         DWORD BufferSize = 2048;  
  19.         LPVOID Buffer = new char[2048];  
  20.           
  21.         WNetEnumResource( hEnum, &Count, Buffer, &BufferSize );  
  22.         NetResource = (NETRESOURCE*)Buffer;  
  23.         char StrHostName[200];  
  24.         for ( unsigned int i = 0; i < BufferSize/sizeof(NETRESOURCE); i++, NetResource++ )         
  25.         {  
  26.             if ( NetResource->dwUsage == RESOURCEUSAGE_CONTAINER && NetResource->dwType == RESOURCETYPE_ANY ){  
  27.                 if ( NetResource->lpRemoteName )  
  28.                 {  
  29.                     CString strFullName = NetResource->lpRemoteName;  
  30.                     if ( 0 == strFullName.Left(2).Compare(_T("\\\\")) )           
  31.                         strFullName = strFullName.Right(strFullName.GetLength()-2);                      
  32.                     //获得主机名  
  33.                     gethostname( StrHostName, strlen( StrHostName ) );  
  34.                     MyListName.Add(CString(StrHostName));  
  35.                     char _hostName[MAX_PATH];  
  36.                     MyWideChar_tToMultiByte(strFullName.GetBuffer(0), _hostName, sizeof(_hostName));  
  37.                     //由主机名获得跟它对应的主机信息  
  38.                     host = gethostbyname(_hostName);  
  39.                     if(host == NULL) continue;   
  40.                     ptr = (struct in_addr *) host->h_addr_list[0];                 
  41.                     // 获得机器的IP地址              
  42.                     int a = ptr->S_un.S_un_b.s_b1;             
  43.                     int b = ptr->S_un.S_un_b.s_b2;    
  44.                     int c = ptr->S_un.S_un_b.s_b3;    
  45.                     int d = ptr->S_un.S_un_b.s_b4;    
  46.                     StrTemp.Format(_T("%s : %d.%d.%d.%d"),strFullName,a,b,c,d);  
  47.   
  48.                     MyList.Add(StrTemp);  
  49.                 }  
  50.             }  
  51.         }  
  52.         delete Buffer;  
  53.           
  54.         WNetCloseEnum( hEnum );   
  55.     }  
  56.       
  57.     WSACleanup();  
  58. }  
  59.   
  60. BOOL CGetNetInfoDlg::MyWideChar_tToMultiByte(LPTSTR lpcwszStr, LPSTR lpOut,INT nOutSize)  
  61. {  
  62.     DWORD dwMinSize = 0;  
  63.     dwMinSize = WideCharToMultiByte(CP_OEMCP,NULL,lpcwszStr,-1,NULL,0,NULL,FALSE);  
  64.   
  65.     if(dwMinSize > nOutSize)  
  66.     {  
  67.         return FALSE;  
  68.     }  
  69.     else  
  70.     {  
  71.         WideCharToMultiByte(CP_OEMCP,NULL,lpcwszStr,-1,lpOut,nOutSize,NULL,FALSE);  
  72.         return TRUE;  
  73.     }  
  74. }  


最后不要忘了加上动态连接库以及头文件哈

  1. #include <WinSock2.h>  
  2. #include <Winnetwk.h>  
  3.   
  4. #pragma comment(lib,"Ws2_32.lib")  
  5. #pragma comment(lib, "Mpr.lib")  


运行效果图

因为的局域网就自己一个人,所以只出现了我自己的主机信息。

 网络的基础应用就写到这吧,下一次我将写socket编程相关的内容。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值