【linux网络(二)】网络基础之套接字编程

news2024/11/30 14:29:05

💓博主CSDN主页:杭电码农-NEO💓

⏩专栏分类:Linux从入门到精通⏪

🚚代码仓库:NEO的学习日记🚚

🌹关注我🫵带你学更多操作系统知识
  🔝🔝


在这里插入图片描述

Linux网络

  • 1. 前言
  • 2. 端口号详解
  • 3. 认识TCP/UDP协议
  • 4. 对网络字节序的理解
  • 5. socket套接字API
  • 6. 套接字编程
  • 7. 总结

1. 前言

Linux网络部分,挺长时间没更新了, 秋招在即, 这篇文章就当是对网络知识的复习, 让我们一起进入网络的时间

本章重点:

本篇文章会认识, 端口号, 网络字节序等网络编程中的基本概念. 会带大家初识TCP和UDP协议. 并且通过套接字编程, 带大家实现一个简单的TCP/UDP服务器


2. 端口号详解

我们知道,一台机器可以启动多个服务. 那么当客户端拿到IP地址来访问你机器时, 你机器上有这么多个服务, 我怎么知道客户端想要访问哪个? 所以说我们需要一个字段来标识一台机器上的唯一一个服务(进程)

在这里插入图片描述

端口号(port)就是用来表示唯一一个服务的
IP + port可以标识全网唯一的服务

端口号(port)是传输层协议的内容:

  • 端口号是一个2字节16位的整数;
  • 端口号用来标识一个进程, 告诉操作系统, 当前的这个数据要交给哪一个进程来处理;
  • IP地址 + 端口号能够标识网络上的某一台主机的某一个进程;
  • 一个端口号只能被一个进程占用.

端口号和进程pid的关系:

在学习Linux系统时,学到过进程id可以用来标识唯一的一个进程. 那么为什么网络服务不直接使用pid来标识唯一的服务(进程)呢? 答案是: 1. 一个端口号只能绑定一个进程, 但一个进程可以绑定到多个端口号上 2. 解耦合, 端口号是传输层到应用层寻找服务时需要使用的字段, 而进程的pid往往用于操作系统管理不同的进程


3. 认识TCP/UDP协议

我们先对TCP和UDP有一个大概的认识,在后面再详细讲解它的协议内容:

TCP协议:

  • 传输层协议
  • 有连接: 通信前需要先建立连接
  • 可靠传输: TCP协议有一些措施来保证传输的可靠性
  • 面向字节流

UDP协议:

  • 传输层协议
  • 无连接: 通信前不需要建立连接
  • 不可靠传输: 无措施来保证可靠性
  • 面向数据报

对于连接性的解释:

TCP通信前需要先建立连接(也就是大名鼎鼎的TCP三次握手,后面会讲). 而UDP通信什么都不用提前做. 我们可以把TCP通信比喻为打电话, 想和你通信必须先经过你的同意. 而把UDP通信比喻为寄快递, 我不需要经过你的同意,我只需要知道你的地址就可以无脑给你寄快递

对于可靠性的解释:

TCP是可靠的,而UDP是不可靠的, 那么在实际生活中我们用TCP就行了啊,为什么还要有UDP协议的存在呢? 相信聪明的你也能想到, TCP的可靠性是通过一定的策略实现的, 所以一定就证明了TCP是比UDP要复杂的, 并且从效率上来说, TCP和UDP谁快谁慢还不一定. 所以在不同场景下, 用到的协议也不同. 比如微信发送信息时, 我们一定不希望消息在网络传输中丢失了, 所以大概率会选择TCP协议. 而当我们在直播上网课时, 及时有部分包丢失在网络中,也不会对整个直播有太大的影响, 所以这时往往会选择UDP协议

对于面向字节流/数据报的解释:

什么是面向字节流? 意思就是TCP协议在发送数据时, 不管一次性发送多少数据, 也不管数据一共要发送几次,它只关心能尽快的将数据从客户端发送到服务器. 所以说TCP在发送数据时, 完整的数据可能是: “abcdefg123456"但是第一次可能发送了"abcdefg1”,第二次再发送"234",再发送"56". 这都是不定的. 而UDP是面向数据报的, 它每次发送数据时, 会将完整的数据全部保存在一个报文中, 然后将这个数据报整体发送过去

所以TCP会有粘包问题,后面会讲


4. 对网络字节序的理解

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

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

在这里插入图片描述

可使用系统调用做大小端字节序的交换

在这里插入图片描述


5. socket套接字API

在这里插入图片描述

socket函数的作用是创建套接字, 套接字的本质是一个文件描述符, 这个套接字会在后续中起重要作用. 第二个函数bind, 它的作用是: 将本服务的IP和端口号绑定到操作系统内部,供外部来访问. 而最后三个函数: listen和accept是TCP通信中需要用到的, 它们表示: listen用于开始监听是否有请求到来. accept用于将到来的请求拿到内存当中做解析. 而connect函数用于发送TCP请求的一方调用,与服务器建立连接

socket函数详解:

  • 第一个参数代表套接字的类型,是个宏. AF_INET代表ipv4,最常用
    在这里插入图片描述

  • 第二个参数代表通信的类型,是个宏. SOCK_DGRAM代表UDP. SOCK_STREAM代表TCP
    在这里插入图片描述

  • 第三个参数设置为0即可

后续的函数看后面的代码就能懂, 如有不懂,欢迎私信


6. 套接字编程

由于socket套接字编程比较复杂, 所以这里只提供TCP的示例:

服务器

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <unistd.h>  
#include <arpa/inet.h>  
#define BUF_SIZE 1024  
#define PORT 8080  
int main() {  
    int server_fd, new_socket;  
    struct sockaddr_in address;  
    int opt = 1;  
    int addrlen = sizeof(address);  
    char buffer[BUF_SIZE] = {0};  
    const char *hello = "Hello from server";  
    // 创建socket文件描述符  
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {  
        perror("socket failed");  
        exit(EXIT_FAILURE);  
    }  
    // 设置socket选项  
    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {  
        perror("setsockopt");  
        exit(EXIT_FAILURE);  
    }  
    address.sin_family = AF_INET;  
    address.sin_addr.s_addr = INADDR_ANY;  
    address.sin_port = htons(PORT);  
    // 绑定socket到端口  
    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address))<0) {  
        perror("bind failed");  
        exit(EXIT_FAILURE);  
    }  
  
    // 开始监听  
    if (listen(server_fd, 3) < 0) {  
        perror("listen");  
        exit(EXIT_FAILURE);  
    }  
    // 等待客户端连接  
    if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen))<0) {  
        perror("accept");  
        exit(EXIT_FAILURE);  
    }  
    // 发送一些数据  
    write(new_socket, hello, strlen(hello));  
    // 读取客户端数据并回显  
    while ((read(new_socket, buffer, BUF_SIZE - 1)) > 0) {  
        // 发送数据回客户端  
        write(new_socket, buffer, strlen(buffer));  
        memset(buffer, 0, BUF_SIZE);  
    }  
    if (read(new_socket, buffer, 0) < 0) {  
        perror("read failed");  
    }  
    // 关闭连接  
    close(new_socket);  
    close(server_fd);  
    return 0;  
}

客户端

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <unistd.h>  
#include <arpa/inet.h>  
  
#define BUF_SIZE 1024  
#define SERVER_IP "127.0.0.1" // 服务器IP地址,这里使用本地回环地址  
#define SERVER_PORT 8080        // 服务器端口号,与服务器设置的端口一致  
  
int main() {  
    int sockfd;  
    struct sockaddr_in server_addr;  
    char buffer[BUF_SIZE] = {0};  
    char *message = "Hello from client";  
    // 创建socket文件描述符  
    sockfd = socket(AF_INET, SOCK_STREAM, 0);  
    if (sockfd < 0) {  
        perror("socket creation failed");  
        exit(EXIT_FAILURE);  
    }  
    memset(&server_addr, 0, sizeof(server_addr));  
    // 配置服务器地址信息  
    server_addr.sin_family = AF_INET;  
    server_addr.sin_port = htons(SERVER_PORT);  
    if (inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr) <= 0) {  
        perror("Invalid server address");  
        exit(EXIT_FAILURE);  
    }  
    // 连接到服务器  
    if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {  
        perror("connection failed");  
        exit(EXIT_FAILURE);  
    }  
    // 接收服务器的欢迎消息  
    read(sockfd, buffer, BUF_SIZE - 1);  
    printf("Server: %s\n", buffer);  
    // 向服务器发送消息  
    write(sockfd, message, strlen(message));  
    printf("Client: %s\n", message);  
    // 读取服务器的响应  
    memset(buffer, 0, BUF_SIZE); // 清空缓冲区以便接收新数据  
    read(sockfd, buffer, BUF_SIZE - 1);  
    printf("Server echo: %s\n", buffer);  
    // 关闭连接  
    close(sockfd);  
    return 0;  
}

7. 总结

网络套接字编程是掌握网络至关重要的一步, 学会了这个网络编程的流程和底层逻辑, 下次遇见其他语言封装好的网络编程函数,你甚至能想象到它底层是如何实现的. 所以学技能不能只学接口,要学底层原理和系统调用, 这些你看所有封装好了的函数都是透明的


🔎 下期预告:HTTP协议详解 🔍

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vs、utf-8、utf-8bom乱码分析及实测

1、系统默认控制台命令行编码 windows命令行默认的编码是ANSI&#xff0c;中文系统下则就是GBK&#xff0c;GBK是对GB2312编码的扩展兼容GB2312&#xff0c;可以等同理解为就是GB2312。 2、vs2022默认新建项目编码 vs默认项目文件编码格式为UTF-8 BOM 默认字符集 Unicode 最终…

MNIST数据集导出

MNIST数据集导出 文章目录 MNIST数据集导出1、 MNIST数据集介绍2、 MNIST数据集下载2.1 使用Pytorch自带的MNIST数据集 3、 MNIST数据集解析3.1 训练集图片文件解析规则3.2 训练集标签文件解析规则3.3 测试集图片文件解析规则3.4 测试集标签文件解析规则 4、 MNIST数据集转图片…

动手学深度学习31 深度学习硬件 CPU和GPU

动手学深度学习31 深度学习硬件 CPU和GPU CPU和GPU主频 QA PPT&#xff1a; https://courses.d2l.ai/zh-v2/assets/pdfs/part-2_1.pdf 视频&#xff1a; https://www.bilibili.com/video/BV1TU4y1j7Wd/?p2&spm_id_frompageDriver&vd_sourceeb04c9a33e87ceba9c9a2e5f09…

nginx ws长连接配置

nginx ws长连接配置 http根节点下配上 map $http_upgrade $connection_upgrade {default upgrade; close;}如下&#xff1a; server服务节点下&#xff0c;后端接口的代理配置 proxy_http_version 1.1;proxy_set_header Upgrade $http_upgrade;proxy_set_header Connec…

2. 面向对象编程推导

1. 面向过程编程 面向过程编程(Procedure-Oriented Programming, POP): 是一种关注于解决问题步骤或过程的编程范式.面向过程编程核心思想: 将复杂问题分解为一系列简单, 可执行的步骤(即过程或函数), 并按照特定的顺序依次执行这些步骤, 直到问题得到解决. 每个步骤(过程或函…

compose for desktop

then 叠加修饰符功能的作用 val reusableModifier Modifier.fillMaxWidth().background(Color.Red).padding(12.dp)// Append to your reusableModifier reusableModifier.clickable { /*...*/ }// Append your reusableModifier otherModifier.then(reusableModifier)https:…

springboot物流管理系统-计算机毕业设计源码00781

摘要 信息化社会内需要与之针对性的信息获取途径&#xff0c;但是途径的扩展基本上为人们所努力的方向&#xff0c;由于站在的角度存在偏差&#xff0c;人们经常能够获得不同类型信息&#xff0c;这也是技术最为难以攻克的课题。针对物流管理系统等问题&#xff0c;对如何通过计…

时间处理获取交易日(考虑兼容性问题)

在获取交易日时间的处理上&#xff0c;出现了苹果14不兼容的问题&#xff0c;就这个问题记录下。 一、获取交易日的代码 封装了一个js文件&#xff0c;在untils目录下&#xff0c;先看代码&#xff0c;然后我讲下思路。 // 获取节假日数据 import { getCalendarHolidays } …

使用脚手架创建vue2项目(关闭eslint语法检查 、运行项目时自动打开网址、src文件夹简写方法)

使用脚手架创建vue2项目会默认安装的插件&#xff08;eslint) 这个插件是检查语法的。 假设我们在main.js中定义了一个变量&#xff0c;没有使用 eslint 就会检测出错误 &#xff08;事实是我们并没有写错而是eslint 给我们判断是错的&#xff0c;所以这样会很麻烦&#xff…

怎么监视员工电脑屏幕?电脑监控软件监控屏幕的六个步骤

监视员工电脑屏幕通常涉及使用专门的电脑监控软件&#xff0c;这些软件设计用于帮助企业管理人员合法合规地监督员工的工作状态、提高工作效率并确保信息安全。以下是实施员工电脑屏幕监视的一般步骤和注意事项&#xff1a; 1. 选择合适的监控软件 首先&#xff0c;选择一款适…

Idea多线程调试

在 IntelliJ IDEA 中调试多线程应用程序可能会有些复杂&#xff0c;因为多个线程可能会同时运行和交互。不过&#xff0c;IDEA 提供了一些强大的工具来帮助你进行多线程调试。以下是一些关键步骤和技巧&#xff0c;帮助你有效地调试多线程应用程序&#xff1a; 创建一个示例多线…

查分易成绩查询入口

今天我来分享一个老师超实用的小技巧&#xff0c;那就是如何用查分易来打造一个专属的成绩查询入口哦&#xff01;无论是我们勤奋的学生们&#xff0c;还是关心孩子学习的家长们&#xff0c;都可以轻松查到自己的成绩信息。来来来&#xff0c;让我来一步步教你怎么用查分易搞定…

优雅迷人的小程序 UI 风格

优雅迷人的小程序 UI 风格

基于Python的信号处理(包络谱,低通、高通、带通滤波,初级特征提取,机器学习,短时傅里叶变换)及轴承故障诊断探索

Python是一种广泛使用的解释型、高级和通用的编程语言&#xff0c;众多的开源科学计算软件包都提供了Python接口&#xff0c;如计算机视觉库OpenCV、可视化工具库VTK等。Python专用计算扩展库&#xff0c;如NumPy、SciPy、matplotlab、Pandas、scikit-learn等。 开发工具上可用…

20240612每日前端-------vue3实现聊天室(一)

先上效果图 讲讲布局设计 聊天室大致分三块&#xff1a; 左边导航右边聊天界面主界面 单独调整一下样式&#xff1a;外层friend-box先调整布局为flex&#xff0c;这样方便进行自适应布局&#xff0c;增加背景色为白色&#xff0c;设置边框圆角使得外观更加美观&#xff0c;使…

树状数组:解锁快速排名的高效利器

文章目录 引言一、快速排名问题概述二、树状数组的应用树状数组概述数据结构初始化查询排名更新排名示例代码 总结参考 引言 在大规模数据排名问题中&#xff0c;树状数组可以用来高效地实现快速排名查询和更新操作&#xff0c;特别是在处理动态变化的数据集时。使用树状数组可…

如何舒适的使用VScode

安装好VScode后通常会很不好用&#xff0c;以下配置可以让你的VScode变得好用许多。 VScode的配置流程 1、设置VScode中文2、下载C/C拓展&#xff0c;使代码可以跳转3、更改编码格式4、设置滚轮缩放5、设置字体6、设置保存自动改变格式7、vscode设置快捷代码 1、设置VScode中文…

Android studio如何导入项目

打开解压好的安装包 找到build.gradle文件 打开查看gradle版本 下载对应的gradle版本Index of /gradle/&#xff08;镜像网站&#xff09; 下载all的对应压缩包 配置gradle的环境变量 新建GRADLE_HOME 将GRADLE_HOME加入到path中 将项目在Android studio中打开进行配置 将gr…

手撕设计模式——计划生育之单例模式

1.业务需求 ​ 大家好&#xff0c;我是菠菜啊。80、90后还记得计划生育这个国策吗&#xff1f;估计同龄的小伙伴们&#xff0c;小时候常常被”只生一个好“”少生、优生“等宣传标语洗脑&#xff0c;如今国家已经放开并鼓励生育了。话说回来&#xff0c;现实生活中有计划生育&…

【Kadane】Leetcode 918. 环形子数组的最大和【中等】

环形子数组的最大和 给定一个长度为 n 的环形整数数组 nums &#xff0c;返回 nums 的非空 子数组 的最大可能和 。 环形数组 意味着数组的末端将会与开头相连呈环状。形式上&#xff0c; nums[i] 的下一个元素是 nums[(i 1) % n] &#xff0c;nums[i] 的前一个元素是 nums…