IP地址、端口号、网络字节序

本文中部分名词可见: 协议、TCP/IP模型、数据包封装与分用
一.IP地址
1. 简单认识
    IP协议有两个版本:IPv4和IPv6。
        (1)IP地址在IP协议中,用来标识网络中不同主机的地址。
        (2)对于IPv4来讲,IP地址是一个4字节,32位的整数; 对于IPv6来讲,IP地址是一个16字节,128位的整数。
         (3)我们常用“点分十进制“的字符串表示IP地址,例如:192.168.2.13,其中用点分割的每一个数字表示一个字节,范围是0-255。
2. 源IP地址与目的IP地址
        在IP数据的包头信息中,有两个IP地址,分别叫做源IP地址、目的IP地址。
        因为,我们不仅要知道数据是谁发的,也要知道数据是要发给谁的。
二. 端口号  
        我们仅仅知道IP地址,知道该把数据发送给哪台机器是远不够的。我们还需要知道这些数据是要发给机器 哪个程序进行解析,所以我们需要一个其他的标识,所以就有了“端口号”。
1. 简单认识
    端口号是传输层协议的内容。
        (1)端口号是一个2字节16位的整数;
        (2)端口号用来标识一个进程,告诉操作系统,当前的数据要交给哪一个进程来处理;
        (3)所以“IP地址+端口号”即套接字socket可以唯一地标识某一台主机上的某一个进程;
        (4)一个端口号只能被一个进程占用。
2. 区分“端口号”和“进程ID”
        之前,我们学进程章节的时候,也说过进程ID可以表示唯一的一个进程。拿这两者的不同在什么地方呢?
       举个例子吧,比如学号和身份证号。 进程的PID相当于一个人的身份证号,在任何地方都唯一标识某个人,而端口号相当于一个学号,它只在学校里来唯一标识一个学生。因此,PID无论在哪里都可以唯一标识一个进程,而端口号只在网络通信中来唯一标识一个进程。
        另外, 一个进程可以绑定 多个端口号,因为很多源主机可以通过不同的端口号来找到目的主机中的同一进程来处理不同的数据。 但是一个端口号不能被多个进程绑定,如果一个端口号绑定了多个进程,那多个进程都对同一数据进行处理,那要采取那个进程返回的结果呢,我们无法确定。
3. 源端口号和目的端口号
        传输层协议(TCP和UDP)的数据段中有两个端口号,分别叫做源端口号和目的端口号。分别用于描述:数据是谁发的,数据要发给谁。
三. 网络字节序
         当我们将源主机中的数据发送给目的主机时,是如何发送的呢?
         根据冯诺依曼体系结构,我们知道源主机要通过内存将数据先发给源主机的外设(网卡),然后发送到网络中,目的主机将网络中的数据项接收到目的主机的网卡上,再传入到目的主机的内存中,然后通过CPU对数据进行处理。
         而数据在内存中的存储有大小端之分。如果在源主机的内存为小端,而目的主机以大端的形式来接受数据时,此时,就会导致数据错误。而不仅不同主机的大小端不同,在网络序列中也有大小端之分。这样一来,就更加混乱了。
         所以,TCP/IP协议规定,网络数据流均采用大端,即低地址处放数据的高位
       如果源主机为小端则在发送数据时应先转为大端,如果是大端,则不用转化。然后将大端数据存放在发送缓冲区中,直接发送到网络中即可。

        当目的主机在接受消息时,先将网络中的大端数据存储到接受缓冲区中。如果目的主机是小端,则将大端数据转换为小端存储到内存中,如果目的主机是大端则不用转换直接存储到内存中。

同时,有以下规定:
        发送主机通常把发送缓冲区中的数据按内存地址从低到高的顺序发出;
        接收 主机把从网络上接到的字节依次保存在接收缓冲区中,也是按内存地址从低到高的顺序保存;
        因此,网络数据流的地址 应该这样规定先发出的数据是低地址,后发出的数据是高地址
所以,我们有以下库函数做网络字节序和主机字节序的转换:
[html]  view plain  copy
  1. #include <arpa/inet.h>  
  2. uint32_t htonl(uint32_t hostlong);  
  3. uint16_t htons(uint16_t hostshort);  
  4. uint32_t ntohl(uint32_t netlong);  
  5. uint16_t ntohs(uint16_t netshort);  
        其中,h代表host即主机,n表示network即网络,l表示32长整数,s表示16位短整数。所以比如htonl函数是将32位的长整型从主机字节序转换为网络字节序,其他函数类似。
        若主机是小端字节序,这些函数将参数做相应的大小端转换然后返回;  若主机是小端字节序,这些函数将不做转换,将参数原封不动地返回。

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在 C 语言中,可以使用 `getsockname()` 函数从套接字中获取 IP 地址和端口号。`getsockname()` 函数的原型如下: ```c #include <sys/types.h> #include <sys/socket.h> int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen); ``` 下面是一个示例代码,演示如何通过套接字获取 IP 地址和端口号: ```c #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> int main() { // 创建套接字 int sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd == -1) { perror("socket"); exit(1); } // 绑定地址和端口号 struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = htons(8000); // 设置端口号 if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { perror("bind"); exit(1); } // 获取 IP 地址和端口号 struct sockaddr_in local_addr; socklen_t addrlen = sizeof(local_addr); getsockname(sockfd, (struct sockaddr *)&local_addr, &addrlen); char ip[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &(local_addr.sin_addr), ip, INET_ADDRSTRLEN); printf("IP 地址: %s\n", ip); int port = ntohs(local_addr.sin_port); printf("端口号: %d\n", port); // 关闭套接字 close(sockfd); return 0; } ``` 在上述示例中,我们首先创建了一个套接字 `sockfd`,然后将其绑定到本地地址和端口号 `8000` 上。接下来,我们使用 `getsockname()` 函数获取套接字的本地地址和端口号,并将其存储在 `local_addr` 结构体中。然后,我们使用 `inet_ntop()` 函数将 IP 地址从网络字节序转换为点分十进制的字符串,并将其存储在 `ip` 数组中。最后,我们通过 `ntohs()` 函数将端口号网络字节序转换为主机字节序,并将其打印出来。 请注意,上述代码只是一个示例,需要你根据实际情况进行适当的修改和错误处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值