网络编程套接字

目录

  • 本节重点
  • 一、预备知识
    • 1.1 理解源IP地址和目的IP地址
    • 1.2 认识端口号
    • 1.3 理解 "端口号" 和 "进程ID"
    • 1.4 理解源端口号和目的端口号
    • 1.5 认识TCP协议
    • 1.6 认识UDP协议
    • 1.7 网络字节序
  • 二、socket编程接口
    • 2.1 socket常见的API
    • 2.2 sockaddr结构
    • 2.3 in_addr结构
    • 2.4 地址转换函数
    • 2.5 关于inet_ntoa
  • 三、Tcp协议通讯流程
  • 四、TCP和UDP的对比
  • 五、关于前台进程和后台进程的命令
    • 5.1 把进程放到后台去运行:&
    • 5.2 查看后台进程的命令:jobs
    • 5.3 把进程变成前台进程:fg + 编号
    • 5.4 把进程变成后台进程:bg + 编号
  • 六、网络编程套接字内容一览表

本节重点

1、认识IP地址, 端口号, 网络字节序等网络编程中的基本概念;
2、学习socket api的基本用法;
3、能够实现一个简单的udp客户端/服务器;
4、能够实现一个简单的tcp客户端/服务器(单连接版本, 多进程版本, 多线程版本);
理解tcp服务器建立连接, 发送数据, 断开连接的流程;

一、预备知识

1.1 理解源IP地址和目的IP地址

在IP数据包头部中, 有两个IP地址, 分别叫做源IP地址, 和目的IP地址。
思考: 我们光有IP地址就可以完成通信了嘛? 想象一下发qq消息的例子, 有了IP地址能够把消息发送到对方的机器上,
但是还需要有一个其他的标识来区分出, 这个数据要给哪个程序进行解析。

1.2 认识端口号

端口号(port)是传输层协议的内容。
1、端口号是一个2字节16位的整数;
2、端口号用来标识一个进程, 告诉操作系统, 当前的这个数据要交给哪一个进程来处理;
3、IP地址 + 端口号能够标识网络上的某一台主机的某一个进程;
4、一个端口号只能被一个进程占用。

1.3 理解 “端口号” 和 “进程ID”

在之前学习系统编程的时候, 学习了 pid 表示唯一一个进程; 此处我们的端口号也是唯一表示一个进程. 那么这两者之间是怎样的关系?
其实这两者是没有什么直接的关系的,只不过都是它们都是用来表示进程的唯一性的而已。那既然有pid能表示进程的唯一性了,那么为什么还要有端口号表示进程的唯一性呢?原因有两点,第一:pid是系统的层面的专有名词,而端口号是网络层面的专有名词,所以把它分开有利于系统和网络名词之间相互解耦,第二:还是解耦,因为 假如有一天不再用pid来唯一标识一个进程的时候,并不会影响到用端口号唯一标识一个进程。打个比方吧,假如对于一个大学生来说,他既是公民也是一个学生,那么每个公民都有自己的身份证来唯一标识一个人,而每个大学生在学校依然有一个学号来唯一标识一个大学生,所以这两个的原理是一样的。

另外, 一个进程可以绑定多个端口号; 但是一个端口号不能被多个进程绑定。

1.4 理解源端口号和目的端口号

传输层协议(TCP和UDP)的数据段中有两个端口号, 分别叫做源端口号和目的端口号. 就是在描述 “数据是谁发的, 要发给谁”。
例如送快递也是一样的,从商家的位置送到客户的位置。那么商家的位置就是源端口号,客户的位置就是目的端口号。

1.5 认识TCP协议

此处我们先对TCP(Transmission Control Protocol 传输控制协议)有一个直观的认识; 后面我们再详细讨论TCP的一些细节问题。
1、传输层协议
2、有连接
3、可靠传输
4、面向字节流

1.6 认识UDP协议

此处我们也是对UDP(User Datagram Protocol 用户数据报协议)有一个直观的认识; 后面再详细学习。
1、传输层协议
2、无连接
3、不可靠传输
4、面向数据报

1.7 网络字节序

我们已经知道,内存中的多字节数据相对于内存地址有大端和小端之分, 磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分, 网络数据流同样有大端小端之分. 那么如何定义网络数据流的地址呢?

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

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

3、因此,网络数据流的地址应这样规定:先发出的数据是低地址,后发出的数据是高地址.

4、TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节.

5、不管这台主机是大端机还是小端机, 都会按照这个TCP/IP规定的网络字节序来发送/接收数据;

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

在这里插入图片描述
为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换。
在这里插入图片描述
这些函数名很好记,h表示host,n表示network,l表示32位长整数,s表示16位短整数。
例如htonl表示将32位的长整数从主机字节序转换为网络字节序,例如将IP地址转换后准备发送。
如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回 ;
如果主机是大端字节序,这些 函数不做转换,将参数原封不动地返回。

二、socket编程接口

2.1 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);

2.2 sockaddr结构

socket API是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4、IPv6,以及后面会学习的UNIX Domain Socket。然而, 各种网络协议的地址格式并不相同。

在这里插入图片描述
1、IPv4和IPv6的地址格式定义在netinet/in.h中,IPv4地址用sockaddr_in结构体表示,包括16位地址类型, 16位端口号和32位IP地址.
2、IPv4、IPv6地址类型分别定义为常数AF_INET、AF_INET6. 这样,只要取得某种sockaddr结构体的首地址,不需要知道具体是哪种类型的sockaddr结构体,就可以根据地址类型字段确定结构体中的内容.
3、socket API可以都用struct sockaddr *类型表示, 在使用的时候需要强制转化成sockaddr_in; 这样的好处是程序的通用性, 可以接收IPv4, IPv6, 以及UNIX Domain Socket各种类型的sockaddr结构体指针做为参数;

struct sockaddr {unsigned short sa_family;  /* 地址族, AF_xxx */char sa_data[14];  /* 14字节的协议地址*/
};
struct sockaddr_in {short int sin_family; /* 地址族 */unsigned short int sin_port; /* 端口号 */struct in_addr sin_addr; /* Internet地址 */unsigned char sin_zero[8]; /* 与struct sockaddr一样的长度 */
};

虽然socket api的接口是sockaddr, 但是我们真正在基于IPv4编程时, 使用的数据结构是sockaddr_in; 这个结构里主要有三部分信息: 地址类型, 端口号, IP地址.

2.3 in_addr结构

在这里插入图片描述
in_addr用来表示一个IPv4的IP地址. 其实就是一个32位的整数;

2.4 地址转换函数

这里介绍的是基于IPv4的socket网络编程,sockaddr_in中的成员struct in_addr sin_addr表示32位 的IP 地址。
但是我们通常用点分十进制的字符串表示IP 地址,以下函数可以在字符串表示 和in_addr表示之间转换;

字符串转in_addr的函数:
在这里插入图片描述
in_addr转字符串的函数:
在这里插入图片描述
其中inet_pton和inet_ntop不仅可以转换IPv4的in_addr,还可以转换IPv6的in6_addr,因此函数接口是void* addrptr。

2.5 关于inet_ntoa

inet_ntoa这个函数返回了一个char*, 很显然是这个函数自己在内部为我们申请了一块内存来保存ip的结果. 那么是否需要调用者手动释放呢?
在这里插入图片描述
man手册上说, inet_ntoa函数, 是把这个返回结果放到了静态存储区. 这个时候不需要我们手动进行释放。

那么问题来了, 如果我们调用多次这个函数, 会有什么样的效果呢? 验证如下:
在这里插入图片描述
运行结果:
在这里插入图片描述
因为inet_ntoa把结果放到自己内部的一个静态存储区, 这样第二次调用时的结果会覆盖掉上一次的结果。

思考::如果有多个线程调用 inet_ntoa, 是否会出现异常情况呢?
1、在APUE中, 明确提出inet_ntoa不是线程安全的函数;
2、但是在centos7上测试, 并没有出现问题, 可能内部的实现加了互斥锁;
3、在多线程环境下, 推荐使用inet_ntop, 这个函数由调用者提供一个缓冲区保存结果, 可以规避线程安全问题;

测试多线程调用inet_ntoa的代码:

#include <iostream>
using namespace std;
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <strings.h>
#include <functional>
#include <cstring>
#include <unordered_map>
#include <pthread.h>void *func1(void *argv)
{struct sockaddr_in t1;t1.sin_addr.s_addr = 0;while (true){char *p1 = inet_ntoa(t1.sin_addr);cout << "thread1:" << p1 << endl;sleep(1);}return nullptr;
}void *func2(void *argv)
{struct sockaddr_in t1;t1.sin_addr.s_addr = 0xffffffff;while (true){char *p1 = inet_ntoa(t1.sin_addr);cout << "thread2:" << p1 << endl;sleep(1);}return nullptr;
}int main()
{// struct sockaddr_in t1;// struct sockaddr_in t2;// t1.sin_addr.s_addr=0;// t2.sin_addr.s_addr=0xffffffff;// char* p1=inet_ntoa(t1.sin_addr);// char* p2=inet_ntoa(t2.sin_addr);// cout<<p1<<endl;// cout<<p2<<endl;pthread_t tid1;pthread_t tid2;pthread_create(&tid1, nullptr, func1, nullptr);sleep(5);pthread_create(&tid2, nullptr, func2, nullptr);pthread_join(tid1, nullptr);pthread_join(tid2, nullptr);return 0;
}

在这里插入图片描述
在centos7上测试并没有出现问题,可能是内部实现加了互斥锁。

三、Tcp协议通讯流程

下图是基于TCP协议的客户端/服务器程序的一般流程:
在这里插入图片描述
一、服务器初始化:
1、调用socket, 创建文件描述符;
2、调用bind, 将当前的文件描述符和ip/port绑定在一起; 如果这个端口已经被其他进程占用了, 就会bind失败;
3、调用listen, 声明当前这个文件描述符作为一个服务器的文件描述符, 为后面的accept做好准备;
4、调用accecpt, 并阻塞, 等待客户端连接过来;
二、建立连接的过程:
1、调用socket, 创建文件描述符;
2、调用connect, 向服务器发起连接请求;
3、connect会发出SYN段并阻塞等待服务器应答; (第一次)
服务器收到客户端的SYN, 会应答一个SYN-ACK段表示"同意建立连接"; (第二次)
客户端收到SYN-ACK后会从connect()返回, 同时应答一个ACK段; (第三次)
这个建立连接的过程, 通常称为 三次握手;
三、数据传输的过程:
建立连接后,TCP协议提供全双工的通信服务; 所谓全双工的意思是, 在同一条连接中, 同一时刻, 通信双方可以同时写数据; 相对的概念叫做半双工, 同一条连接在同一时刻, 只能由一方来写数据;

服务器从accept()返回后立刻调用read(), 读socket就像读管道一样, 如果没有数据到达就阻塞等待;
这时客户端调用write()发送请求给服务器, 服务器收到后从read()返回,对客户端的请求进行处理, 在此期间客户端调用read()阻塞等待服务器的应答;
服务器调用write()将处理结果发回给客户端, 再次调用read()阻塞等待下一条请求;
客户端收到后从read()返回, 发送下一条请求,如此循环下去;

四、断开连接的过程:
如果客户端没有更多的请求了, 就调用close()关闭连接, 客户端会向服务器发送FIN段(第一次);
此时服务器收到FIN后, 会回应一个ACK, 同时read会返回0 (第二次);
read返回之后, 服务器就知道客户端关闭了连接, 也调用close关闭连接, 这个时候服务器会向客户端发送一个FIN; (第三次)
客户端收到FIN, 再返回一个ACK给服务器; (第四次)
这个断开连接的过程, 通常称为 四次挥手

在学习socket API时要注意应用程序和TCP协议层是如何交互的:
应用程序调用某个socket函数时TCP协议层完成什么动作,比如调用connect()会发出SYN段。
应用程序如何知道TCP协议层的状态变化,比如从某个阻塞的socket函数返回就表明TCP协议收到了某些段,再比如read()返回0就表明收到了FIN段。

四、TCP和UDP的对比

可靠传输 vs 不可靠传输
有连接 vs 无连接
字节流 vs 数据报

五、关于前台进程和后台进程的命令

5.1 把进程放到后台去运行:&

在这里插入图片描述

5.2 查看后台进程的命令:jobs

在这里插入图片描述

5.3 把进程变成前台进程:fg + 编号

在这里插入图片描述

5.4 把进程变成后台进程:bg + 编号

在这里插入图片描述

六、网络编程套接字内容一览表

在这里插入图片描述
以上就是今天想要跟大家分享的所有内容啦,你学会了吗?如果感觉到有所收获的话,那就点点赞点点关注呗,后期还会持续更新网络编程的相关知识哦,我们下期见!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/252449.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot集成axis发布WebService服务

文章目录 1、使用maven-web项目生成server-config.wsdd文件1.1、新建maven-web项目1.1.1、新建项目1.1.2、添加依赖 1.2、编写服务接口和实现类1.2.1、OrderService接口1.2.2、OrderServiceImpl实现类 1.3、配置deploy.wsdd文件deploy.wsdd文件 1.4、配置tomcat1.4.1、配置tomc…

MySQL数据库练习【一】

MySQL数据库练习【一】 一、建库建表-数据准备二、习题2.1. 查询部门编号为30的部门的员工详细信息2.2.查询从事clerk工作的员工的编号、姓名以及其部门号2.3.查询奖金多于基本工资的员工的信息、查询奖金小于基本工资的员工的信息2.4.查询奖金多于基本工资60%的员工的信息2.5.…

宠物空气净化器适合养猫家庭吗?除猫毛好的猫用空气净化器推荐

宠物掉毛是一个普遍存在的问题&#xff0c;尤其在脱毛季节&#xff0c;毛发似乎无处不在。这给家中的小孩和老人带来了很多麻烦&#xff0c;他们容易流鼻涕、过敏等不适。此外&#xff0c;宠物有时还会不规矩地拉扯和撒尿&#xff0c;这股气味实在是难以忍受。家人们对宠物的存…

【DC渗透系列】DC-4靶场

主机发现 arp-scan -l┌──(root㉿kali)-[~] └─# arp-scan -l Interface: eth0, type: EN10MB, MAC: 00:0c:29:6b:ed:27, IPv4: 192.168.100.251 Starting arp-scan 1.10.0 with 256 hosts (https://github.com/royhills/arp-scan) 192.168.100.1 00:50:56:c0:00:08 …

掌握Go的加密技术:crypto/rsa库的高效使用指南

掌握Go的加密技术&#xff1a;crypto/rsa库的高效使用指南 引言crypto/rsa 库概览RSA 加密算法基本原理crypto/rsa 库的功能和应用 安装和基本设置在 Go 项目中引入 crypto/rsa 库基本环境设置和配置 密钥生成与管理生成 RSA 密钥对密钥存储和管理 加密和解密操作使用 RSA 加密…

Kafka零拷贝技术与传统数据复制次数比较

读Kafka技术书遇到困惑: "对比传统的数据复制和“零拷贝技术”这两种方案。假设有10个消费者&#xff0c;传统复制方式的数据复制次数是41040次&#xff0c;而“零拷贝技术”只需110 11次&#xff08;一次表示从磁盘复制到页面缓存&#xff0c;另外10次表示10个消费者各自…

加固平板电脑丨三防智能平板丨工业加固平板丨智能城市管理

随着智能城市的不断发展&#xff0c;人们对于城市管理的要求也在不断提高&#xff0c;这就需要高效、智能的城市管理平台来实现。而三防平板就是一款可以满足这一需求的智能设备。 三防平板是一种集防水、防尘、防摔于一体的智能平板电脑&#xff0c;它可以在复杂的环境下稳定运…

【EI会议征稿通知】第三届智能控制与应用技术国际学术会议(AICAT 2024)

第三届智能控制与应用技术国际学术会议&#xff08;AICAT 2024&#xff09; 2024 3rd International Symposium on Artificial Intelligence Control and Application Technology 2024年第三届智能控制与应用技术国际学术会议&#xff08;AICAT 2024&#xff09;定于2024年5月…

Leetcode24:两两交换链表中的节点

一、题目 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能进行节点交换&#xff09;。 示例&#xff1a; 输入&#xff1a;head [1,2,3,4] 输出&#xff…

[Linux 进程控制(二)] 写时拷贝 - 进程终止

文章目录 1、写时拷贝2、进程终止2.1 进程退出场景2.1.1 退出码2.1.2 错误码错误码 vs 退出码2.1.3 代码异常终止引入 2.2 进程常见退出方法2.2.1 exit函数2.2.2 _exit函数 本片我们主要来讲进程控制&#xff0c;讲之前我们先把写时拷贝理清&#xff0c;然后再开始讲进程控制。…

JAVA面试汇总总结更新中ing

本人面试积累面试题 多线程微服务JVMKAFKAMYSQLRedisSpringBoot/Spring 1.面向对象的三个特征 封装&#xff0c;继承&#xff0c;多态&#xff0c;有时候也会加上抽象。 2.多态的好处 允许不同类对象对同一消息做出响应&#xff0c;即同一消息可以根据发送对象的不同而采用多种…

软考21-上午题-数组、矩阵

数组&#xff1a;一组地址连续的空间。 数组是定长线性表在维数上的扩展&#xff0c;即&#xff0c;线性表中的元素又是一个线性表。 一、数组 数组的特点&#xff1a; 数组数目固定&#xff0c;一旦定义了数组结构&#xff0c;不再有元素个数的增减变化。因此&#xff0c;数…

C# Onnx GroundingDINO 开放世界目标检测

目录 介绍 效果 模型信息 项目 代码 下载 介绍 地址&#xff1a;https://github.com/IDEA-Research/GroundingDINO Official implementation of the paper "Grounding DINO: Marrying DINO with Grounded Pre-Training for Open-Set Object Detection" 效果 …

MATLAB矩阵的操作(第二部分)

师从清风 矩阵的创建方法 在MATLAB中&#xff0c;矩阵的创建方法主要有三种&#xff0c;分别是&#xff1a;直接输入法、函数创建法和导入本地文件中的数据。 直接输入法 输入矩阵时要以中括号“[ ]”作为标识符号&#xff0c;矩阵的所有元素必须都在中括号内。 矩阵的同行元…

openssl3.2 - use openssl cmd create ca and p12

文章目录 openssl3.2 - use openssl cmd create ca and p12概述笔记实验的openssl环境建立CA生成私钥和证书请求生成CA证书用CA签发应用证书用CA对应用证书进行签名将已经签名好的PEM证书封装为P12证书验证P12证书是否可用END openssl3.2 - use openssl cmd create ca and p12 …

Redis(三)(实战篇)

查漏补缺 1.spring 事务失效 有时候我们需要在某个 Service 类的某个方法中&#xff0c;调用另外一个事务方法&#xff0c;比如&#xff1a; Service public class UserService {Autowiredprivate UserMapper userMapper;public void add(UserModel userModel) {userMapper.…

睿尔曼超轻量仿人机械臂—外置按钮盒使用说明

睿尔曼RM系列机械臂的控制方式有很多种&#xff0c;包括&#xff1a;示教器、JSON、API等。在此为大家介绍外置按钮盒的使用方法。 按钮盒接线安装 按钮盒外观如下图所示&#xff0c;有&#xff1a;急停、暂停、开始、继续。四个功能按钮。用户可通过这四个按钮来实现对机械臂运…

[嵌入式AI从0开始到入土]13_orangepi aipro开箱测评

[嵌入式AI从0开始到入土]嵌入式AI系列教程 注&#xff1a;等我摸完鱼再把链接补上 可以关注我的B站号工具人呵呵的个人空间&#xff0c;后期会考虑出视频教程&#xff0c;务必催更&#xff0c;以防我变身鸽王。 第1期 昇腾Altas 200 DK上手 第2期 下载昇腾案例并运行 第3期 官…

如何在电脑上恢复查看iPhone短信?4个有效方法给你!

在当今科技发达的世界&#xff0c;能够在计算机上查看 iPhone 短信将彻底改变游戏规则。无论是存档珍贵的对话还是管理与工作相关的聊天&#xff0c;这都是一项至关重要的技能。在本指南中&#xff0c;我们将引导您了解如何在计算机上查看 iPhone 短信的四种高效方法。通过执行…

1.0 Zookeeper 分布式配置服务教程

ZooKeeper 是 Apache 软件基金会的一个软件项目&#xff0c;它为大型分布式计算提供开源的分布式配置服务、同步服务和命名注册。 ZooKeeper 的架构通过冗余服务实现高可用性。 Zookeeper 的设计目标是将那些复杂且容易出错的分布式一致性服务封装起来&#xff0c;构成一个高…