【网络】socket套接字基础知识

2023-10-15 12:26:19 浏览数 (3)

IP与端口号

  • IP

每台主机都有自己的IP地址,所以当数据从一台主机传输到另一台主机就需要IP地址。报头中就会包含源IP和目的IP

源IP地址:发送数据报那个主机的IP地址,目的IP地址:想发送到的那个主机的IP地址

我们把数据从一台主机传递到另一台主机不是真正目的,真正通信的不是这两个机器,其实是这两台机器上面的软件

应用层不止一个软件公网IP标识了一台唯一的主机,那么数据就可以由一台主机传递到另一台主机。但是有这么多的软件(进程),怎么保证软件A发送的被软件B接收呢?也就是说用什么来标识主机上客户或者服务进程的唯一性呢?

为了更好的表示一台主机上服务进程的唯一性,用端口号port标识服务进程、客户端进程的唯一性。

  • 端口号

端口号是一个2字节16位的整数 端口号用来标识一个进程,告诉操作系统要把数据交给哪一个进程 一个端口号只能被一个进程占用(同一个主机)

由上面可以知道:

IP地址(标识主机全网唯一主机) 端口号(标识服务器上唯一的进程)能够标识网络上的某一台主机的某一个进程(全网唯一进程) 网络通信的本质就是进程间通信。而我们之前说过进程间通信的本质是看到同一份资源现在这个资源就是网络 通信的本质就是IO,因为我们上网的行为就两种情况:1.把数据发送出去 2.接收到数据。

标识一个进程有pid,那么为什么还需要端口号port呢?

解耦首先pid是系统规定的,而port是网络规定的,这样就可以把系统和网络解耦。 port标识服务器的唯一性不能做任何改变,要让客户端能找到服务器,就像110,120样不能被改变。而每次启动进程pid就会改变。 不是所有的进程都需要提供网络服务或请求(不需要port),但每个进程都需要pid。

一个端口号只能被一个进程占用,但是一个进程可以绑定多个端口号

底层OS如何根据port找到指定的进程——uint16(端口号)——task_struct——哈希

我们在网络通信的过程中,IP port标识唯一性,IP有源IP和目的IP,port也有源端口号和目的端口号。所以我们在发送数据的时候也要把自己的IP和端口号发送过去,因为数据还要被发送回来。所以发送数据的时候一定会多出一部分数据(以协议的形式呈现)

TCP/UDP协议

我们用的套接字接口一定会使用传输层协议,不会绕过传输层去调用下面的协议。而传输层的协议分为TCP协议和UDP协议

  • TCP协议

TCP(Transmission Control Protocol 传输控制协议) 特点:

传输层协议 有连接(正式通信前要先建立连接) 可靠传输(在内部帮我们做可靠传输工作) 面向字节流

UDP(User Datagram Protocol 用户数据报协议) 特点:

传输层协议 无连接 不可靠传输 面向数据报

注意:

理解不可靠传输:如发送数据时出现了丢包的情况、或者数据被重复传递了(传递了多份)、或者网络出现了问题等等造成的后果就叫做不可靠。所以传输层就是用来解决可靠性的一个协议。 可不可靠是一个中性词。可靠是需要成本的,往往在维护和编码上都比较复杂;而不可靠没有成本,使用起来也简单。所以要分场景使用。

网络字节流

我们知道内存中的多字节数据相对于内存地址有大端和小端之分。

小端:低权值的数放入低地址。(低低低) 大端:低权值的数放入高地址。

现在就出现一种情况:可能一个大端机用大端的方式发送数据到一个小端机。如果现在跨网络我们也不知道数据到底是大端和小端:

所以就有了规定:网络中的数据都是大端。 发送数据的主机如果是大端机就不用管,如果是小端机就把小端转成大端再发送。接收数据同理

如何定义网络数据流的地址:

发送主机把发送缓冲区中的数据按内存地址从低到高的顺序发出 接收主机把从网络上接到的字节依次保存在接收缓冲区中,也是按内存地址从低到高的顺序保存 也就是说先发出的数据是低地址,后发出的数据是高地址 TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节 不管这台主机是大端机还是小端机, 都会按照这个TCP/IP规定的网络字节序来发送/接收数据;如果当前发送主机是小端, 就需要先将数据转成大端; 否则就忽略, 直接发送即可

把数据转化成大端的工作可不需要我们自己来做,那太麻烦了,可以调用库函数做网络字节序和主机字节序的转换即可:

代码语言:javascript复制
#include <arpa/inet.h>
// 主机序列转网络序列
uint16_t htons(uint16_t hostshort);
uint32_t htonl(uint32_t hostlong);
// 网络序列转主机序列
uint16_t ntohs(uint16_t netshort);
uint32_t ntohl(uint32_t netlong);

**h表示host,n表示network,l表示32位长整数,s表示16位短整数。**主机是大端还是小端在函数内部会自己进行判断。 如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回。 如果主机是大端字节序,这些函数不做转换,将参数原封不动地返回。

如果是其他数据类型呢?不用担心,未来网络发送的时候所有数据都是字符串;发送数据时使用的原生接口对于字符串信息自动转化

socket套接字接口

  • socket常见API
代码语言:javascript复制
// 创建 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);

IP地址 端口号能够标识该主机上的唯一的一个进程:ip和端口号port就叫为套接字,socket就是插座的意思,未来进行网络通信时,插头和插座配套使用。

函数参数里面有个叫sockaddr的结构体类型,这个我们需要了解一下

0 人点赞