TCP通信流程以及套接字函数

TCP和UDP的特点: 

对于单播,多播,广播的解释:
  • 单播: 一对一通信,目标地址唯一。
  • 多播: 一对多通信,目标地址是一个特定的组。
  • 广播: 一对全部通信,目标地址是整个网络。

UDP不可靠的原因:

UDP(用户数据报协议)被认为是不可靠的主要是因为它缺乏一些保证数据可靠性的机制,这使得在传输过程中可能出现数据丢失、乱序或重复的情况。以下是导致UDP不可靠的一些主要原因:

1.  无连接性: UDP是一种无连接的协议,不需要在通信的两端建立和维护连接。这虽然提高了传输效率,但也意味着在传输过程中不进行状态检查和确认。

2.  不提供流控制: UDP不提供流控制机制,因此发送方会以最大速率发送数据,而不考虑接收方的处理能力。这可能导致数据包丢失或过载

3.  不提供重传机制: 在UDP中,如果一个数据包在传输过程中丢失,协议本身不提供重传机制。这意味着丢失的数据包将不会被自动重新发送,而是由应用层来处理。

4.  不提供顺序控制: UDP也不保证数据包的传输顺序。数据包可能以不同的顺序到达接收方,这需要应用层来处理,特别是对于需要按顺序处理的数据。

5.  不提供拥塞控制: UDP不具备拥塞控制机制,因此在网络拥塞时可能导致数据包丢失。与TCP不同,UDP没有慢启动、拥塞避免等机制。

尽管UDP不可靠,但它的简单性和低开销使其在某些场景下非常有用,特别是对于实时性要求较高、对延迟敏感的应用,如音频和视频传输、在线游戏等。在这些情况下,轻量级的UDP可以提供更低的延迟,而可靠性方面则由应用层来处理

TCP通信流程:

 

// TCP 通信的流程
// 服务器端 (被动接受连接的角色)
1. 创建一个用于监听的套接字
    - 监听:监听有客户端的连接
    - 套接字:这个套接字其实就是一个文件描述符
2. 将这个监听文件描述符和本地的IP和端口绑定(IP和端口就是服务器的地址信息)
- 客户端连接服务器的时候使用的就是这个IP和端口
3. 设置监听,监听的fd开始工作
4. 阻塞等待,当有客户端发起连接,解除阻塞,接受客户端的连接,会得到一个和客户端通信的套接字
(fd)
5. 通信
        - 接收数据
        - 发送数据
6. 通信结束,断开连接
// 客户端
1. 创建一个用于通信的套接字(fd)
2. 连接服务器,需要指定连接的服务器的 IP 和 端口
3. 连接成功了,客户端可以直接和服务器通信
    - 接收数据
    - 发送数据
4. 通信结束,断开连接

 

套接字函数:

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h> // 包含了这个头文件,上面两个就可以省略
int socket(int domain, int type, int protocol);
    - 功能:创建一个套接字
    - 参数:
        - domain: 协议族
            AF_INET : ipv4
            AF_INET6 : ipv6
            AF_UNIX, AF_LOCAL : 本地套接字通信(进程间通信)
        - type: 通信过程中使用的协议类型 // 流式协议和报式协议都不止一个协议,所以需要具体的协议
            SOCK_STREAM : 流式协议
            SOCK_DGRAM : 报式协议
        - protocol : 具体的一个协议。一般写0
             - SOCK_STREAM : 流式协议默认使用 TCP
             - SOCK_DGRAM : 报式协议默认使用 UDP
        - 返回值:
             - 成功:返回文件描述符,操作的就是内核缓冲区。
             - 失败:-1

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); // socket命
名
    - 功能:绑定,将fd 和本地的IP + 端口进行绑定
    - 参数:
            - sockfd : 通过socket函数得到的文件描述符
            - addr : 需要绑定的socket地址,这个地址封装了ip和端口号的信息
            - addrlen : 第二个参数结构体占的内存大小
    - 返回值:
             - 成功:0
             - 失败:-1,并设置错误号

int listen(int sockfd, int backlog); // /proc/sys/net/core/somaxconn
    - 功能:监听这个socket上的连接
    - 参数:
            - sockfd : 通过socket()函数得到的文件描述符
            - backlog : 未连接的和已经连接的和的最大值, 5(不能超过机器规定好的最大值)
    - 返回值:
             - 成功:0
             - 失败:-1,并设置错误号

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    - 功能:接收客户端连接,默认是一个阻塞的函数,阻塞等待客户端连接
    - 参数:
            - sockfd : 用于监听的文件描述符
            - addr : 传出参数,记录了连接成功后客户端的地址信息(ip,port)
            - addrlen : 指定第二个参数的对应的内存大小
    - 返回值:
            - 成功 :用于通信的文件描述符(服务端套接字中的文件描述符只是用来监听的)
            - -1 : 失败

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    - 功能: 客户端连接服务器
    - 参数:
            - sockfd : 用于通信的文件描述符
            - addr : 客户端要连接的服务器的地址信息
            - addrlen : 第二个参数的内存大小
    - 返回值:成功 0, 失败 -1

ssize_t write(int fd, const void *buf, size_t count); // 写数据

ssize_t read(int fd, void *buf, size_t count); // 读数据

对两种协议的解释:

  1. 流式协议(Stream-oriented Protocol):

    • 定义: 流式协议是一种将数据视为连续流(流式数据)的通信方式。数据被视为一串连续的字节,没有明确的分割点。
    • 例子: TCP(传输控制协议)是流式协议的一个典型例子。在TCP通信中,数据被视为流动的字节序列,而不是独立的消息。
  2. 报式协议(Message-oriented Protocol):

    • 定义: 报式协议是一种将数据划分为独立的消息或报文的通信方式。每个消息都是一个完整的数据单元,有明确的起始和结束。
    • 例子: UDP(用户数据报协议)通常被认为是报式协议,因为它将数据划分为独立的数据包(报文),每个数据包都是一个完整的消息。

测试代码:

服务端:

// TCP 通信的服务器端

#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

int main() {

    // 1.创建socket(用于监听的套接字)
    int lfd = socket(AF_INET, SOCK_STREAM, 0);

    if(lfd == -1) {
        perror("socket");
        exit(-1);
    }

    // 2.绑定
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    // inet_pton(AF_INET, "172.22.2.64", saddr.sin_addr.s_addr);
    saddr.sin_addr.s_addr = INADDR_ANY;  // 0.0.0.0,表示任意的地址
    saddr.sin_port = htons(9999);
    int ret = bind(lfd, (struct sockaddr *)&saddr, sizeof(saddr));

    if(ret == -1) {
        perror("bind");
        exit(-1);
    }

    // 3.监听
    ret = listen(lfd, 8);
    if(ret == -1) {
        perror("listen");
        exit(-1);
    }

    // 4.接收客户端连接
    struct sockaddr_in clientaddr;
    int len = sizeof(clientaddr);
    int cfd = accept(lfd, (struct sockaddr *)&clientaddr, &len);
    
    if(cfd == -1) {
        perror("accept");
        exit(-1);
    }

    // 输出客户端的信息
    char clientIP[16];
    inet_ntop(AF_INET, &clientaddr.sin_addr.s_addr, clientIP, sizeof(clientIP));
    unsigned short clientPort = ntohs(clientaddr.sin_port);
    printf("client ip is %s, port is %d\n", clientIP, clientPort);

    // 5.通信
    char recvBuf[1024] = {0};
    while(1) {
        
        // 获取客户端的数据
        int num = read(cfd, recvBuf, sizeof(recvBuf));
        if(num == -1) {
            perror("read");
            exit(-1);
        } else if(num > 0) {
            printf("recv client data : %s\n", recvBuf);
        } else if(num == 0) {
            // 表示客户端断开连接
            printf("clinet closed...");
            break;
        }

        char * data = "hello,i am server";
        // 给客户端发送数据
        write(cfd, data, strlen(data));
    }
   
    // 关闭文件描述符
    close(cfd);
    close(lfd);

    return 0;
}

输出结果如下:

客户端的端口号通常是动态分配的,并且由操作系统自动选择一个可用的端口。因此,客户端自身的端口号不一定与服务端的端口号相同。 所以输出的不是9999端口

客户端: 

// TCP通信的客户端

#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

int main() {

    // 1.创建套接字
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if(fd == -1) {
        perror("socket");
        exit(-1);
    }

    // 2.连接服务器端
    struct sockaddr_in serveraddr;
    serveraddr.sin_family = AF_INET;
    inet_pton(AF_INET, "172.22.2.64", &serveraddr.sin_addr.s_addr);
    serveraddr.sin_port = htons(9999);
    int ret = connect(fd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));

    if(ret == -1) {
        perror("connect");
        exit(-1);
    }

    
    // 3. 通信
    char recvBuf[1024] = {0};
    while(1) {

        char * data = "hello,i am client";
        // 给客户端发送数据
        write(fd, data , strlen(data));

        sleep(1);
        
        int len = read(fd, recvBuf, sizeof(recvBuf));
        if(len == -1) {
            perror("read");
            exit(-1);
        } else if(len > 0) {
            printf("recv server data : %s\n", recvBuf);
        } else if(len == 0) {
            // 表示服务器端断开连接
            printf("server closed...");
            break;
        }

    }

    // 关闭连接
    close(fd);

    return 0;
}

输出结果如下:

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

vue3之 websoket发送消息

1.封装websoket var ws null; //建立的连接 var lockReconnect false;//是否真正建立连接 var timeout 6 * 1000 * 5;//30秒一次心跳 var timeoutObj null;//心跳心跳倒计时 var serverTimeoutObj null;//心跳倒计时 var timeoutnum null;//断开 重连倒计时 var global_…

win10安装redis并配置加自启动(采用官方推荐unix子系统)

记录&#xff0c;为啥有msi安装包&#xff0c;还这么麻烦的用linux版本redis的安装方式&#xff0c;是因为从github上下载别人制作的msi报毒&#xff0c;还不止一处&#xff0c;这种链接数据库的东西&#xff0c;用别人加工过的&#xff0c;都报毒了还用就是傻逼了。 所以采用…

Linux---文件系统

在基础IO中&#xff0c;我们所讲的都是对被打开文件的管理&#xff0c;但是不是所有的文件都是被打开的&#xff0c;对那些在磁盘中保存的没有被打开的文件&#xff0c;我们同样也需要管理&#xff0c;这个就像是快递站中等待被人取走的快递&#xff0c;我们需要将它们分门别类…

赋值运算符

注意点&#xff1a;复合赋值运算符&#xff0c;会进行强制类型转换&#xff0c;不会报错 byte b 2; b 3; b; b 2; b为byte类型 b 3; 等价于b b 3;而b3的结果为int类型&#xff1b; 但在此过程中存在强制类型转换&#xff0c;b(byte)(b3);因而不会报错

电脑提示找不到opencl.dll无法继续执行的多种解决方法,实测有效

Opencl.dll文件的丢失可能会引发一系列系统运行与软件功能上的问题。作为一款重要的动态链接库文件&#xff0c;Opencl.dll在计算机中扮演着关键角色&#xff0c;它主要负责支持和实现OpenCL&#xff08;开放运算语言&#xff09;标准&#xff0c;该标准允许程序能够利用多种不…

C/C++ LeetCode:跳跃问题

个人主页&#xff1a;仍有未知等待探索-CSDN博客 专题分栏&#xff1a;算法_仍有未知等待探索的博客-CSDN博客 题目链接&#xff1a;45. 跳跃游戏 II - 力扣&#xff08;LeetCode&#xff09; 一、题目 给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。 每个元…

eNSP学习——利用三层交换机实现VLAN间路由

目录 背景 实验内容 实验目的 实验步骤 实验拓扑 实验编址 实验步骤 基本配置 配置三层交换机实现VLAN间通信 背景 虽说单臂路由可以实现不同VLAN之间主机的通信&#xff0c;但该技术存在一些局限性&#xff0c;比如带宽、转发效率等。 三层交换机在原有二层交换机…

备忘录模式-C#实现

该实例基于WPF实现&#xff0c;直接上代码&#xff0c;下面为三层架构的代码。 目录 一 Model 二 View 三 ViewModel 一 Model using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace 设计模式练…

阅读go语言工具源码系列之gopacket(谷歌出品)----第二集 layers-巧妙的抽象与无聊的协议包

上一集中我们讲到了wpcap.dll的go封装方法&#xff0c;对于linux系统下libpcap的go封装采用的是常用的cgo方式&#xff0c;想了解的可以看看pcap文件夹中的pcap_unix.go。 我们得到了wpcap.dll的go调用&#xff0c;就可以利用它来进行列举所有网络设备&#xff0c;例如以下代码…

韦东山嵌入式Liunx入门笔记一

文章目录 一、嵌入式Linux二、Ubuntu系统2-1 安装软件2-2 Linux文件(1) 文件架构(2)文件属性(3)文件命令(4) 解压、压缩文件(5) 网络命令 2-3 vi编辑器2-4 Ubuntu下包管理 三、配置网卡四、安装后续学习使用的软件4-1 MobaXterm4-2 FileZilla4-3 Source Insight4.04-4 下载BSP4…

vivado 定义和配置I/O端口、

定义和配置I/O端口 您可以使用Vivado IDE导入、创建和配置I/O端口&#xff0c;如中所述以下部分。 导入I/O端口 根据项目类型&#xff0c;可以使用以下方法导入I/O端口&#xff1a; •I/O规划项目&#xff1a;您可以将XDC和CSV文件导入空的I/O规划项目当您使用文件导入功能…

Java Lock源码解读

一&#xff0c;概述 多线程问题本质是多个线程共同访问了同一块内存&#xff0c;导致该内存状态不确定而产生了一系列问题。concurrent包中提供的Lock类本质是对线程对象进行监督、排队&#xff0c;调度&#xff0c;确保lock只能有一个线程或共享线程成功返回&#xff0c;否则…

幻兽帕鲁游戏服务器搭建by阿里云服务器4核16G和32G配置价格表

如何自建幻兽帕鲁服务器&#xff1f;基于阿里云服务器搭建幻兽帕鲁palworld服务器教程来了&#xff0c;一看就懂系列。本文是利用OOS中幻兽帕鲁扩展程序来一键部署幻兽帕鲁服务器&#xff0c;阿里云百科aliyunbaike.com分享官方基于阿里云服务器快速创建幻兽帕鲁服务器教程&…

go 引用fork后的模块的两种方式(replace和工作区)

很久没更新了&#xff0c;一是工作琐碎&#xff0c;二是处在舒适区&#xff0c;但最近看着身边的同事一个个离开&#xff0c;危机感骤然而生&#xff0c;不得不重拾书本&#xff0c;毕竟生活还得继续&#xff0c;不卷是不可能的&#xff0c;谁让我们生在这个卷中卷的国度&#…

3d gaussian splatting介绍整理

3D 高斯分布是用于实时辐射场渲染的 3D 高斯分布中描述的一种光栅化技术&#xff0c;它允许实时渲染从小图像样本中学习到的逼真场景。 paper github 本文翻译整理自&#xff1a; blog: Introduction to 3D Gaussian Splatting DDPMs - Part 2 给出一些2D图片&#xff0c;用…

「阿里云」幻兽帕鲁个人服务器已上线,3分钟快速搭建

基于阿里云搭建幻兽帕鲁服务器方法&#xff0c;1到2分钟部署完成&#xff0c;稳定运行无卡顿&#xff0c;阿里云服务器网aliyunfuwuqi.com分享保姆级手把手教程&#xff0c;基于阿里云计算巢、云服务器或无影云桌面都可以&#xff1a; 基于阿里云幻兽帕鲁服务器创建教程 基于…

WLAN

前言 今天给大家讲一个不一样的实验,生活息息相关,特别有意思的,顺便让大家放松放松 实验 一.引入 实验拓扑图: 明眼人已经知道我没要干嘛了,WIFI无线路由器 所有的PC设备都换成WIMP300N模块无线接收 成功后你们的拓扑图就会和我的一样 二、配置Linksys WRT300N   配置pc3…

循环测试之旅——深度解析Pytest插件 pytest-repeat

在软件开发中,测试的重要性不言而喻。而为了提高测试的鲁棒性和可靠性,Pytest插件 pytest-repeat 应运而生。这个插件可以帮助你轻松实现测试用例的循环运行,以更全面地评估代码的稳定性。本文将深入介绍 pytest-repeat 插件的基本用法和实际案例,助你更好地利用循环测试,…

独占指针:unique_ptr 与 函数调用 笔记

推荐B站视频&#xff1a; 2.unique_ptr_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV18B4y187uL?p2&vd_sourcea934d7fc6f47698a29dac90a922ba5a3 3.unique_ptr与函数调用_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV18B4y187uL?p3&vd_sourcea934d…

MIT_线性代数笔记:第 29 讲 奇异值分解

目录 如何实现用矩阵数学语言描述这一过程举例 本讲介绍奇异值分解&#xff08;Singular value decomposition&#xff09;&#xff0c;简称 SVD。这是矩阵最终也是最好的分解&#xff0c;任意矩阵可分解为 A U Σ V T AUΣV^T AUΣVT&#xff0c;分解结果为正交矩阵 U&#x…