Linux网络服务器编程:TCP与UDP详解

文章目录

    • 一、TCP与UDP概述
      • 1.1 TCP的原理
      • 1.2 UDP的原理
      • 1.3 数据流动
    • 二、Socket的使用
      • 2.1 TCP Socket示例
      • 2.2 UDP Socket示例
    • 三、数据流动时序图
      • 3.1 TCP通信详解
      • 3.2 UDP通信详解
    • 四、异常情况处理
      • 4.1 服务器ACK丢失
      • 4.2 第三次握手的ACK丢失
    • 五、总结
    • 推荐阅读

Linux网络服务器编程中,TCP和UDP是两种主要的传输层协议。本文将详细分析TCP和UDP在服务器编程中的使用、原理、代码示例、数据流动,以及一些异常情况的处理方式。

一、TCP与UDP概述

1.1 TCP的原理

TCP是一种面向连接的协议,它通过三次握手建立连接,然后在连接上进行可靠的数据传输。TCP使用序列号和确认应答(ACK)来保证数据的可靠传输,通过滑动窗口和拥塞控制算法进行流量控制和拥塞控制。

1.2 UDP的原理

相比于TCP,UDP是一种更简单的协议。UDP是无连接的,它直接在IP协议之上发送数据报,不提供数据的可靠传输、流量控制或拥塞控制。因此,UDP的延迟和开销较小,适用于对实时性要求高的应用,如语音和视频通信。

1.3 数据流动

在TCP和UDP通信中,数据是从客户端流向服务器的。客户端首先建立连接(TCP)或直接发送数据报(UDP),然后服务器接收并处理这些数据,可能会返回响应给客户端。在TCP通信中,数据的流动是双向的,客户端和服务器都可以发送数据和接收数据。在UDP通信中,数据的流动也是双向的,但是由于UDP是无连接的,客户端和服务器可以独立地发送和接收数据。

二、Socket的使用

在Linux网络服务器编程中,我们使用socket来实现TCP和UDP通信。以下是TCP和UDP的socket使用示例:

2.1 TCP Socket示例

服务器端:

#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
#include <iostream>int main() {int server_fd = socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in server_addr;server_addr.sin_family = AF_INET;server_addr.sin_port = htons(8080);server_addr.sin_addr.s_addr = INADDR_ANY;bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr));listen(server_fd, 5);while (true) {struct sockaddr_in client_addr;socklen_t client_addr_len = sizeof(client_addr);int client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_addr_len);char buffer[1024];ssize_t read_len = read(client_fd, buffer, sizeof(buffer) - 1);buffer[read_len] = '\0';std::cout << "Received: " << buffer << std::endl;write(client_fd, buffer, strlen(buffer));close(client_fd);}close(server_fd);return 0;
}

客户端:

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <iostream>int main() {int client_fd = socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in server_addr;server_addr.sin_family = AF_INET;server_addr.sin_port = htons(8080);inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr);connect(client_fd, (struct sockaddr *)&server_addr, sizeof(server_addr));const char *message = "Hello, Server!";write(client_fd, message, strlen(message));char buffer[1024];ssize_t read_len = read(client_fd, buffer, sizeof(buffer) - 1);buffer[read_len] = '\0';std::cout << "Received: " << buffer << std::endl;close(client_fd);return 0;
}

在Linux网络编程中,socket(), sockaddr_in 结构体和相关常量都是用于创建和配置套接字的关键组件。以下是上面代码的含义和用法:

  • AF_INET:这是一个地址族(Address Family)常量,表示我们使用的是IPv4协议。在创建套接字时,需要指定地址族以确定使用哪种协议。另一个常见的地址族是AF_INET6,表示使用IPv6协议。

  • SOCK_STREAM:这是一个套接字类型(Socket Type)常量,表示我们使用的是面向连接的、可靠的字节流。在TCP协议中,我们使用SOCK_STREAM类型的套接字。另一个常见的套接字类型是SOCK_DGRAM,表示无连接的、不可靠的数据报文,通常用于UDP协议。

  • socket(AF_INET, SOCK_STREAM, 0):这是一个系统调用,用于创建一个新的套接字。它接受三个参数:地址族(如AF_INET)、套接字类型(如SOCK_STREAM)和协议(通常设置为0,让系统自动选择协议,如TCP或UDP)。此函数返回一个套接字文件描述符,用于后续的网络操作。

  • struct sockaddr_in:这是一个用于表示IPv4套接字地址的结构体。它包含了地址族、端口号和IPv4地址。在网络编程中,我们需要使用此结构体来设置服务器和客户端的地址信息。

  • server_addr.sin_family = AF_INET:设置sockaddr_in结构体中的地址族字段为AF_INET,表示使用IPv4协议。

  • server_addr.sin_port = htons(8080):设置sockaddr_in结构体中的端口号字段。htons()函数将主机字节序(Host Byte Order)转换为网络字节序(Network Byte Order)。这里我们设置端口号为8080。

  • INADDR_ANY:这是一个特殊的IPv4地址(0.0.0.0),表示服务器将监听所有可用的网络接口。当服务器有多个网络接口时,使用INADDR_ANY可以让服务器接受来自任何接口的连接请求。

  • server_addr.sin_addr.s_addr = INADDR_ANY:设置sockaddr_in结构体中的IPv4地址字段为INADDR_ANY,表示服务器将监听所有可用的网络接口。

2.2 UDP Socket示例

服务器端:

#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
#include <iostream>int main() {int server_fd = socket(AF_INET, SOCK_DGRAM, 0);struct sockaddr_in server_addr;server_addr.sin_family = AF_INET;server_addr.sin_port = htons(8080);server_addr.sin_addr.s_addr = INADDR_ANY;bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr));while (true) {char buffer[1024];struct sockaddr_in client_addr;socklen_t client_addr_len = sizeof(client_addr);ssize_t read_len = recvfrom(server_fd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&client_addr, &client_addr_len);buffer[read_len] = '\0';std::cout << "Received: " << buffer << std::endl;sendto(server_fd, buffer, strlen(buffer), 0, (struct sockaddr *)&client_addr, client_addr_len);}close(server_fd);return 0;
}

客户端:

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <iostream>int main() {int client_fd = socket(AF_INET, SOCK_DGRAM, 0);struct sockaddr_in server_addr;server_addr.sin_family = AF_INET;server_addr.sin_port = htons(8080);inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr);const char *message = "Hello, Server!";sendto(client_fd, message, strlen(message), 0, (struct sockaddr *)&server_addr, sizeof(server_addr));char buffer[1024];struct sockaddr_in recv_addr;socklen_t recv_addr_len = sizeof(recv_addr);ssize_t read_len = recvfrom(client_fd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&recv_addr, &recv_addr_len);buffer[read_len] = '\0';std::cout << "Received: " << buffer << std::endl;close(client_fd);return 0;
}

三、数据流动时序图

以下是TCP和UDP通信的时序图,展示了客户端与服务器之间的数据流动。

3.1 TCP通信详解

在TCP通信中,我们首先需要建立一个TCP连接,然后才能在这个连接上进行数据传输。以下是TCP通信的详细步骤和时序图:

  1. 服务器执行socket()函数,创建一个新的套接字。
  2. 服务器执行bind()函数,将套接字绑定到一个指定的地址(包括IP地址和端口号)。
  3. 服务器执行listen()函数,使套接字进入监听模式,等待客户端的连接请求。
  4. 服务器执行accept()函数,阻塞并等待客户端的连接请求。当一个客户端连接请求到来时,accept()函数返回,并创建一个新的套接字与客户端进行通信。
  5. 客户端执行socket()connect()函数,向服务器发起连接请求。connect()函数会发送一个SYN(同步)数据包到服务器。
  6. 服务器收到SYN数据包,在accept()函数返回后,回复一个SYN+ACK(确认应答)数据包给客户端。
  7. 客户端收到SYN+ACK数据包,回复一个ACK数据包给服务器,完成TCP连接的建立。
  8. TCP连接建立后,客户端和服务器可以通过read()write()函数进行数据传输。

以下是TCP通信的时序图:

Server                Client|                     || socket()            ||                     || bind()              ||                     || listen()            ||                     || accept()            ||                     ||--等待客户端连接请求--->||                     ||                     || socket(), connect() | |<--- SYN ------------||                     ||-- SYN + ACK ------->||                     ||<--- ACK ------------||                     ||<-- Data ------------|| read(), write()     ||						||-- Data -----------> || read(), write()     ||                     |

3.2 UDP通信详解

与TCP不同,UDP是一种无连接的协议,客户端和服务器不需要建立连接就可以直接发送数据。以下是UDP通信的详细步骤:

  1. 服务器执行socket()函数,创建一个新的套接字。
  2. 服务器执行bind()函数,将套接字绑定到一个指定的地址(包括IP地址和端口号)。
  3. 客户端执行socket()函数,创建一个新的套接字。
  4. 客户端可以直接通过sendto()函数发送数据到服务器。
  5. 服务器通过recvfrom()函数接收客户端发送的数据。

以下是UDP通信的时序图:

Server                Client|                     || socket()            ||                     || bind()              ||                     ||----等待客户端数据---->||                     ||                     ||   			socket()|| 			sendto()| |<--- Data -----------|| recvfrom()          ||                     |

在这种情况下,服务器已经准备好接受客户端的数据。当客户端执行socket()sendto()函数发送数据时,服务器会通过recvfrom()函数接收这些数据。

四、异常情况处理

在网络通信中,可能会遇到一些异常情况,如TCP握手过程中服务器ACK丢失、第三次握手的ACK丢失等。以下是这些异常情况的处理方式:

4.1 服务器ACK丢失

当服务器发送的ACK丢失时,客户端将无法收到确认,因此会重新发送SYN。服务器在收到重复的SYN后,会再次发送ACK。这个过程会持续进行,直到客户端收到ACK或达到最大重传次数。

4.2 第三次握手的ACK丢失

当第三次握手的ACK丢失时,服务器可能仍在等待客户端的ACK。然而,客户端已经认为连接建立,可能会开始发送数据。服务器在收到客户端的数据后,会认为连接已建立,并更新连接状态。因此,即使第三次握手的ACK丢失,TCP连接仍然可以正常建立。

五、总结

本文详细讨论了Linux网络服务器编程中TCP和UDP两种方式的socket使用、原理分析、代码示例、数据流动时序图,以及一些异常情况的处理方式。理解这些概念和技巧有助于更高效地进行网络服务器编程,应对各种网络通信场景。

推荐阅读

TCP与UDP:网络协议的技术原理与要点
从HTTP到QUIC:网络协议的演进与优化
HTTPS:原理、使用方法及安全威胁

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

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

相关文章

win11 如何把微软账户切换成administrator

windows11系统第一次进来需要绑定微软账户&#xff0c;当然也不是必须的。但是微软搞得不明所以。 如果真的是绑定了微软账户&#xff0c;也是有好处的&#xff0c;可以在微软的账户里看到绑定的设备以及定位&#xff08;没做深究是否准确&#xff09; 如果要去掉微软账户换成a…

设计模式:备忘录模式示例

文章目录 示例一定义备忘录类&#xff08;Memento&#xff09;定义发起人类&#xff08;Originator&#xff09;定义看护人类&#xff08;Caretaker&#xff09;使用备忘录模式 示例二定义备忘录类&#xff08;Memento&#xff09;定义发起人类&#xff08;Originator&#xff…

力扣 | 148. 排序链表

和数组里面的归并排序思想一致 class Solution {public ListNode sortList(ListNode head) {//过滤条件if(head null || head.next null)return head;ListNode slow head;ListNode fast head.next;while (fast ! null && fast.next ! null){slow slow.next;fast …

Golang基础-12

Go语言基础 介绍 目录操作 创建 删除 重命名 遍历目录 修改权限 文件操作 创建 打开关闭 删除 重命名 修改权限 读文件 写文件 文件定位 拷贝 测试 单元测试 基准测试 示例 介绍 本文介绍Go语言中目录操作&#xff08;创建目录、删除目录、重命名、遍历…

c++的学习之路:20、继承(1)

摘要 本章主要是讲以一下继承的一些概念以及使用方法等等。 目录 摘要 一、继承的概念及定义 1、继承的概念 2、继承定义 1.2.1、定义格式 1.2.2、继承关系和访问限定符 1.2.3、继承基类成员访问方式的变化 3、总结 二、基类和派生类对象赋值转换 三、继承中的作用…

Matlab自学笔记二十九:元胞数组的创建、索引和转换方法

1.概念 元胞数组&#xff08;cell array&#xff09;是一种具有容器特性的数据类型&#xff0c;每个元素可以包含任何类型的数据&#xff1b; 2.创建方法 &#xff08;1&#xff09;方法一&#xff1a;使用大括号{}&#xff1b; &#xff08;2&#xff09;方法二&#xff1…

9【原型模式】复制一个已存在的对象来创建新的对象

你好&#xff0c;我是程序员雪球。 今天我们来学习23种设计模式之原型模式&#xff0c;在平时开发过程中比较少见。我带你了解什么是原型模式&#xff0c;使用场景有哪些&#xff1f;有什么注意事项&#xff1f;深拷贝与浅拷贝的区别&#xff0c;最后用代码实现一个简单的示例…

大数据深度学习:基于Tensorflow深度学习卷积神经网络CNN算法垃圾分类识别系统

文章目录 大数据深度学习&#xff1a;基于Tensorflow深度学习卷积神经网络CNN算法垃圾分类识别系统一、项目概述二、深度学习卷积神经网络&#xff08;Convolutional Neural Networks&#xff0c;简称CNN&#xff09;三、部分数据库架构四、系统实现系统模型部分核心代码模型训…

Android之图片压缩几种方式

大概可以分为以下几类&#xff1a;更换图片格式&#xff0c;质量压缩&#xff0c;采样率压缩&#xff0c;缩放压缩&#xff0c;调用jpeg压缩等 1.设置图片格式 Android目前常用的图片格式有png&#xff0c;jpeg和webp&#xff0c; png&#xff1a;无损压缩图片格式&#xff0…

【Java】新手一步一步安装 Java 语言开发环境

文章目录 一、Windows 10 系统 安装 JDK8二、 Mac 系统 安装 JDK8三、IDEA安装 一、Windows 10 系统 安装 JDK8 &#xff08;1&#xff09;打开 JDK下载网站&#xff0c;根据系统配置选择版本&#xff0c;这里选择windows 64位的版本&#xff0c;点击下载&#xff08;这里需要…

5.SpringSpringBoot八股

Spring,Spring MVC,Spring Boot 之间什么关系? Spring就是整个Spring框架的整体&#xff0c;包含AOP、JDBC、Spring MVC等等模块 SpringBoot是Spring的精简版&#xff0c;它在Spring的基础上添加了自动装配、内置tomcat服务器等功能&#xff0c;使得代码量更少&#xff0c;同…

Finetuning vs. Prompting:大语言模型两种使用方式

目录 前言1. 对于大型语言模型的两种不同期待2. Finetune(专才)3. Prompt(通才)3.1 In-context Learning3.2 Instruction-tuning3.3 Chain of Thought(COT) Prompting3.4 用机器来找Prompt 总结参考 前言 这里和大家分享下关于大语言模型的两种使用方式&#xff0c;一种是 Fine…

2024最新 PyCharm 2024.1 更新要点汇总

2024最新 PyCharm 2024.1 更新要点汇总 文章目录 2024最新 PyCharm 2024.1 更新要点汇总摘要引言 Hugging Face&#xff1a;模型和数据集的快速文档预览针对 JavaScript 和 TypeScript 的全行代码补全 PyCharm Professional编辑器中的粘性行编辑器内代码审查新终端 Beta新的 AI…

ip连接检测

1.在C#中&#xff0c;你可以使用Socket类来检测IP连接是否正常。以下是一个简单的示例代码&#xff0c;演示了如何使用Socket类来检测IP连接的可用性&#xff1a; using System; using System.Net; using System.Net.Sockets;class Program {static void Main(string[] args){…

js+网络摄像头实现人体肢体关键点动作捕获

最近有一个项目&#xff0c;客户需要用户人体姿势识别&#xff0c;进行表演考核用途&#xff0c;或者康复中心用户恢复护理考核&#xff0c;需要用摄像头进行人体四肢进行肢体关键点对比考核&#xff0c;资料还是太少了。只有个别大佬发了部分技术指导。感觉写的不错。 阿里云…

【微信小程序——案例——本地生活(列表页面)】

案例——本地生活&#xff08;列表页面&#xff09; 九宫格中实现导航跳转——以汽车服务为案例&#xff08;之后可以全部实现页面跳转——现在先实现一个&#xff09; 在app.json中添加新页面 修改之前的九宫格view改为navitage 效果图&#xff1a; 动态设置标题内容—…

PTA(题目集三 题目 代码 C++ 注解)

目录 题目一&#xff1a; 代码&#xff1a; 题目二&#xff1a; 代码&#xff1a; 题目三&#xff1a; 代码&#xff1a; 题目四&#xff1a; 代码&#xff1a; 题目五&#xff1a; 代码&#xff1a; 题目六&#xff1a; 代码&#xff1a; 题目七&#xff1a; 代码…

【QT+QGIS跨平台编译】161:【qgispython跨平台编译】—【qgis_python.h生成】

点击查看专栏目录 文章目录 一、qgis_python.h介绍二、信息分析三、qgis_python.h生成一、qgis_python.h介绍 qgis_python.h 是 QGIS(Quantum Geographic Information System)GIS 软件的一个头文件。QGIS 是一个开源的地理信息系统软件,提供了丰富的地图制图和空间分析功能。…

第四百五十九回

文章目录 1. 概念介绍2. 方法与细节2.1 获取方法2.2 使用细节 3. 示例代码4. 内容总结 我们在上一章回中介绍了"如何获取当前系统语言"相关的内容&#xff0c;本章回中将介绍如何获取时间戳.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介绍 我们在本章…

Arduino IDE开发esp8266,如何调试

在Arduino IDE中开发ESP8266时,调试通常需要依赖于串行输出(Serial.print 语句)和LED指示灯。由于ESP8266没有像一些高级开发平台那样的内置调试功能(例如JTAG或SWD接口),所以调试可能较为基础。以下是一些用于Arduino IDE中ESP8266开发调试的基本步骤: 环境准备:首先,…