C语言TCP通信基础CS模型

模型局限性:

1 不使用 非阻塞 同步异步 信号驱动 多路复用 select poll epoll 事件驱动 等技术

2 意在展示最原始的TCP模型

3 代码命名规整清晰,注释详尽,不官方,使用GPT4做了详细检查

实验流程:

服务器:socket->bind->listen->accept->recv->send->close 共7步

客户端:socket->connect->send->recv->close 共5步

预设共识:

只有一个服务器 可以有多个客户端 实际上后面的各种io模型变种都是基于此共识 

实验描述及现象:

当客户端连接上服务器后,会输出已连接字样,并打印收到的数据

然后回给客户端一句话 为了模拟大数据处理 启用了5秒计时

客户端会收到这些数据并打印在自己的终端

这样就模拟了一次CS模型的TCP通信,可以反复运行客户端观察结果

观察阻塞现象:将客户端复制几份 多个控制台同时运行,可以观察到同时只有一个控制台在数54321

注意事项:

1 用于通信的ip地址和端口号要进行大小端转换

2 服务器 accept会返回新的socket用于读写,而客户端一直使用同一个socket

3 如果服务器和客户端都使用whlie(recv)的结构会造成类似死锁的现象,所以必须有一端缓冲区足够大

4 客户端进程的结束取决于服务器的处理速度,当服务器send全部数据后会close socket,客户端会收到0个字节后结束进程

5 服务器ip地址及端口号 已经预设到宏中 方便修改 必须同时修改CS两端

6 在更复杂的应用场景中,可能需要引入更高级的通信模型和技术,以提高效率和响应性。

#define _GNU_SOURCE
#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
// 服务器地址
#define SERVER_IP "192.168.142.132"
// 服务器端口
#define SERVER_PORT 55555
int main()
{// server_sockfd用于socket的返回值,client_sockfd用于accept的返回值int server_sockfd, client_sockfd;// 不用struct sockaddr的原因是不能直接设置ip和端口,而后都需要强转,属于历史遗留问题// 这个结构体主要存 ip地址 端口号 地址族struct sockaddr_in server_sockaddr, client_sockaddr;memset(&server_sockaddr, 0, sizeof(server_sockaddr));memset(&client_sockaddr, 0, sizeof(client_sockaddr));// accept要求填入lensocklen_t client_sockaddr_len = sizeof(client_sockaddr);// 表示发送接收的字节ssize_t send_bytes, recv_bytes;// 发送用bufchar send_buf[1024] = "server say : fine.";// 接受用bufchar recv_buf[1024] = {0};// 大小端转换用bufchar ipv4_addr_buf[64] = {0};// 新建ipv4 tcp类型socketserver_sockfd = socket(AF_INET, SOCK_STREAM, 0);if (server_sockfd == -1){perror("socket");exit(EXIT_FAILURE);}// 地址族server_sockaddr.sin_family = AF_INET;// ip地址大小端转换+存入结构体inet_pton(AF_INET, SERVER_IP, &server_sockaddr.sin_addr.s_addr);// 端口大小端转换+存入结构体server_sockaddr.sin_port = htons(SERVER_PORT);// 绑定到socketif (bind(server_sockfd, (struct sockaddr *)&server_sockaddr, sizeof(server_sockaddr)) == -1){perror("bind");exit(EXIT_FAILURE);}// 使用ss -tuln命令 可以看见此进程正在监听55555if (listen(server_sockfd, 16) == -1){perror("listen");exit(EXIT_FAILURE);}printf("server start...\n");while (1){// 有客户端connect,他就会accept// 函数返回后,代表建立了连接,此时client_sockaddr中会有内容// 函数的返回值是一个用于读写的新socketclient_sockfd = accept(server_sockfd, (struct sockaddr *)&client_sockaddr, &client_sockaddr_len);if (client_sockfd == -1){perror("accept");continue;}// 尝试打印出client_sockaddr中的内容,当然要进行字节序转换inet_ntop(AF_INET, &client_sockaddr.sin_addr, ipv4_addr_buf, sizeof(ipv4_addr_buf));printf("ipv4_addr:%s port:%d connected\n", ipv4_addr_buf, ntohs(client_sockaddr.sin_port));// 服务器收到的数据,假设缓冲区足够大能一次性读取recv_bytes = recv(client_sockfd, recv_buf, sizeof(recv_buf), 0);if (recv_bytes == -1){perror("recv");exit(EXIT_FAILURE);}else if (recv_bytes == 0){printf("closed by peer\n");continue;}// 打印出来看看printf("%s\n", recv_buf);// 假设有大型数据total字节,一次发不完ssize_t total = (ssize_t)strlen(send_buf);// 已发送数量ssize_t sent = 0;// 当已发送小于总数就继续发while (total > sent){// send_buf + sent 指针运算,发完的下次指针就往后挪// total - sent剩余数量send_bytes = send(client_sockfd, send_buf + sent, total - sent, 0);if (send_bytes == -1){perror("send");break;}// 计算已发送字节总数sent += send_bytes;}// 模拟大数据处理时间char a[5] = "54321";for (size_t i = 0; i < 5; i++){send(client_sockfd, &a[i], 1, 0);sleep(1);}// 这个客户端的请求就处理完了close(client_sockfd);}close(server_sockfd);return 0;
}
#define _GNU_SOURCE
#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>// 服务器地址
#define SERVER_IP "192.168.142.132"
// 服务器端口
#define SERVER_PORT 55555
int main()
{// 客户端用socketint client_sockfd;// 这个结构体主要存 ip地址 端口号 地址族struct sockaddr_in server_sockaddr, client_sockaddr;memset(&server_sockaddr, 0, sizeof(server_sockaddr));memset(&client_sockaddr, 0, sizeof(client_sockaddr));// getsockname要求填入lensocklen_t client_sockaddr_len = sizeof(client_sockaddr);// 表示发送接收的字节ssize_t send_bytes, recv_bytes;// 发送用bufchar send_buf[1024] = {0};// 接受用bufchar recv_buf[1024] = {0};// 地址转换用bufchar ipv4_addr_buf[64] = {0};// 新建ipv4 tcp类型socketclient_sockfd = socket(AF_INET, SOCK_STREAM, 0);if (client_sockfd == -1){perror("socket");exit(EXIT_FAILURE);}// 对server_sockaddr进行大小端转换及存入服务器地址信息inet_pton(AF_INET, SERVER_IP, &server_sockaddr.sin_addr.s_addr);server_sockaddr.sin_port = htons(SERVER_PORT);server_sockaddr.sin_family = AF_INET;// 连接服务器if ((connect(client_sockfd, (struct sockaddr *)&server_sockaddr, sizeof(server_sockaddr))) == -1){perror("connect");exit(EXIT_FAILURE);}// 获取本机地址及端口信息,端口是随机分配的getsockname(client_sockfd, (struct sockaddr *)&client_sockaddr, &client_sockaddr_len);inet_ntop(AF_INET, &client_sockaddr.sin_addr, ipv4_addr_buf, sizeof(ipv4_addr_buf));// 向服务器问好,并告诉是谁给他发的消息snprintf(send_buf, sizeof(send_buf), "ipv4_addr:%s port:%u say : how are you ?", ipv4_addr_buf, ntohs(client_sockaddr.sin_port));// 假设这句话是一个很大型的数据// 总字节ssize_t total = (ssize_t)strlen(send_buf);// 已发送字节ssize_t sent = 0;// 已发送小于总字节就继续发while (total > sent){// send_buf + sent 指针运算,发完的下次指针就往后挪// total - sent剩余数量send_bytes = send(client_sockfd, send_buf + sent, total - sent, 0);if (send_bytes == -1){perror("send");break;}// 计算已发送字节总数sent += send_bytes;}// 为什么客户端可以使用一个阻塞式的while(recv())?// 因为服务端发送完毕会close连接,客户端recv_bytes=0 跳出循环while ((recv_bytes = recv(client_sockfd, recv_buf, sizeof(recv_buf), 0)) > 0){printf("%s\n", recv_buf);memset(recv_buf, 0, sizeof(recv_buf));}if (recv_bytes == -1){perror("recv");}else if (recv_bytes == 0){printf("closed by peer\n");}// 全剧终close(client_sockfd);return 0;
}

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

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

相关文章

16 六年级基本概念

打算开始预习六年级上册了&#xff0c;先写一篇文章作为基本概念汇总。 【注意&#xff1a;本篇所提到的重点知识只有课本上提到的重点&#xff0c;并不代表考试范围。】 1 长方体与正方体 第一部分 正方体与长方体的初步认识 两个面相交的线叫做棱&#xff0c;三条棱相交的…

k8s etcdctl 备份

安装 #!/bin/bash # Author: WeiyiGeek # Description: etcd 与 etcdctl 下载安装 ETCD_VERv3.5.5 ETCD_DIRetcd-download DOWNLOAD_URLhttps://github.com/coreos/etcd/releases/download mkdir ${ETCD_DIR} cd ${ETCD_DIR} wget ${DOWNLOAD_URL}/${ETCD_VER}/etcd-${ETCD_V…

算法---排序

目录 插入排序插入排序的思想代码实现 冒泡排序冒泡排序的思想代码实现 堆排序堆排序的基本思想代码实现 希尔排序希尔排序基本思想代码实现 选择排序选择排序基本思想代码展示 总结 插入排序 插入排序的思想 简单来说&#xff0c;插入排序就时将一个数插入一个数插入一个有序…

docker 容器与本地主机间文件/文件夹的传输

在Docker中&#xff0c;容器与宿主机之间的文件传输主要有两种方式&#xff1a;挂载目录和使用 docker cp 命令。 1、挂载目录 类似共享文件夹&#xff0c;直接在容器中/本地将需要传输的文件/文件夹复制进去即可。限制是在将镜像展开成容器时需要挂载上去&#xff0c;后面再…

使用 ReclaiMe Pro 查找并恢复网络中的 SSH 服务器数据

天津鸿萌科贸发展有限公司是 ReclaiMe Pro 数据恢复软件的授权代理商。ReclaiMe Pro 数据恢复软件专注于恢复几乎所有文件系统及各种类型和复杂程度的 RAID 阵列。 在本文中&#xff0c;我们介绍 ReclaiMe Pro 对于采用 SSH 连接方式的网络服务器中数据的恢复方法。 ReclaiMe…

设计模式及其在项目、框架中的应用

设计模式的作用&#xff1a; 1、类之间关系图&#xff0c;明确的角色及其关系、作用&#xff1b; 2、符合开闭原则&#xff0c;职责明确&#xff0c;并且开放的拓展点可以有效应对后期的变化。 &#xff08;一&#xff09;、责任链模式 适用场景&#xff1a; 在一个流程中&…

AutoTokenizer.from_pretrained 与BertTokenizer.from_pretrained

AutoTokenizer.from_pretrained 和 BertTokenizer.from_pretrained 都是 Hugging Face 的 Transformers 库中用于加载预训练模型的 tokenizer 的方法&#xff0c;但它们之间有一些区别。 灵活性&#xff1a; AutoTokenizer.from_pretrained&#xff1a;这个方法是灵活的&#x…

mysql 锁的知识点简述

1 . mysql 全局锁 mysql 全局锁主要是针对整个数据库的锁,最常用的全局锁是读锁与写锁 读锁 (共享锁) 1.1 读锁 (共享锁) : 他阻止其他用户更新数据,但允许他们读取数据,在你需要的时间内,保持数据的一致性 写锁 (排它锁) 1.2 写锁 (排它锁) : 它阻止其他用户读取和更新数据,…

HWL-41无辅源静态电流继电器 0.5-9.99A 导轨安装 JOSEF约瑟

HWL-40系列无辅源静态电流继电器 HWL-41HWL-42 HWL-43HWL-61 HWL-62HWL-63 HWL-71HWL-72 HWL-73HWL-81 HWL-82HWL-83 产品概述 1、HWL系列集成电路无辅源电流继电器用于发电机、变压器和输电线的过负荷和短路保护装置中作为启动元件。本继电器为集成电路静态型继电器&a…

Token的详解

Token的详解 文章目录 Token的详解前言:简介:使用token&#xff1a; 前言: 为什么会用到Token&#xff0c;因为cookie和session一些自身的缺点&#xff0c;限制了一些功能的实现&#xff0c;比如&#xff1a; cookie&#xff1a;优点是节省服务器空间&#xff0c;缺点不安全。…

启动Vue-demo时引发的一系列问题—解决办法

目录 1.初始遇到的问题&#xff1a;输入npm run dev 1.治标的解决方法 2.治本的解决方法 第一步&#xff1a;检查是否安装了cnpm 第二步&#xff1a;手动找到cnpm目录 第三步&#xff1a;配置系统环境变量 第四步&#xff1a;查看是否安装成功 1.初始遇到的问题&#xf…

memcached面试问题以及答案

文章目录 memcached是什么&#xff1f;Memcached的工作原理Memcached的最大优势Memcached与MySQL的Query Cache比较Memcached与服务器的Local Cache比较Memcached的缓存机制Memcached的冗余机制Memcached的容错处理Memcached中的批量导入导出Memcached中的批量导出导入Memcache…

宝塔面板系列——两种方式安装青龙面板

因为最近旧windows服务器到期了&#xff0c;在搬服务器&#xff0c;新服务器尝试用Linux系统。过程中有很多不懂的地方&#xff0c;只能边搬迁边学边弄&#xff0c;顺带记录下来&#xff0c;哪天又要搬迁了&#xff0c;翻翻自己的文章也就一应俱全了。 非科班出身&#xff0c;选…

【计算机网络实践】Cisco Packet Tracer局域网组网(FTP服务器通过交换机连接客户端)

本文为应对计算机网络第一次实验所写的预习报告 一、实验准备 一台装有Cisco Packet Tracer的PC机&#xff0c;一个大学生大脑。 二、了解FTP和Cisco Packet Tracer 具体内容可在百度搜索&#xff0c;在物理机上用FileZilla Server实现ftp可参看我前面的文章。Cisco Packet Tr…

什么是上市公司股权收购线?

一、什么是上市公司股权收购线&#xff1f; 上市公司股权收购线是指在进行上市公司股权收购时&#xff0c;根据相关法律法规和规定&#xff0c;收购方需要遵守的特定比例或条件。这些比例或条件通常用于确定收购方在收购过程中需要采取的行动或满足的要求。 其中&#xff0c;…

【LabVIEW FPGA入门】FPGA不同传递数据方法比较

数据共享方法的选择应基于应用的需要。根据应用程序的重要特性&#xff0c;所讨论的任何一种方法都可能是合适的。 传输方法FPGA资源损耗&#xff1f;不同时钟源之间传递数据&#xff1f;新数据通知&#xff1f;常见用途变量逻辑片是是否提取最新数据存储器存储器是否否提取最新…

深入理解MySQL中的JOIN算法

码到三十五 &#xff1a; 个人主页 心中有诗画&#xff0c;指尖舞代码&#xff0c;目光览世界&#xff0c;步履越千山&#xff0c;人间尽值得 ! 目录 一、引言二、嵌套循环连接&#xff08;Nested-Loop Join&#xff09;2.1 工作原理2.2 性能考虑2.3 优化策略 三、块嵌套循环…

es6类,判断数据类型

es6的类 今日目标&#xff1a; 1.判断数据类型的方法 2.es6的类 3.es6类实现轮播图 00-回顾 # 原型作用&#xff1a;共享属性和方法特性&#xff1a;1. 每一个函数&#xff0c;这里特指构造函数&#xff0c;都会有一个prototype,这个就是原型对象&#xff0c;也叫显式原型…

机器翻译评价指标 BLEU分数

文章目录 提出背景英文全称和提出时间基本思想 提出背景 在机器翻译任务中&#xff0c;将同一个句子翻译成另外一种语言时&#xff0c;往往会有多个都是正确的翻译结果。因此&#xff0c;在构建机器翻译的评价指标时需要注意如何在有多个正确答案的情况下评价翻译结果的好坏。…

Java代码基础算法练习-递归求数-2024.03.22

任务描述&#xff1a; 利用递归函数调用方式&#xff0c;将所输入的5个字符&#xff0c;以相反顺序打印出来。 任务要求&#xff1a; 代码示例&#xff1a; package march0317_0331;import java.util.Scanner;/*** m240322类&#xff0c;提供了一个反转输入字符串前5个字符的…