一、echo客户/服务器程序
1、网络结构模型
a、集中式系统----有一个大型的中央处理系统,有点是数据容易备份,不易感染病毒,日常生活中的ATM、POS等使用的都是集中式系统
b、分布式系统----具有高度的可靠性、均衡负载、满足不同的需要
c、对等网络模型----P2P网络,每一台主机处于同等地位,既作为客户端,又充当其他主机的服务器,有可非为无结构网络、有结构网络、混合式网络、集中式网络
2、echo客户程序
mySocketClient.cpp:
[cpp] view plain copy print ?
- #include "stdafx.h"
- #include "winsock2.h"
- #include "iostream"
- #pragma comment(lib, "ws2_32")
-
- #define ECHO_DEF_PORT 7 //连接的默认端口
- #define ECHO_BUF_SIZE 256 //缓冲区的大小
-
- #define InitSockets() { \
- WORD version; \
- WSADATA wsaData; \
- version = MAKEWORD( 2,0 ); \
- WSAStartup(version, &wsaData); }
-
- #define CleanupSockets() WSACleanup()
-
- void DoSoming()
- {
- SOCKET echoSoc = 0;
- struct sockaddr_in servAddr;
- unsigned short port = ECHO_DEF_PORT;
- int result = 0;
- int sendLen = 0;
- char *testSendData = "hello world";
- char recv_buf[ECHO_BUF_SIZE];
-
- sendLen = strlen(testSendData);
-
- servAddr.sin_family = AF_INET;
- servAddr.sin_port = htons(port);
- servAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
-
- if (servAddr.sin_addr.s_addr == INADDR_NONE)
- {
- std::cout << "[ECHO] invalid address" << WSAGetLastError() << std::endl;
- exit(-1);
- }
-
- echoSoc = socket(AF_INET, SOCK_STREAM, 0);
- result = connect(echoSoc, (struct sockaddr *)&servAddr, sizeof(servAddr));
-
- if (result == 0)
- {
- result = send(echoSoc, testSendData, sendLen, 0);
- result = recv(echoSoc, recv_buf, ECHO_BUF_SIZE, 0);
- }
-
- if (result > 0)
- {
- recv_buf[result] = 0;
- std::cout << "[Echo Client] receives : " << recv_buf << std::endl;
- }
- else
- {
- std::cout << "[Echo Client] error : " << WSAGetLastError() << std::endl;
- }
-
- closesocket(echoSoc);
- CleanupSockets();
- }
-
- int _tmain(int argc, _TCHAR* argv[])
- {
- DoSoming();
-
- return 0;
- }
#include "stdafx.h"
#include "winsock2.h"
#include "iostream"
#pragma comment(lib, "ws2_32")
#define ECHO_DEF_PORT 7 //连接的默认端口
#define ECHO_BUF_SIZE 256 //缓冲区的大小
#define InitSockets() { \
WORD version; \
WSADATA wsaData; \
version = MAKEWORD( 2,0 ); \
WSAStartup(version, &wsaData); }
#define CleanupSockets() WSACleanup()
void DoSoming()
{
SOCKET echoSoc = 0;
struct sockaddr_in servAddr;
unsigned short port = ECHO_DEF_PORT;
int result = 0;
int sendLen = 0;
char *testSendData = "hello world";
char recv_buf[ECHO_BUF_SIZE];
sendLen = strlen(testSendData);
servAddr.sin_family = AF_INET;
servAddr.sin_port = htons(port);
servAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
if (servAddr.sin_addr.s_addr == INADDR_NONE)
{
std::cout << "[ECHO] invalid address" << WSAGetLastError() << std::endl;
exit(-1);
}
echoSoc = socket(AF_INET, SOCK_STREAM, 0);
result = connect(echoSoc, (struct sockaddr *)&servAddr, sizeof(servAddr));
if (result == 0)
{
result = send(echoSoc, testSendData, sendLen, 0);
result = recv(echoSoc, recv_buf, ECHO_BUF_SIZE, 0);
}
if (result > 0)
{
recv_buf[result] = 0;
std::cout << "[Echo Client] receives : " << recv_buf << std::endl;
}
else
{
std::cout << "[Echo Client] error : " << WSAGetLastError() << std::endl;
}
closesocket(echoSoc);
CleanupSockets();
}
int _tmain(int argc, _TCHAR* argv[])
{
DoSoming();
return 0;
}
3、echo服务器程序
mySokcetSer.cpp:
[cpp] view plain copy print ?
- #include "stdafx.h"
- #include "winsock2.h"
- #include "iostream"
- #pragma comment(lib, "ws2_32")
-
- #define ECHO_DEF_PORT 7 //连接的默认端口
- #define ECHO_BUF_SIZE 256 //缓冲区的大小
-
- #define InitSockets() { \
- WORD version; \
- WSADATA wsaData; \
- version = MAKEWORD( 2,0 ); \
- WSAStartup(version, &wsaData); }
-
- #define CleanupSockets() WSACleanup()
-
- void DoSoming()
- {
- SOCKET echoSoc = 0;
- SOCKET acptSoc = 0;
-
- struct sockaddr_in servAddr;
- struct sockaddr_in clntAddr;
-
- unsigned short port = ECHO_DEF_PORT;
- int result = 0;
- int addrLen = sizeof(struct sockaddr_in);
- char recvBuf[ECHO_BUF_SIZE];
-
- echoSoc = socket(AF_INET, SOCK_STREAM, 0);
-
- servAddr.sin_family = AF_INET;
- servAddr.sin_port = htons(port);
- servAddr.sin_addr.s_addr = INADDR_ANY;
-
- result = bind(echoSoc, (struct sockaddr *)&servAddr, sizeof(servAddr));
- if (result == SOCKET_ERROR)
- {
- std::cout << "[Echo Server] bind error : " << WSAGetLastError() << std::endl;
- closesocket(echoSoc);
- exit(-1);
- }
-
- listen(echoSoc, SOMAXCONN);
-
- std::cout << "[Echo Server] is running ... ..." << std::endl;
- while (true)
- {
- acptSoc = accept(echoSoc, (struct sockaddr *)&clntAddr, &addrLen);
- if (acptSoc == INVALID_SOCKET)
- {
- std::cout << "[Echo Server] bind error : " << WSAGetLastError() << std::endl;
- break;
- }
-
- result = recv(acptSoc, recvBuf, ECHO_BUF_SIZE, 0);
- if (result > 0)
- {
- recvBuf[result] = 0;
- std::cout << "[Echo Server] receives : " << recvBuf << "from : "
- << inet_ntoa(clntAddr.sin_addr) << std::endl;
-
- result = send(acptSoc, recvBuf, result, 0);
- }
-
- closesocket(acptSoc);
- }
-
- closesocket(acptSoc);
- CleanupSockets();
- }
-
- int _tmain(int argc, _TCHAR* argv[])
- {
- DoSoming();
-
- return 0;
- }
#include "stdafx.h"
#include "winsock2.h"
#include "iostream"
#pragma comment(lib, "ws2_32")
#define ECHO_DEF_PORT 7 //连接的默认端口
#define ECHO_BUF_SIZE 256 //缓冲区的大小
#define InitSockets() { \
WORD version; \
WSADATA wsaData; \
version = MAKEWORD( 2,0 ); \
WSAStartup(version, &wsaData); }
#define CleanupSockets() WSACleanup()
void DoSoming()
{
SOCKET echoSoc = 0;
SOCKET acptSoc = 0;
struct sockaddr_in servAddr;
struct sockaddr_in clntAddr;
unsigned short port = ECHO_DEF_PORT;
int result = 0;
int addrLen = sizeof(struct sockaddr_in);
char recvBuf[ECHO_BUF_SIZE];
echoSoc = socket(AF_INET, SOCK_STREAM, 0);
servAddr.sin_family = AF_INET;
servAddr.sin_port = htons(port);
servAddr.sin_addr.s_addr = INADDR_ANY;
result = bind(echoSoc, (struct sockaddr *)&servAddr, sizeof(servAddr));
if (result == SOCKET_ERROR)
{
std::cout << "[Echo Server] bind error : " << WSAGetLastError() << std::endl;
closesocket(echoSoc);
exit(-1);
}
listen(echoSoc, SOMAXCONN);
std::cout << "[Echo Server] is running ... ..." << std::endl;
while (true)
{
acptSoc = accept(echoSoc, (struct sockaddr *)&clntAddr, &addrLen);
if (acptSoc == INVALID_SOCKET)
{
std::cout << "[Echo Server] bind error : " << WSAGetLastError() << std::endl;
break;
}
result = recv(acptSoc, recvBuf, ECHO_BUF_SIZE, 0);
if (result > 0)
{
recvBuf[result] = 0;
std::cout << "[Echo Server] receives : " << recvBuf << "from : "
<< inet_ntoa(clntAddr.sin_addr) << std::endl;
result = send(acptSoc, recvBuf, result, 0);
}
closesocket(acptSoc);
}
closesocket(acptSoc);
CleanupSockets();
}
int _tmain(int argc, _TCHAR* argv[])
{
DoSoming();
return 0;
}
二、TCP/IP体系结构
1、术语
TCP/IP的名字来自于:传输控制协议(TCP)和网际协议(IP)分别位于TCP/IP模型的第三次和第四层。数据在不同层中的常用称呼:
a、段-------段用在TCP协议中,一个段是指端到端的传输单位,它包括了TCP首部及后面的应用程序数据,被封装在IP数据报中传输
b、消息----在底层协议的描述中,消息是指传输层协议的数据单位
c、IP数据报----用于IP协议中,一个IP数据报是是IP协议中端到端的传输单位
d、分组-----分组是通过网络层和链路层之间的接口传递的数据单位
e、帧--------帧是链路层协议中的传输单位,包括链路层首部及后面的分组
2、模型
OSI分为7个层次,在OSI参考模型出现时,用于网络互连的TCP/IP协议已经被广泛使用了,所以现在常用的是TCP/IP模型。TCP/IP模型包含了四层:应用层、传输层、网络层、网络接口层,从上到下OSI参考模型对比可以看出:TCP/IP的应用层与OSI模型的最上面三层相对应,不再区分表示层和会话层;两个模型的第三、第四层是一一对应的;TCP/IP的网络接口层与OSI模型的数据链路层对应;TCP/IP模型不包括硬件设备所在的物理层
三、网际协议----------IP
1、简介
互联网中IP是一个主机到主机的协议。TCP/UDP/ICMP/TGMP都封装在IP数据报中传输,他是整个TCP/IP协议的核心部分。
2、主要功能
IP协议的功能和目的就是在互相连接的网络间传输数据报,为了完成这个功能,他把数据报从一个IP模块传送到另一个IP模块,一直到目的主机。参与网络通信的主机和路由器都需要实现IP协议,这些模块共同遵守IP协议制定的规则,用同样的方式解释IP首部中的各个字段、选项、分片和重组数据报等。中间结点(通常是路由器)使用IP首部中的目的地址为数据报选择路由,路由过程中,数据报可能会通过MTU(Maximum Transmissim Unit最大传输单元)小于数据报大小的网络,为了克服这个困难,IP协议提供了分片机制,他的主要功能有:
a、寻址和选路
在选路的时有两种数据报的交付形式:直接交付和间接交付。直接交付是两台主机位于同一个物理网络,数据可以从一台主机直接到另一台数据。间接交付是两台主机不在同一个物理网络中,发送方把数据报发给一个路由器,然后有路由器发到目的主机
b、封装和解析
c、分片和重组
3、数据报格式
a、版本号
b、首部长度
c、服务类型
d、总长度
e、标识符
f、标志
g、片偏移
h、生存时间
i、协议
j、首部校验和
IP数据报首部校验和,用于检验首部的有效性和完整性。校验和的计算是首部校验和字段置0,把首部看成是16位(bits)的整数序列,计算每个16位(bits)整数的反码,再求他们的和,最后计算结果的反码而求出。这是一个简单的校验和算法,实验证明他是合适的。数据报首部发生变化时,如减少TTL、增加或修改选项、发生分片等,要重新计算校验和。
收到IP数据报时,使用同样的算法计算校验和,由于接收方计算校验和时包含了发送方保存在首部中的校验和,如果首部在传输过程上没有发送差错,接收方计算的结果应该是0.如果不是0,接收方便默默地抛弃收到的数据报,不产生差错信息。
校验和只包含首部,而不对数据部分校验,带来的好处是:首部一般都比数据少,在主机或路由器处理数据报时,减少了计算时间;另外,允许高层协议选择自己的校验和算法,在可靠性高的网络上,甚至可以不对数据校验,比较灵活。
k、地址
l、选项
3、校验和程序(测试环境:win7+vs2012)
为了使用快速的非安全版本的函数,使用了_CRT_SECURE_NO_WARNINGS和_CRT_SECURE_NO_DEPRECATE宏
CheckSum.cpp:
[cpp] view plain copy print ?
- #include "stdafx.h"
- #include "iostream"
-
- #define DATA_MAX_LEN 14 //最大数据长度
-
- struct dataSum
- {
- char data[DATA_MAX_LEN];
- unsigned short checkSum;
- };
-
- unsigned short ipCheckSum(unsigned short *buf, int bufLen)
- {
- unsigned long checkSum = 0;
-
- while (bufLen > 1)
- {
- checkSum += *buf++;
- bufLen -= sizeof(unsigned short);
- }
-
- if (bufLen)
- {
- checkSum += *(unsigned char *)buf;
- }
-
- checkSum = (checkSum >> 16) + (checkSum & 0xffff);
- checkSum += (checkSum >> 16);
-
- return (unsigned short)(~checkSum);
- }
-
- int _tmain(int argc, _TCHAR* argv[])
- {
- struct dataSum msg = { "hello world!", 0 };
- int length = sizeof(msg);
-
-
- msg.checkSum = ipCheckSum((unsigned short *)&msg, length);
- std::cout << "Calculate check sum : " << msg.checkSum << std::endl;
-
-
- msg.checkSum = ipCheckSum((unsigned short *)&msg, length);
- std::cout << "Verify check sum : " << msg.checkSum << std::endl;
-
-
-
- strcpy(msg.data, "test");
- msg.checkSum = ipCheckSum((unsigned short *)&msg, length);
- std::cout << "Verify check sum : " << msg.checkSum << std::endl;
-
- return 0;
- }
#include "stdafx.h"
#include "iostream"
#define DATA_MAX_LEN 14 //最大数据长度
struct dataSum
{
char data[DATA_MAX_LEN]; //数据
unsigned short checkSum; //校验和
};
unsigned short ipCheckSum(unsigned short *buf, int bufLen)
{
unsigned long checkSum = 0;
while (bufLen > 1)
{
checkSum += *buf++;
bufLen -= sizeof(unsigned short);
}
if (bufLen)
{
checkSum += *(unsigned char *)buf;
}
checkSum = (checkSum >> 16) + (checkSum & 0xffff);
checkSum += (checkSum >> 16);
return (unsigned short)(~checkSum);
}
int _tmain(int argc, _TCHAR* argv[])
{
struct dataSum msg = { "hello world!", 0 };
int length = sizeof(msg);
//计算校验和
msg.checkSum = ipCheckSum((unsigned short *)&msg, length);
std::cout << "Calculate check sum : " << msg.checkSum << std::endl;
//包含checksum值、验证校验和----产生正确的校验
msg.checkSum = ipCheckSum((unsigned short *)&msg, length);
std::cout << "Verify check sum : " << msg.checkSum << std::endl;
//包含checksum值、验证校验和----消息被修改后的校验
// msg.data = "test";
strcpy(msg.data, "test");
msg.checkSum = ipCheckSum((unsigned short *)&msg, length);
std::cout << "Verify check sum : " << msg.checkSum << std::endl;
return 0;
}
四、用户数据报协议----UDP
1、主要功能
UDP是一个面向数据报的传输层协议,他的特点是:简单、快捷
他是无连接的,发送数据之前,不需要建立连接,数据发送完成后,也不用终止连接,只要应用程序有数据,就直接发送,没有连接建立、维护、终止所带来的开销。应用程序和UDP协议都不关心底层网络是的MTU、分片及路由等操作,这些瓯都由IP层来完成
UDP是不可靠的,对发送的数据,并不保证对方一定能收到,即使收到了也不会确认。使用UDP的应用程序要自己负责数据的重传和确认,解决可靠性相关问题。
数据传输过程发生拥塞时,UDP不做流量及拥塞控制,这在实际使用中会引起一些问题。如路由器发送拥塞就会扔掉数据,TCP发现有数据丢失时,将减慢数据的发送速度;而UDP并不关心数据的丢失仍然照常发送。结果TCP减少使用的网络带宽都被UDP抢占了,导致TCP不能正常运行或性能严重下降,这对TCP是不公平的。因此,在路由器的实现中需要对UDP做特殊的处理,发送拥塞时也要用一些算法抛弃UDP的数据
2、主要应用
a、多播或广播通信
b、简短的请求/应答交换----比如DNS、TFTP、DHCP、BOOTP、SNMP
c、效率高于可靠性的应用----比如媒体传输RTP
d、轻量级的通信协议----WSP、WTP、WAP
五、传输控制协议----TCP
1、简介
TCP是一个面向连接的、端到端的可靠传输协议,为跨越不同网络的主机进程通信提供了可靠的传输机制。即使底层网络不可靠或有拥塞发送,TCP也要保持足够的健壮性。他对底层通信协议的可靠性做了非常少的假设,认为可以使用一个简单的、提供不可靠数据报服务的底层协议。原则上,TCP可以运行在各种通信设备行,如:分组交换和电路交换等。
TCP吧应用程序交给他的数据看做是简单的字节流,他不理解数据的内容,也不区分数据的边界。他把应用程序的数据封装成TCP段,TCP段再有IP封装成数据报发送到网络上。
TCP使用滑动窗口管理传输的数据流,允许每一端根据他处理能力的大小声明使用的窗口,避免高速设备发送的数据导致低速设备的缓冲区溢出。TCP完成如下功能:
a、数据的封装和传输----会有双方协商的最大段大小(Maximum Segment Size,MSS)
b、可靠性----包括校验和、序号、超时重传
c、流量控制
d、拥塞避免
e、多路复用
f、连接管理
2、TCP连接管理,TCP有很多的状态比如CLOSED和LISTEN等,这些状态间的转换都需要进行管理
六、ICMP协议
1、简介
ICMP(Internet Control Message Protocol),译为“互联网控制报文协议”,封装了在IP数据报内被传输,看起来像是IP的上层协议,实际上它是IP协议的一个组成部分,必须被每一个IP模块实现。
IP协议是无连接的,不能提供可靠的数据传输。在数据报传输过程中,可能会由于路由器、主机或传输链路故障导致传输错误,IP协议并不处理这些故障,ICMP协议对此问题提供了反馈机制,通过ICMP,路由器和目的主机能够与源主机通信,报告数据传输过程中的错误和控制消息。
2、Echo请求和应答
Echo主要用来探测另一个主机是否可达到。ICMP是在IP协议中传输的,成功接收到Echo应答表明源主机和目的主机的IP和ICMP可以正常工作,中间的路由器正确,可以成功转发数据,但不能确定源主机和目的主机的上层协议,如TCP或UDP是否工作正常。
TCP/IP协议提供了许多有用的工具帮助用户或网络管理员诊断网络问题,最常用的调试工具Ping和Traceroute就利用Echo的请求和应答报文。
3、目的地不可达
4、源抑制
5、重定向
6、路由通告与请求
7、超时
七、socket基本概念
1、简介
a、协议簇和地址簇
TCP/IP协议簇符号标识是PF_*,地址簇符号标识是AF_*。由于TCP/IP的协议簇只有一种地址簇,已有的实现都把他们定义为相同的值。
b、端口
能通过端口找到相应的进程
2、WinSocket和Berkeley socket的区别
3、WinSocket I/O模式
在WinSocket中主要有三种I/O操作模式:阻塞、非阻塞和重叠I/O。在默认情况下,Berkeley和WinSocket都是建立阻塞的socket。为了建立非阻塞的socket,应用程序使用ioctlsocket的FIONBIO命令把socket设置为非阻塞的。在创建socket中制定了重叠I/O属性时,才能使用重叠I/O功能
a、阻塞I/O
线程使用了一个recv调用,但socket上没有数据,线程将被阻塞,直到socket接收到数据,并把数据复制到线程接收函数提供的缓冲区中。阻塞I/O虽然简单,但他不是最有效的编程方式
b、非阻塞I/O
和上面的一个简单的区别就是应用程序使用select检测是否有数据刻度,如有则使用recv接收数据,确保recv不会阻塞
c、重叠I/O
这是在WinSocket2.0版本中新加的特征。发送数据时,程序可以连续多次调用发送函数提供多个数据缓冲区去排队输出数据。发送数据时,程序使用WSASend或WSASendTo提供用户数据的缓冲区,如果Winsock不能立即发送数据,这些数据将按调用的顺序排队,当可以发送时再把数据发送到网络上。用户在接受到数据已经成功发送出去的通知之前,不能破坏缓冲区中的内容
d、同步和异步
同步传输数据块与数据块之间的时间间隔是固定的,必须严格规定它们的时间关系。同步有两种传输方式:字节同步和位同步,一般采用位同步方式
异步传输是指字符之间(前一个字符结束到下一个字符开始)的时间间隔是可变的,并不需要严格地限制它们的时间关系
八、转换函数
1、字节序
一般有两种选择:从多字节数据的小段(右端,最低有效位)开始,称为小尾数;另一种是从大端(左端,最高有效位)开始顺序传输,称为大尾数
主机上面的字节序称为主机字节序
在只有一台计算机的情况下,不需要考虑字节相关的问题,计算机会自动确定用哪种方式存储数值数据。但如果把计算机连接到网络上,就必须考虑字节序问题。当两台计算机的字节序不同时,如果在传输和接收数据时都按自己的方式处理数据,就会导致错误
2、一个判断字节序的程序
在这个程序中有一个联合体,第一个成员是sval,是程序中要判断的数组,第二个成员cval与sval占用相同的内存大小,这样就可以知道sval中的高位和地位都在内存的什么位置了。这两个成员有同样的长度,当联合体中的成员长度不同时,最大的成员长度就是联合的长度
HostEndian.cpp:
[cpp] view plain copy print ?
- #include "stdafx.h"
- #include "iostream"
-
- union endian
- {
- unsigned short sval;
- unsigned char cval[4];
- };
-
- int _tmain(int argc, _TCHAR* argv[])
- {
- char *info = "unknow endian";
- union endian t = { 0x1234 };
-
- if (t.cval[0] == 0x12 && t.cval[1] == 0x34)
- {
- info = "big-endian";
- }
- else if (t.cval[0] == 0x34 && t.cval[1] == 0x12)
- {
- info = "little-endian";
- }
-
- std::cout << "host is : " << info << std::endl;
-
- return 0;
- }
#include "stdafx.h"
#include "iostream"
union endian
{
unsigned short sval;
unsigned char cval[4];
};
int _tmain(int argc, _TCHAR* argv[])
{
char *info = "unknow endian";
union endian t = { 0x1234 };
if (t.cval[0] == 0x12 && t.cval[1] == 0x34)
{
info = "big-endian";
}
else if (t.cval[0] == 0x34 && t.cval[1] == 0x12)
{
info = "little-endian";
}
std::cout << "host is : " << info << std::endl;
return 0;
}
3、网络字节序
为了防止字节不统一的情况发生,即通信的双方对同一数组数据做出不同的解释,而导致错误,Internet定义了存储数值数据的标准顺序----高序字节在低序字节之前,保存在内存的低地址,即大尾数字节序,也称为网络字节序,而计算机上的字节序称为主机字节序。这两个字节序有可能不一样,所以需要先进行判断
NetConvt.cpp:
[cpp] view plain copy print ?
- #include "stdafx.h"
- #include "iostream"
- #include "winsock2.h"
-
- #define INADDR_INVALD 0xffffffff //无效地址
-
- static unsigned long range[] = { 0xffffffff, 0xffffff, 0xffff, 0xff };
-
- unsigned short NetHtons(unsigned short shost)
- {
- unsigned char *p = (unsigned char *)&shost;
-
- return (unsigned short)(p[0] << 8 | p[1]);
- }
-
- unsigned short NetNtohs(unsigned short snet)
- {
- unsigned char *p = (unsigned char *)&snet;
-
- return (unsigned short)(p[0] << 8 | p[1]);
- }
-
- unsigned long NetHtonl(unsigned long lhost)
- {
- unsigned char *p = (unsigned char *)&lhost;
-
- return (unsigned long)(p[3] | (p[2] << 8) | (p[1] << 16) | (p[0] << 24));
- }
-
- unsigned long NetNtohl(unsigned long lnet)
- {
- unsigned char *p = (unsigned char *)&lnet;
-
- return (unsigned long)(p[3] | (p[2] << 8) | (p[1] << 16) | (p[0] << 24));
- }
-
-
-
-
-
-
-
-
-
-
- char *NetNtoa(struct in_addr addr)
- {
- static char buf[16];
- unsigned char *p = (unsigned char *)&addr;
-
- sprintf(buf, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
-
- return buf;
- }
-
-
-
-
-
-
-
-
-
-
- unsigned long NetAddr(const char *cp)
- {
- unsigned char addr[4], *p = addr, c = *cp;
- unsigned long value;
- int i, n, base, digit;
-
- while (true)
- {
- base = 10, value = 0; digit = 0;
- if (!isdigit(c))
- {
- return !INADDR_INVALD;
- }
-
-
- if (c == '0')
- {
- base = 8, c = *++cp;
- if (c == 'x' || c == 'X')
- {
- base = 16, c = *++cp;
- }
- }
-
-
- while (c = *cp++)
- {
- if (isdigit(c))
- {
- if (base == 8 && c >= '8')
- {
- return INADDR_INVALD;
- }
- value = value * base + (c - '0');
- }
- else if (base == 16 && isxdigit(c))
- {
- value = (value << 4) | (c + 10 - (islower(c) ? 'a' : 'A' ));
- }
- else
- {
- break;
- }
- digit = 1;
- }
-
- if (c == '.')
- {
- if (!digit || p >= addr + 3 || value > 0xff)
- {
- return INADDR_INVALD;
- }
- *p++ = (unsigned char)value;
- c = *cp;
- }
- else
- {
- break;
- }
- }
-
- n = p - addr;
-
- if (!digit || (value > range[n])
- || c != '\0' && !isspace(c))
- {
- return INADDR_INVALD;
- }
-
- for (i = 3; i >= n; i--, value >>= 8)
- {
- addr[i] = (unsigned char)(value & 0xff);
- }
- value = *(unsigned long *)addr;
-
- return value;
- }
-
- int _tmain(int argc, _TCHAR* argv[])
- {
- unsigned short host_s = 0x1234, net_s;
- unsigned long host_l = 0x12345678, net_l;
- char *addr_dec = "192.168.10.26", *p;
- struct in_addr addr;
-
- net_s = NetHtons(host_s);
- net_l = NetHtonl(host_l);
-
- std::cout << "net byte order is : net_s = 0x" << std::hex << net_s
- << ", net_l = 0x" << std::hex << net_l << std::endl;
-
- addr.s_addr = NetAddr(addr_dec);
- p = NetNtoa(addr);
-
- std::cout << "net addr is : net_s = " << addr.s_addr
- << ", string addr is " << p << std::endl;
-
- return 0;
- }
#include "stdafx.h"
#include "iostream"
#include "winsock2.h"
#define INADDR_INVALD 0xffffffff //无效地址
static unsigned long range[] = { 0xffffffff, 0xffffff, 0xffff, 0xff };
unsigned short NetHtons(unsigned short shost)
{
unsigned char *p = (unsigned char *)&shost;
return (unsigned short)(p[0] << 8 | p[1]);
}
unsigned short NetNtohs(unsigned short snet)
{
unsigned char *p = (unsigned char *)&snet;
return (unsigned short)(p[0] << 8 | p[1]);
}
unsigned long NetHtonl(unsigned long lhost)
{
unsigned char *p = (unsigned char *)&lhost;
return (unsigned long)(p[3] | (p[2] << 8) | (p[1] << 16) | (p[0] << 24));
}
unsigned long NetNtohl(unsigned long lnet)
{
unsigned char *p = (unsigned char *)&lnet;
return (unsigned long)(p[3] | (p[2] << 8) | (p[1] << 16) | (p[0] << 24));
}
/**************************************************************************
*
* 函数功能: 把网络字节序地址转换为点分十进制的IP地址.
*
* 参数说明: [IN] addr, 网络字节序地址.
*
* 返 回 值: 返回点分十进制的IP地址.
*
**************************************************************************/
char *NetNtoa(struct in_addr addr)
{
static char buf[16]; //转换后的最大长度为15
unsigned char *p = (unsigned char *)&addr;
sprintf(buf, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
return buf;
}
/**************************************************************************
*
* 函数功能: 把点分十进制的IP地址转换为网络字节序地址.
*
* 参数说明: [IN] cp, 点分十进制的IP地址.
*
* 返 回 值: 成功返回网络字节序地址, 失败返回 INADDR_INVALD.
*
**************************************************************************/
unsigned long NetAddr(const char *cp)
{
unsigned char addr[4], *p = addr, c = *cp;
unsigned long value;
int i, n, base, digit;
while (true)
{
base = 10, value = 0; digit = 0;
if (!isdigit(c))
{
return !INADDR_INVALD;
}
//1.处理前缀:0:8进制;0x:16进制;否则10进制
if (c == '0')
{
base = 8, c = *++cp;
if (c == 'x' || c == 'X')
{
base = 16, c = *++cp;
}
}
//2.解析地址的一部分,处理数字,知道‘.’或非数字字符
while (c = *cp++)
{
if (isdigit(c))
{
if (base == 8 && c >= '8')
{
return INADDR_INVALD;
}
value = value * base + (c - '0');
}
else if (base == 16 && isxdigit(c))
{
value = (value << 4) | (c + 10 - (islower(c) ? 'a' : 'A' ));
}
else
{
break;
}
digit = 1; //到这说明至少有一个数字
}
//3.遇到‘.’的处理
if (c == '.')
{
if (!digit || p >= addr + 3 || value > 0xff)
{
return INADDR_INVALD;
}
*p++ = (unsigned char)value;
c = *cp;
}
else
{
break;
}
}
n = p - addr; //已经保存到addr中的数字个数
//解析完成,看是否有数字,检查尾部字节及值的范围
if (!digit || (value > range[n])
|| c != '\0' && !isspace(c))
{
return INADDR_INVALD;
}
//把地址的最后一部分保存到addr中
for (i = 3; i >= n; i--, value >>= 8)
{
addr[i] = (unsigned char)(value & 0xff);
}
value = *(unsigned long *)addr;
return value;
}
int _tmain(int argc, _TCHAR* argv[])
{
unsigned short host_s = 0x1234, net_s;
unsigned long host_l = 0x12345678, net_l;
char *addr_dec = "192.168.10.26", *p;
struct in_addr addr;
net_s = NetHtons(host_s);
net_l = NetHtonl(host_l);
std::cout << "net byte order is : net_s = 0x" << std::hex << net_s
<< ", net_l = 0x" << std::hex << net_l << std::endl;
addr.s_addr = NetAddr(addr_dec);
p = NetNtoa(addr);
std::cout << "net addr is : net_s = " << addr.s_addr
<< ", string addr is " << p << std::endl;
return 0;
}
程序不太难,比如NetNtohs、NetHtonl主要思想就是得到主机序列,在移位过来
先处理前缀部分,主要是让前面的数字乘以基数base并加上本次的数值,最后保存到addr数组中的一个过程
NetNtoa就是一个用sprintf来分割的操作;NetAddr只是一位一位的处理输入参数(点分十进制ip地址的一个过程)----首
分享到: