Linux网络编程:Socket编程预备

目录

1.理解源IP地址和目的IP地址

2.认识端口号

端口号范围划分

理解"端口号"和"进程ID"

源端口号和目的端口号

理解socket

3.传输层的典型代表

TCP协议

UDP协议

4.网络字节序

5.socket编程接口

socket常见API

socket种类

sockaddr


1.理解源IP地址和目的IP地址

因特网上的每台计算机都有一个唯一的IP地址,如果一台主机上的数据要传输到另一台主机,那么对端主机的IP地址就应该作为该数据传输时的目的IP地址。但仅仅知道目的IP地址是不够的,当对端主机收到该数据后,对端主机还需要对该主机做出响应,因此对端主机也需要发送数据给该主机,此时对端主机就必须知道该主机的IP地址。因此一个传输的数据当中应该涵盖其源IP地址和目的IP地址,目的IP地址表明该数据传输的目的地,源IP地址作为对端主机响应时的目的IP地址。

在数据进行传输之前,会先自顶向下贯穿网络协议栈完成数据的封装,其中在网络层封装的IP报头当中就涵盖了源IP地址和目的IP地址。而除了源IP地址和目的IP地址之外,还有源MAC地址和目的MAC地址的概念。

源MAC地址和目的MAC地址是包含在链路层的报头当中的,而MAC地址实际只在当前局域网内有效,因此当数据跨网络到达另一个局域网时,其源MAC地址和目的MAC地址就需要发生变化,因此当数据达到路由器时,路由器会将该数据当中链路层的报头去掉,然后再重新封装一个报头,此时该数据的源MAC地址和目的MAC地址就发生了变化。


思考⼀个问题:数据传输到主机是⽬的吗?

不是的。因为数据是给⼈⽤的。⽐如:聊天是⼈在聊天,下载是⼈在下载,浏览⽹页是⼈在浏览?

但是⼈是怎么看到聊天信息的呢?怎么执⾏下载任务呢?怎么浏览⽹页信息呢?通过启动的 qq,迅雷,浏览器。⽽启动的 qq,迅雷,浏览器都是进程。换句话说,进程是⼈在系统中的代表,只要把数据给进程,⼈就相当于就拿到了数据。

所以:数据传输到主机不是⽬的,⽽是⼿段。到达主机内部,在交给主机内的进程,才是⽬的。

但是系统中,同时会存在⾮常多的进程,当数据到达⽬标主机之后,怎么转发给⽬标进程?这就要在⽹络的背景下,在系统中,标识主机的唯⼀性

2.认识端口号

端⼝号( port )是传输层协议的内容.

• 端⼝号是---个 2 字节 16 位的整数;

• 端⼝号⽤来标识---个进程, 告诉操作系统, 当前的这个数据要交给哪---个进程来处理;

IP地址 + 端⼝号能够标识⽹络上的某---台主机的某---个进程;

• ---个端⼝号只能被---个进程占⽤(端口到进程唯一映射,相当于哈希表中端口号是key,进程PCB是value)

端口号范围划分

• 0 - 1023 : 知名端⼝号, HTTP, FTP, SSH 等这些⼴为使⽤的应⽤层协议, 他们的端⼝号都

是固定的.(这个就当与端口界的119,110)

• 1024 - 65535 : 操作系统动态分配的端⼝号. 客户端程序的端⼝号, 就是由操作系统从这个范

围分配的.

理解"端口号"和"进程ID"

我们之前在学习系统编程的时候, 学习了 pid 表⽰唯⼀个进程; 此处我们的端⼝号也是唯⼀表⽰⼀个进程. 那么这两者之间是怎样的关系?

问题1:pid已经能够标识一台主机上进程的唯一性了,为什么要需要搞一个端口号?

(1)首先从技术角度绝对是可以的,但是如果我们把网络和系统都用这个pid,那么一旦系统改了,网络也要跟着改(牵一发而动全身),所以不如单独设计一套专属于网络的数据来让系统和网络功能解耦(有点像生活中,我们有唯一的身份证,但是在学校要有学号,在公司要有工号) ,也就是说,我们存在的意义相似,但这并不代表我就得和你一样。

进程 PID 属于系统概念,技术上也具有唯⼀性,确实可以⽤来标识唯⼀的⼀个进程,但是这样做,会让系统进程管理和⽹络强耦合,实际设计的时候,并没有选择这样做。

(2)不是所有的进程都需要网络通信,但是所有的进程都要有pid。
问题2:客户端怎么知道服务端的端口号?

所以端口号必须是众所周知、精心设计、被用户所知晓的!!app和服务端都是一个公司开发的,所以端口号肯定提前已经被内置进去了,我们用户并不需要关心,我们只需要知道打开了这个app然后发送请求,对方就一定可以收到 所以客户端默认必须得知道服务端的端口号,所以无法通信!!
问题3:一个进程可以绑定多个端口号么?一个端口号可以绑定多个进程么?

一个进程可以绑定多个端口号!但是一个端口号不能绑定多个进程!因为我们只是想在网络通信的时候能通过端口号找到进程就行了!端口号到进程具有唯一性。

源端口号和目的端口号

传输层协议( TCP 和 UDP )的数据段中有两个端⼝号, 分别叫做源端⼝号和⽬的端⼝号. 就是在描述 "数据是谁发的, 要发给谁";

理解socket

• 综上, IP 地址⽤来标识互联⽹中唯⼀的⼀台主机, port ⽤来标识该主机上唯⼀的⼀个⽹络进程

• IP+Port 就能表⽰互联⽹中唯⼀的⼀个进程

• 所以,通信的时候,本质是两个互联⽹进程代表⼈来进⾏通信,{srcIp,srcPort,dstIp,dstPort}

这样的4元组就能标识互联⽹中唯⼆的两个进程

• 所以,⽹络通信的本质,也是进程间通信

• 我们把 ip+port 叫做套接字 socket

3.传输层的典型代表

如果我们了解了系统,也了解了⽹络协议栈,我们就会清楚,传输层是属于内核的,那么我们要通

过⽹络协议栈进⾏通信,必定调⽤的是传输层提供的系统调⽤,来进⾏的⽹络通信。

TCP协议

此处我们先对 TCP ( Transmission Control Protocol 传输控制协议)有⼀个直观的认识; 后

⾯我们再详细讨论TCP的⼀些细节问题.

• 传输层协议

• 有连接

• 可靠传输

• ⾯向字节流

UDP协议

此处我们也是对 UDP ( User Datagram Protocol ⽤户数据报协议)有⼀个直观的认识; 后⾯再详

细讨论.

• 传输层协议

• ⽆连接

• 不可靠传输

• ⾯向数据报

因为我们暂时还没有深⼊了解 tcp 、 udp 协议,此处只做了解即可

问题1:有连接无连接怎么理解?

这里的连接其实就是我们之后要谈到的"握手",有连接就像是两个人打电话都喜欢先"喂"一句,这是为了确保连接上了才能进行通信。

无连接就是无握手过程,就好比发送消息不需要确保对方在不在,对方有没有收到消息。

问题2:可靠传输/不可靠传输怎么理解?

问题3:为什么tcp看起来比udp好这么多,那为啥udp还得存在呢??

注:计算机中很多词语都是中性的,并无褒贬之意(比如可靠和不可靠)只不过是在描述他的特性而已。不同特性可以最优匹配不同场景。

可靠是有成本的,而不可靠会更简单,比如tcp为了可靠所以要能够制定一个策略能够知道数据包是否丢失然后进行重传,而你在数据包丢出之后在还没确保传输成功之前你必然会把报文信息在传输层一直维护着,否则你拿什么重传呢??又或者需要重传几次呢??万一数据乱序了你是不是还得排序、编序号?? 而udp就是一拿到数据包就转手往下扔,他也懒得维护报文信息,因为他压根不关心数据包的发送情况。

比如像支付系统就得是TCP的,这是不允许出错的

实时多媒体传播为了减少延迟会采用UDP即使丢失1%数据包也丝毫不影响观感,如果采用TCP重传可能就会导致400ms延迟。还有电竞比赛的数据也会采用UDP。

问题4:面向字节流,面向数据报怎么理解?

一句话总结

面向字节流(TCP)像持续流动的水管,发送和接收的数据是连续的字节序列,没有固定边界;面向数据报(UDP)像独立快递包裹,每个发送/接收的数据包都是明确分隔的完整单元。

核心区别

TCP不保证应用层写入和读出的数据块一一对应(可能合并/拆分),而UDP严格保证每次sendto的数据必定是独立完整的recvfrom消息。

4.网络字节序

我们已经知道,内存中的多字节数据相对于内存地址有大端和小端之分, 磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分

网络数据流同样有大端小端之分. 那么如何定义网络数据流的地址呢?

• 发送主机通常将发送缓冲区中的数据按内存地址从低到⾼的顺序发出;

• 接收主机把从⽹络上接到的字节依次保存在接收缓冲区中,也是按内存地址从低到⾼的顺序保存;

• 因此,⽹络数据流的地址应这样规定: 先发出的数据是低地址,后发出的数据是⾼地址.

• TCP/IP协议规定,⽹络数据流应采⽤⼤端字节序,即低地址⾼字节.

• 不管这台主机是⼤端机还是⼩端机, 都会按照这个TCP/IP规定的⽹络字节序来发送/接收数据;

• 如果当前发送主机是⼩端, 就需要先将数据转成⼤端; 否则就忽略, 直接发送即可;


为使⽹络程序具有可移植性,使同样的C代码在⼤端和⼩端计算机上编译后都能正常运⾏,可以调⽤以下库函数做⽹络字节序和主机字节序的转换。

• 这些函数名很好记, h 表⽰ host , n 表⽰ network , l 表⽰ 32 位⻓整数, s 表⽰ 16 位短整数。

• 例如 htonl 表⽰将 32 位的⻓整数从主机字节序转换为⽹络字节序,例如将IP地址转换后准备发送。

• 如果主机是⼩端字节序,这些函数将参数做相应的⼤⼩端转换然后返回。

• 如果主机是⼤端字节序,这些函数不做转换,将参数原封不动地返回。
注:

网络规定,所有发送到网络上的数据都必须是大端的!

5.socket编程接口

socket常见API

// 创建 socket ⽂件描述符 (TCP/UDP, 客⼾端 + 服务器 )
int socket ( int domain, int type, int protocol);
// 绑定端⼝号 (TCP/UDP, 服务器 )
int bind ( int socket, const struct sockaddr *address, socklen_t address_len);
// 开始监听 socket (TCP, 服务器 )
int listen ( int socket, int backlog);
// 接收请求 (TCP, 服务器 )
int accept ( int socket, struct sockaddr* address, socklen_t * address_len);
// 建⽴连接 (TCP, 客⼾端 )
int connect ( int sockfd, const struct sockaddr *addr, socklen_t addrlen);

可以看到很多参数都是sockaddr*类型

socket种类

Socket是网络通信的端点,相当于程序间的"电话插座",通过IP地址+端口号实现不同主机进程间的数据收发。

种类如下:

1、域间套接字(同一个机器内) struct sockaddr_un

2、原始套接字(网络工具)

原始套接字一般不关心传输层的东西,他一般是绕过传输层去考虑网络层和链路层,所以他一般被用来封装一些网络工具:比如网络装包、网络诊断......

3、网络套接字(用户间通信)struct sockaddr_in

sockaddr

而sockaddr就是为了统一套接字接口的,使用时会根据他的前16位来分辨他是哪一种类型的套接字,然后做一个强转(这一切对于用户来说是透明的),他们之间的关系就像是基类(sockaddr)和子类(sockaddr_in,sockaddr_un)那样。

问题:为什么要用sockaddr这个结构,用void*不好吗?C语言不是经常用他来做任意类型转换吗?然后我们再用short强转一下不就拿到前面的数据了吗??

因为网络接口出来的时候C语言的标准还没有void* 之后再想改也很难改回来了。= ̄ω ̄=

---------------------------------------------------------END------------------------------------------------------------------

相关推荐
迎風吹頭髮4 小时前
Linux服务器编程实践22-TCP头部选项解析:MSS、窗口扩大因子与SACK
服务器·网络·tcp/ip
deng-c-f4 小时前
Linux C/C++ 学习日记(25):KCP协议:普通模式与极速模式
linux·学习·kcp
Net_Walke4 小时前
【Linux系统】系统编程
linux·运维·服务器
_dindong4 小时前
Linux网络编程:宏观网络体系
linux·网络·笔记·学习
想不明白的过度思考者4 小时前
JavaEE初阶——TCP/IP协议栈:从原理到实战
java·网络·网络协议·tcp/ip·java-ee
人邮异步社区4 小时前
内网攻防实战图谱:从红队视角构建安全对抗体系
网络·安全·web安全
猫林老师4 小时前
OpenHarmony南向开发环境搭建 - 深入理解Ubuntu、DevEco Device Tool与HPM
linux·运维·ubuntu·harmonyos·openharmony
碱化钾5 小时前
学习笔记——GPU训练
笔记·学习
爱装代码的小瓶子5 小时前
Linux下的权限与文件
linux·运维·服务器