参考

  1. 《TCP/IP网络编程》 尹圣雨

分配给套接字的IP地址与端口号

IP是Internet Protocol(网络协议)的简写,是为收发网络数据而分配给计算机的值。端口号并非赋予计算机的值,而是为区分程序中创建的套接字而分配套接字的序号

网络地址(Internet Address)

IP地址分为两类:
(1)IPv4(Internet Protocol version 4):4字节地址族
(2)IPv6(Internet Protocol version 6):16字节地址族

现在还是主要使用IPv4,IPv6的普及将需要更长时间。

IPv4标准的4字节IP地址分为网络地址和主机(指计算机)地址,且分为A、B、C、D、E等类型。网络地址(网络ID)是为区分网络而设置的一部分IP地址。所以,“向相应网络传输数据”实际上是向构成网络的路由器(Router)或交换机(Switch)传递数据,由接收数据的路由器根据数据中的主机地址向目标主机传递数据

端口号

计算机中一般配有NIC(Network Interface Card,网络接口卡)数据传输设备。通过NIC向计算机内部传输数据时会用到IP。操作系统负责把传递到内部的数据适当分配给套接字,这时就要利用端口号。即,通过NIC接收的数据内由端口号,操作系统参考此端口号把数据传输给相应端口的套接字

无法将1个端口号分配给不同的套接字。另外,端口号由16位构成,可分配的端口号范围是0-65535。但0-1023是知名端口(Well-known PORT),一般分配给特定应用程序

虽然端口号不能重复,但TCP套接字和UDP套接字不会共用端口号,所以允许重复

地址信息的表示

表示IPv4地址的结构体

struct sockaddr_in
{sa_family_t sin_family;       // 地址族(Address Family)unit16_t sin_port;            // 16位TCP/UDP端口号struct in_addr sin_addr;      // 32位IP地址char sin_zero[8];             // 不使用
};

其中,in_addr定义如下

struct in_addr
{in_addr_t s_addr;             // 32位IPv4地址
};

里面存在大量额外定义的数据类型。可以参考POSIX(Portable Operating System Interface,可移植操作系统接口)。POSIX是为UNIX系列操作系统设立的标准

POSIX中定义的数据类型:
(1)sys/types.h头文件

  1. int8_t:signed 8-bit int

  2. uint8_t:unsigned 8-bit int (unsigned char)

  3. int16_t:signed 16-bit int

  4. uint16_t:unsigned 16-bit int (unsigned short)

  5. int32_t:signed 32-bit int

  6. uint32_t:unsigned 32-bit int (unsigned long)

(2)sys/socket.h

  1. sa_family_t:地址族(address family)

  2. socklen_t:长度(length of struct)

(3)netinet/in.h

  1. in_addr_t:IP地址,声明为uint32_t

  2. in_port_t:端口号,声明为uint16_t

之所以额外定义这些数据类型,是考虑到扩展性。例如,如果使用int32_t类型的数据,就能保证在任何时候都占用4字节,即使将来用64位表示int类型

结构体sockaddr_in的成员

(1)sin_family
每种协议族适用的地址族均不同。比如,IPv4使用4字节地址族,IPv6使用16字节地址族

  1. AF_INET:IPv4网络协议中使用的地址族

  2. AF_INET6:IPv6网络协议中使用的地址族

  3. AF_LOCAL:本地通信中采用的UNIX协议的地址族

(2)sin_port
以网络字节序保存16位端口号

(3)sin_addr
以网络字节序保存32位IP地址信息。本质上为32位整型数

(4)sin_zero
无特殊含义。只是为使结构体sockaddr_in的大小与sockaddr结构体保持一致而插入的成员。必须填充为0

实际上,bind函数的第二个参数期望得到的是sockaddr结构体变量地址值。而sockaddr结构体的定义如下:

struct sockaddr
{sa_family_t sin_family;     // 地址族sa_data[14];             // 地址信息
};

其中,sa_data保存的地址信息中需包含IP地址和端口号,剩余部分应填充0。而这对于包含地址信息来讲非常麻烦,继而有了新的结构体sockaddr_in,二者内存大小相同,只是sockaddr_in填写信息更方便

另外,由于sockaddr并非只为IPv4设计,因此,结构体要求在sin_family中指定地址族信息。为了与sockaddr一致,sockaddr_in结构体中也有地址族的信息,尽管sockaddr_in只用于保存IPv4地址信息

网络字节序与地址变换

字节序与网络字节序

CPU向内存保存数据的方式有2种,这意味着CPU解析数据的方式也有2种:

  1. 大端序(Big Endian):高位字节存放到低位地址
  2. 小端序(Little Endian):高位字节存放到高位地址

主机字节序(Host Byte Order)在不同CPU中也各不相同。目前主流的Intel系列CPU以小端序方式保存数据。2台字节序不同的计算机之间数据传递会出现问题,只有改变数据保存顺序才能被识别为同一值

所以,在通过网络传输数据时约定统一方式,这种约定称为网络字节序(Network Byte Order),统一为大端序。即,先把数据数组转化成大端序格式再进行网络传输

字节序转换(Endian Conversion)

转换字节序的函数:

  1. unsigned short htons(unsigned short);

  2. unsigned short ntohs(unsigned short);

  3. unsigned long htonl(unsigned long);

  4. unsigned long ntohl(unsigned long);

其中,

  1. htons中的h代表主机(host)字节序
  2. htons中的n代表网络(network)字节序
  3. s指short;l指的是long(Linux中long类型占用4个字节)
  4. 通常htons和ntohs用于端口号转换;htonl和ntohl用于IP地址转换
  5. 即使在大端序系统中,最好也经过主机字节序转换为网络字节序的过程,便于代码扩展
  6. 除了向sockaddr_in结构体变量填充数据外,其他情况无需考虑字节序问题,都是自动的

网络地址的初始化与分配

将字符串信息转换为网络字节序的整数型

sockaddr_in中保存地址信息的成员为32位整数型,但对于IP地址的表示,我们熟悉的是点分十进制表示法(Dotted Decimal Notation),而非整数型数据表示法

(1)inet_addr()
使用inet_addr函数可以将字符串形式的IP地址转换成32位整数型数据

#include <arpa/inet.h>in_addr_t inet_addr(const char* string);

成功时返回32位大端序整数型值,失败时返回INADDR_NONE

(2)inet_aton()
inet_aton函数与inet_addr函数在功能上完全相同,也将字符串形式IP地址转换为32位网络字节序整数并返回。只不过该函数利用了in_addr结构体,且使用频率更高

#include <arpa/inet.h>int inet_aton(const char* string, struct in_addr * addr);

成功时返回1,失败时返回0。string:含有需转换的IP地址信息的字符串地址值;addr:将保存转换结果的in_addr结构体变量的地址值

(3)inet_ntoa()
与inet_aton函数正好相反,inet_ntoa可以把网络字节序整数型IP地址转换成我们熟悉的字符串形式

#include <arpa/inet.h>char* inet_ntoa(struct in_addr adr);

成功时返回转换的字符串地址值,失败时返回-1

该函数未向程序员要求分配内存,而是在内部申请了内存并保存了字符串。也就是说,调用完该函数后,应立即将字符串信息复制到其他内存空间;否则,再次调用该函数,会覆盖之前保存的字符串信息

网络地址初始化

常见的网络地址信息初始化方法如下:

struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));            // 结构体变量addr的所有成员初始化为0
addr.sin_family = AF_INET;                 // 指定地址族
addr.sin_addr.s_addr = inet_addr(serv_ip); // 基于字符串的IP地址初始化
addr.sin_port = htons(atoi(serv_port));    // 基于字符串的端口号初始化

让所有成员初始化为0,是为了将sin_zero初始化为0

客户端地址信息初始化

服务器端声明sockaddr_in结构体变量,将其初始化为赋予服务器端IP和套接字的端口号,然后调用bind函数;而客户端则声明sockaddr_in结构体,并初始化为要与之连接的服务器端套接字的IP和端口号,然后调用connect函数

INADDR_ANY

利用常数INADDR_ANY分配服务器端的IP地址:

addr.sin_addr.s_addr = htonl(INADDR_ANY);

若采用这种方式,则可自动获取运行服务器端的计算机IP地址。而且,若同一计算机中已分配多个IP地址(多宿主(Multi-homed)计算机,路由器属于这一类),则只要端口号一致,就可以从不同IP地址接收数据。因此,服务器端中优先考虑这种方式;而客户端中除非带有一部分服务器端功能,否则不会采用

同一计算机中可以分配多个IP地址,实际IP地址的个数与计算机中安装的NIC的数量相等。即使是服务器端套接字,也需要决定应接收哪个IP传来的(哪个NIC传来的)数据。因此,服务器端套接字初始化过程中要求IP地址信息。若只有1个NIC,则直接使用INADDR_ANY

向套接字分配网络地址

bind函数负责把初始化的地址信息分配给套接字

#include <sys/socket.h>int bind(int sockfd, struct sockaddr* myaddr, socklen_t addrlen);

成功时返回0,失败时返回-1。sockfd:要分配地址信息(IP地址和端口号)的套接字文件描述符;myaddr:存有地址信息的结构体变量地址值;addrlen:第二个结构体变量的长度

基于Windows的实现

htons和htonl

与Linux平台下的使用无区别

inet_addr和inet_ntoa

Windows中不存在inet_aton,而inet_addr和inet_ntoa的使用于Linux下一样。但inet_addr会出现不能使用的情况,可以用inet_pton,需要包含头文件<Ws2tcpip.h>

#include <stdio.h>
#include <string.h>
#include <WinSock2.h>
#include <Ws2tcpip.h>
void ErrorHandling(char* message);int main(int argc, char* argv[])
{WSADATA wsaData;if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0){ErrorHandling("WSAStartup() error!");}{char* addr = "127.212.124.78";// unsigned long conv_addr = inet_addr(addr);// if (conv_addr == INADDR_NONE)// {//     printf("Error occured! \n");// }// else// {//     printf("Network ordered integer addr: %#lx \n", conv_addr);// }unsigned long conv_addr;if ((inet_pton(AF_INET, addr, &conv_addr)) == 0){printf("Error occured! \n");}else{printf("Network ordered integer addr: %#lx \n", conv_addr);}}{struct sockaddr_in addr;char* strPtr;char strArr[20];addr.sin_addr.s_addr = htonl(0x1020304);strPtr = inet_ntoa(addr.sin_addr);strcpy(strArr, strPtr);printf("Dotted-Decimal notation3 %s \n", strArr);}WSACleanup();return 0;
}void ErrorHandling(char* message)
{fputs(message, stderr);fputc('\n', stderr);exit(1);
}

WSAStringToAddress和WSAAddressToString

这是WinSock2中增加的2个转换函数,在功能上与inet_addr和inet_ntoa完全相同,但优点在于支持多种协议,在IPv4和IPv6中均可适用。但这两个函数依赖于特定平台,会降低兼容性

(1)WSAStringToAddress

#include <WinSock2.h>INT WSAStringToAddress(LPTSTR AddressString, INT AddressFamily, LPWSAPROTOCOL_INFO lpProtocolInfo,LPSOCKADDR lpAddress, LPINT lpAddressLength
);

成功时返回0,失败时返回SOCKET_ERROR。AddressString:含有IP和端口号的字符串地址值;AddressFamily:第一个参数中地址所属的地址族信息;lpProtocolInfo:设置协议提供这(Provider),默认为NULL;lpAddress:保存地址信息的结构体变量地址值;lpAddressLength:第四个参数中传递的结构体长度所在的变量地址值。新出现的各种类型都是针对默认数据类型的typedef声明

(2)WSAAddressToString
它将结构体中的地址信息转换成字符串形式

#include <WinSock2.h>INT WSAAddressToString(LPSOCKADDR lpsaAddress, DWORD dwAddressLength,LPWSAPROTOCOL_INFO lpProtocolInfo, LPSTR lpszAddressString,LPDWORD lpdwAddressStringLength
);

成功时返回0,失败时返回SOCKET_ERROR。lpsaAddress:需要转换的地址信息结构体变量地址值;dwAddressLength:第一个参数中结构体的长度;lpProtocolInfo:设置协议提供者(Provider),默认为NULL;lpszAddressString:保存转换结果的字符串的地址值;lpdwAddressStringLength:第四个参数中存有地址信息的字符串长度

示例:

#undef UNICODE
#undef _UNICODE
#include <stdio.h>
#include <WinSock2.h>int main(int argc, char* argv[])
{char* strAddr = "203.211.218.102:9190";char strAddrBuf[50];SOCKADDR_IN servAddr;int size;WSADATA wsaData;WSAStartup(MAKEWORD(2, 2), &wsaData);size = sizeof(servAddr);WSAStringToAddress(strAddr, AF_INET, NULL, (SOCKADDR*)&servAddr, &size);size = sizeof(strAddrBuf);WSAAddressToString((SOCKADDR*)&servAddr, sizeof(servAddr), NULL, strAddrBuf, &size);printf("Second conv result: %s \n", strAddrBuf);WSACleanup();return 0;
}

网络编程——分配给套接字的IP地址与端口号相关推荐

  1. TCP/IP网络编程 学习笔记_3 --给套接字分配IP地址和端口号

    IP地址和端口号 1,IP地址:为使计算机连接到网络并收发数据,必须为其分配IP地址.IP地址分为两类:IPv4(4字节地址族)和IPv6(16字节地址族).它们主要区别就是在表示IP地址所用的字节数 ...

  2. 【Linux网络编程】UDP 套接字编程

    [Linux网络编程]UDP 套接字编程 [1]用户数据报协议(UDP) UDP是一个简单的传输层协议,不保证UDP数据报会到达其最终目的地,不保证各个数据报的先后顺序跨网络后保持不变,也不保证每个数 ...

  3. Linux网络编程:原始套接字的魔力【续】

    如何从链路层直接发送数据帧        本来以为这部分都弄完了,结果有朋友反映说看了半天还是没看到如何从链路层直接发送数据.因为上一篇里面提到的是从链路层"收发"数据,结果只&q ...

  4. 【网络编程】Socket套接字;UDP数据报套接字编程;TCP流套接字编程

    文章目录 1. 什么是网络编程 2. 网络编程中的基本概念 3. Socket套接字 4 UDP数据报套接字编程 4.1 客户端服务器交互流程 4.2 UDP版本的回显服务 4.3 英译汉服务 5. ...

  5. UNIX网络编程.卷1,套接字联网API(第3版)(中文版)(Stevens经典著作,两位顶级网络编程专家应邀执笔修订)...

    UNIX网络编程.卷1,套接字联网API(第3版)(中文版)(Stevens经典著作,两位顶级网络编程专家应邀执笔修订) 基本信息 原书名: Unix Network Programming, Vol ...

  6. python socket 域名_Python网络编程中的套接字名和DNS解析。

    距离上一次TCP的文章,这一次要讲的是套接字名和DNS,并且还会涉及到网络数据的发送接受和网络错误的发生和处理. 下面说套接字名,在创建和部署每个套接字对象时总共需要做5个主要的决定,主机名和IP地址 ...

  7. JavaSE(十四)——网络编程(IP地址、端口号、TCP、UDP)

    文章目录 1. 概述 2. InetAddress类 3. 端口 4. TCP协议 4.1 传输消息 4.2 文件上传 5. UDP协议 6. TCP与UDP区别 7. URL 8. 下载资源 9. ...

  8. 网络编程之通俗讲解,有了IP地址,为何还要用MAC地址?

    网络编程之通俗讲解,有了IP地址,为何还要用MAC地址? 前言 书上说的 基本概念: 什么是MAC地址? 什么是IP地址? 为什么要用到MAC地址? IP地址与MAC地址的区别是什么? MAC地址涉及 ...

  9. 网络通信协议(IP地址,端口号,InetAddress,UDP与TCP)

    一.计算机网络: 通过某种方式将多台计算机进行连接,实现多台计算机彼此之间的互联以及数据的交换.即在不同的计算机上编写一些实现了网络连接的程序,这些程序可以实现位于同一个网络中的计算机之间的数据的交换 ...

最新文章

  1. 数据分析 python 用途-Python 从爬虫到数据分析
  2. 【转】NHibernate入门教程
  3. 并不对劲的bzoj4816:loj2000:p3704[SDOI2017]数字表格
  4. [转] CMake入门
  5. es6 遍历数组对象获取所有的id_ES6对象遍历Object.keys()方法
  6. jquery解析XML
  7. 软件测试计划和测试报告
  8. POJ 2488 A Knight's Journey
  9. java digestutils.md5hex_linux下md5sum和DigestUtils.md5Hex的关系 博客分类: java
  10. java float 输出文本框_关于Java中float数输出时显示问题
  11. ubuntu20.04 nvidia 460显卡安装
  12. Android中使用OKHttp上传图片,从相机和相册中获取图片并剪切
  13. LocalDateTime的基本使用
  14. redis之数据倾斜如何处理
  15. word2013插入excel对象报错_教大家Excel2013如何插入对象文件
  16. colorkey唇釉是否安全_colorkey唇釉安全吗-colorkey唇釉真假辨别
  17. 北京工业大学计算机考研录取名单,2018年硕士研究生招生考试复试一志愿考生名单(信息学部)...
  18. 李开复:长尾效应带给媒体的不是威胁
  19. mini2440裸试验—计算器(LCD显示,触摸屏突破)
  20. 互联网的一些小知识点

热门文章

  1. 基于jquery框架实现以行的添加、上移、下移和删除操作
  2. java队列(Queue)用法总结
  3. Windows C++ CreateThread
  4. idea项目首次通过git上传到码云报错解决方法“push reject”
  5. 2.14 OrCAD中怎么运用表格创建复杂的元器件?
  6. PyCharm快捷键大全-你想要的全都有
  7. 初中python程序设计教学研究_初中Python程序设计教学方法初探
  8. 7种 JVM 垃圾收集器特点、优劣势及使用场景(多图)
  9. [364]尚未启动messenger服务,将不发送netsend通知
  10. 【MySQL】分组数据