《TCP/IP网络编程》阅读笔记--基于TCP的服务器端/客户端
目录
1--TCP/IP协议栈
2--TCP服务器端默认函数调用顺序
3--TCP客户端的默认函数调用顺序
4--Linux实现迭代回声服务器端/客户端
5--Windows实现迭代回声服务器端/客户端
6--TCP原理
7--Windows实现计算器服务器端/客户端
1--TCP/IP协议栈
TCP/IP协议栈共分 4 层,可以理解为数据收发分成了 4 个层次化过程;
链路层:
链路层是物理链接领域标准化的结果,专门定义LAN、WAN、MAN等网络标准;
IP层:
IP层用于解决数据传输过程中路径的选择问题;
TCP/IP层:
即传输层,用于解决数据传输的问题(数据顺序、可靠性等);
应用层:
程序员根据数据传输规则,编写规定的程序(例如Socket)来实现数据传输;
2--TCP服务器端默认函数调用顺序
一般 TCP 服务器端调用默认函数的顺序如下:socket() 创建 Socket → bind() 分配Socket 地址 → listen() 等待连接请求状态 → accept() 允许连接 → read()/write() 数据交换 → close() 断开连接;
调用 listen() 函数进入等待连接请求状态,只有服务器端调用了 listen() 函数,客户端才能进入可发出连接请求的状态;
#include <sys/socket.h>int listen(int sock, int backlog); // 成功时返回 0, 失败时返回 -1;// sock 表示希望进入等待连接请求状态的Socket的文件描述符
// backlog 表示连接请求等待队列的长度,即最多可以使多少个连接请求进入队列
服务器端调用 accept() 函数来受理客户端的连接请求,即受理等待队列中待处理的客户端连接请求;
#include <sys/socket.h>int accept(int sock, struct sockaddr* addr, socklen_t* addrlen);
// 成功时返回创建的Socket的文件描述符,失败时返回-1
3--TCP客户端的默认函数调用顺序
一般 TCP 客户端调用默认函数的顺序如下:socket() 创建 Socket → connect() 请求连接 → read()/write() 交换数据 → close() 断开连接;
在服务器端调用 listen() 函数创建连接请求等待队列后,客户端可通过调用 connect() 函数来请求连接;
#include <sys/socket.h>int connect(int sock, struct sockaddr* servaddr, socklen_t addrlen);// sock 表示客户端socket的文件描述符
// servaddr 保存了目标服务器端地址信息
// addrlen 第二个结构体参数 servaddr 的地址变量长度,以字节为单位
4--Linux实现迭代回声服务器端/客户端
服务器端:
// gcc echo_server.c -o echo_server
// ./echo_server 9190#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>#define BUF_SIZE 1024
void error_handling(char *message){fputs(message, stderr);fputc('\n', stderr);exit(1);
}int main(int argc, char *argv[]){int serv_sock, clnt_sock;char message[BUF_SIZE];int str_len, i;struct sockaddr_in serv_adr, clnt_adr;socklen_t clnt_adr_sz;if(argc != 2){printf("Usage : %s <port>\n", argv[0]);exit(1);}serv_sock = socket(PF_INET, SOCK_STREAM, 0);if(serv_sock == -1){error_handling("socket() error");}memset(&serv_adr, 0, sizeof(serv_adr));serv_adr.sin_family = AF_INET;serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);serv_adr.sin_port = htons(atoi(argv[1]));if(bind(serv_sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr)) == -1){error_handling("bind() error");}if(listen(serv_sock, 5) == -1){error_handling("listen() error");}clnt_adr_sz = sizeof(clnt_adr);for(i = 0; i < 5; i++){clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_adr, &clnt_adr_sz);if(clnt_sock == -1){error_handling("accept() error");}else{printf("Connected client %d \n", i+1);}while((str_len = read(clnt_sock, message, BUF_SIZE)) != 0){write(clnt_sock, message, str_len);}close(clnt_sock);}close(serv_sock);return 0;
}
客户端:
// gcc echo_client.c -o echo_client
// ./echo_client 127.0.0.1 9190#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>#define BUF_SIZE 1024void error_handling(char *message){fputs(message, stderr);fputc('\n', stderr);exit(1);
}int main(int argc, char *argv[]){int sock;char message[BUF_SIZE];int str_len;struct sockaddr_in serv_adr;if(argc != 3){printf("Usage : %s <IP> <port>\n", argv[0]);exit(1);}sock = socket(PF_INET, SOCK_STREAM, 0);if(sock == -1){error_handling("socket() error");}memset(&serv_adr, 0, sizeof(serv_adr));serv_adr.sin_family = AF_INET;serv_adr.sin_addr.s_addr = inet_addr(argv[1]);serv_adr.sin_port = htons(atoi(argv[2]));if(connect(sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr)) == -1){error_handling("connect() error!");}else{puts("Connected.......");}while(1){fputs("Input message(Q to quit): ", stdout);fgets(message, BUF_SIZE, stdin);if(!strcmp(message, "q\n") || !strcmp(message, "Q\n")){break;}write(sock, message, strlen(message));str_len = read(sock, message, BUF_SIZE-1);message[str_len] = 0;printf("Message from server: %s", message);}close(sock);return 0;
}
运行结果:
5--Windows实现迭代回声服务器端/客户端
服务器端:
// gcc echo_server_win.c -o echo_server_win -lwsock32
// echo_server_win 9190#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>#define BUF_SIZE 1024void ErrorHandling(char *message){fputs(message, stderr);fputc('\n', stderr);exit(1);
}int main(int argc, char *argv[]){WSADATA wsaData;SOCKET hServSock, hClntSock;char message[BUF_SIZE];int strLen, i;SOCKADDR_IN servAdr, clntAdr;int clntAdrSize;if(argc != 2){printf("Usage : %s <port>\n", argv[0]);exit(1);}if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0){ErrorHandling("WSAStartup() error!");}hServSock = socket(PF_INET, SOCK_STREAM, 0);if(hServSock == INVALID_SOCKET){ErrorHandling("socket() error");}memset(&servAdr, 0, sizeof(servAdr));servAdr.sin_family = AF_INET;servAdr.sin_addr.s_addr = htonl(INADDR_ANY);servAdr.sin_port = htons(atoi(argv[1]));if(bind(hServSock, (SOCKADDR*)&servAdr, sizeof(servAdr)) == SOCKET_ERROR){ErrorHandling("bind() error");}if(listen(hServSock, 5) == SOCKET_ERROR){ErrorHandling("listen() error");}clntAdrSize = sizeof(clntAdr);for(int i = 0; i < 5; i++){hClntSock = accept(hServSock, (SOCKADDR*)&clntAdr, &clntAdrSize);if(hClntSock == -1){ErrorHandling("accept() error");}else{printf("Connected client %d \n", i + 1);}while((strLen = recv(hClntSock, message, BUF_SIZE, 0)) != 0){send(hClntSock, message, strLen, 0);}closesocket(hClntSock);}closesocket(hServSock);WSACleanup();return 0;
}
客户端:
// gcc echo_client_win.c -o echo_client_win -lwsock32
// echo_client_win 127.0.0.1 9190#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>#define BUF_SIZE 1024void ErrorHandling(char *message){fputs(message, stderr);fputc('\n', stderr);exit(1);
}int main(int argc, char *argv[]){WSADATA wsaData;SOCKET hSocket;char message[BUF_SIZE];int strLen;SOCKADDR_IN servAdr;if(argc != 3){printf("Usage: %s <IP> <port>\n", argv[0]);exit(1);}if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0){ErrorHandling("WSAStartup() error!");}hSocket = socket(PF_INET, SOCK_STREAM, 0);if(hSocket == INVALID_SOCKET){ErrorHandling("socket() error");}memset(&servAdr, 0, sizeof(servAdr));servAdr.sin_family = AF_INET;servAdr.sin_addr.s_addr = inet_addr(argv[1]);servAdr.sin_port = htons(atoi(argv[2]));if(connect(hSocket, (SOCKADDR*)&servAdr, sizeof(servAdr)) == SOCKET_ERROR){ErrorHandling("connect() error!");}else{puts("Connected........");}while(1){fputs("Input message(Q to quit): ", stdout);fgets(message, BUF_SIZE, stdin);if(!strcmp(message, "q\n") || !strcmp(message, "Q\n")){break;}send(hSocket, message, strlen(message), 0);strLen = recv(hSocket, message, BUF_SIZE - 1, 0);message[strLen] = 0;printf("Message from server: %s", message);}closesocket(hSocket);WSACleanup();return 0;
}
测试结果:
6--TCP原理
I/O缓冲:
当调用 write() 函数后并不会立即传输数据,而是将数据移至输出缓冲;
同样当调用 read() 函数后也并不会马上接收数据,而是将数据移至输入缓冲;
TCP会控制数据流,使用滑动窗口协议(参考博主之前的笔记:TCP流量控制)来限制数据传输不会超过输入缓冲的大小;
TCP 协议使用三次握手来建立连接,使用四次挥手来断开连接;(具体分析参考书中图解)
7--Windows实现计算器服务器端/客户端
服务器端:
// gcc op_server_win.c -o op_server_win -lwsock32
// op_server_win 9190#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>#define BUF_SIZE 1024
#define OPSZ 4void ErrorHandling(char *message){fputs(message, stderr);fputc('\n', stderr);exit(1);
}int calculate(int opnum, int opnds[], char op){int result = opnds[0], i;switch(op){case '+':for(i = 1; i < opnum; i++) result += opnds[i];break;case '-':for(i = 1; i < opnum; i++) result -= opnds[i];break;case '*':for(i = 1; i < opnum; i++) result *= opnds[i];break;}return result;
}int main(int argc, char* argv[]){WSADATA wsaData;SOCKET hServSock, hClntSock;char opinfo[BUF_SIZE];int result, opndCnt, i;int recvCnt, recvLen;SOCKADDR_IN servAdr, clntAdr;int clntAdrSize;if(argc != 2){printf("Usage: %s <port>\n", argv[0]);exit(1);}if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0){ErrorHandling("WSAStartup() error!");}hServSock = socket(PF_INET, SOCK_STREAM, 0);if(hServSock == INVALID_SOCKET){ErrorHandling("socket() error!");}memset(&servAdr, 0, sizeof(servAdr));servAdr.sin_family = AF_INET;servAdr.sin_addr.s_addr = htonl(INADDR_ANY);servAdr.sin_port = htons(atoi(argv[1]));if(bind(hServSock, (SOCKADDR*)&servAdr, sizeof(servAdr)) == SOCKET_ERROR){ErrorHandling("bind() error");}if(listen(hServSock, 5) == SOCKET_ERROR){ErrorHandling("listen() error");}clntAdrSize = sizeof(clntAdr);for(int i = 0; i < 5; i++){opndCnt = 0;hClntSock = accept(hServSock, (SOCKADDR*)&clntAdr, &clntAdrSize);recv(hClntSock, (char*)&opndCnt, 1, 0); // recv one byterecvLen = 0;while((opndCnt * OPSZ + 1) > recvLen){recvCnt = recv(hClntSock, &opinfo[recvLen], BUF_SIZE - 1, 0);recvLen += recvCnt;}result = calculate(opndCnt, (int*)opinfo, opinfo[recvLen - 1]);send(hClntSock, (char*)&result, sizeof(result), 0);closesocket(hClntSock);}closesocket(hServSock);WSACleanup();return 0;
}
客户端:
// gcc op_client_win.c -o op_client_win -lwsock32
// op_client_win 127.0.0.1 9190#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>#define BUF_SIZE 1024
#define RLT_SIZE 4
#define OPSZ 4void ErrorHandling(char *message){fputs(message, stderr);fputc('\n', stderr);exit(1);
}int main(int argc, char* argv[]){WSADATA wsaData;SOCKET hSocket;char opmsg[BUF_SIZE];int result, opndCnt, i;SOCKADDR_IN servAdr;if(argc != 3){printf("Usage : %s <IP> <port>\n", argv[0]);exit(1);}if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0){ErrorHandling("WSAStartup() error!");}hSocket = socket(PF_INET, SOCK_STREAM, 0);if(hSocket == INVALID_SOCKET){ErrorHandling("socket() error!");}memset(&servAdr, 0, sizeof(servAdr));servAdr.sin_family = AF_INET;servAdr.sin_addr.s_addr = inet_addr(argv[1]);servAdr.sin_port = htons(atoi(argv[2]));if(connect(hSocket, (SOCKADDR*)&servAdr, sizeof(servAdr)) == SOCKET_ERROR){ErrorHandling("connect() error!");}else{puts("Connected........");}fputs("Operand count: ", stdout);scanf("%d", &opndCnt);opmsg[0] = (char)opndCnt; // one bytefor(i = 0; i < opndCnt; i++){printf("Operand %d: ", i + 1);scanf("%d", (int*)&opmsg[i*OPSZ + 1]);}fgetc(stdin);fputs("Operator: ", stdout);scanf("%c", &opmsg[opndCnt * OPSZ + 1]); // one bytesend(hSocket, opmsg, opndCnt * OPSZ + 2, 0);recv(hSocket, (char*)&result, RLT_SIZE, 0);printf("Operation result: %d \n", result);closesocket(hSocket);WSACleanup();return 0;
}
运行结果:
相关文章:

《TCP/IP网络编程》阅读笔记--基于TCP的服务器端/客户端
目录 1--TCP/IP协议栈 2--TCP服务器端默认函数调用顺序 3--TCP客户端的默认函数调用顺序 4--Linux实现迭代回声服务器端/客户端 5--Windows实现迭代回声服务器端/客户端 6--TCP原理 7--Windows实现计算器服务器端/客户端 1--TCP/IP协议栈 TCP/IP协议栈共分 4 层…...
【每日一题】43. 字符串相乘
43. 字符串相乘 - 力扣(LeetCode) 给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。 注意:不能使用任何内置的 BigInteger 库或直接将输入转换为整数。 示例…...

机器学习——K最近邻算法(KNN)
机器学习——K最近邻算法(KNN) 文章目录 前言一、原理二、距离度量方法2.1. 欧氏距离2.2. 曼哈顿距离2.3. 闵可夫斯基距离2.4. 余弦相似度2.5. 切比雪夫距离2.6. 马哈拉诺比斯距离2.7. 汉明距离 三、在MD编辑器中输入数学公式(额外࿰…...

同步FIFO的verilog实现(1)——计数法
一、FIFO概述 1、FIFO的定义 FIFO是英文First-In-First-Out的缩写,是一种先入先出的数据缓冲器,与一般的存储器的区别在于没有地址线, 使用起来简单,缺点是只能顺序读写数据,其数据地址由内部读写指针自动加1完成&…...
python正则表达式笔记1
最近工作中经常用到正则表达式处理数据,慢慢发现了正则表达式的强大功能,尤其在数据处理工作中,记录下来分享给大家。 一、 正则表达式语法介绍 正则表达式(或 RE)指定了一组与之匹配的字符串;模块内的函…...

YOLO目标检测——口罩规范佩戴数据集+已标注xml和txt格式标签下载分享
实际项目应用:目标检测口罩佩戴检测数据集的应用场景涵盖了公共场所监控、疫情防控管理、安全管理与控制以及人员统计和分析等领域。这些应用场景可以帮助相关部门和机构更好地管理口罩佩戴情况,提高公共卫生和安全水平,保障人们的健康和安全…...

Android 13 - Media框架(9)- NuPlayer::Decoder
这一节我们将了解 NuPlayer::Decoder,学习如何将 MediaCodec wrap 成一个强大的 Decoder。这一节会提前讲到 MediaCodec 相关的内容,如果看不大懂可以先跳过此篇。原先觉得 Decoder 部分简单,越读越发现自己的无知,Android 源码真…...
23.09.5 《CLR via C#》 笔记5
第六章 类型和成员基础 类型可以定义0或多个以下成员:常量、字段、实例构造器、类型构造器、方法、操作符重载、转换操作符、属性、事件、类型类型的可见性分为public和internal(默认)C#中,成员的可访问性分为private、protected、internal、protected …...

laravel部署api项目遇到问题总结
laravel线上部署问题 一、Ubuntu远程Mysql 61“Connection refused”二、Ubuntu更新php8三、线上部署Permission denied3.1、部署完之后访问域名出现报错:3.2、The /bootstrap/cache directory must be present and writable. 四、图片访问404五、git部署线上文件 一…...
lintcode 1646 · 合法组合【字符串DFS, vip 中等 好题】
题目 https://www.lintcode.com/problem/1646 给一个单词s,和一个字符串集合str。这个单词每次去掉一个字母,直到剩下最后一个字母。求验证是否存在一种删除的顺序,这个顺序下所有的单词都在str中。例如单词是’abc’,字符串集合是{‘a’,’…...

【多线程】线程安全 问题
线程安全 问题 一. 线程不安全的典型例子二. 线程安全的概念三. 线程不安全的原因1. 线程调度的抢占式执行2. 修改共享数据3. 原子性4. 内存可见性5. 指令重排序 一. 线程不安全的典型例子 class ThreadDemo {static class Counter {public int count 0;void increase() {cou…...

【用unity实现100个游戏之11】复刻经典消消乐游戏
文章目录 前言开始项目开始一、方块网格生成二、方块交换三、添加交换的动画效果四、水平消除检测五、垂直消除检测六、完善删除功能七、效果优化(移动方块后再进行消除检测)八、方块下落十、方块填充十一、后续 源码参考完结 前言 欢迎来到经典消消乐游…...

若依cloud 修改包名等
一、项目的项目名。 先改pom 然后在重命名文件 1、 修改主pom.xml <artifactId>ruoyi-api</artifactId> 缓存 <artifactId>zxf-api</artifactId> <groupId>com.ruoyi</groupId> <groupId>com.zhixiaofeng</groupId> 2、…...

健康系统练习
健康系统 项目建构: 前后端分离,前端vue3,后端Java,springboot做跨域处理,前端将在vscode中 的tomcat下部署,后端将在ideal中集成的tomcat中部署 创建项目工程在ideal中直接选用springi…创建,…...

网络协议从入门到底层原理学习(一)—— 简介及基本概念
文章目录 网络协议从入门到底层原理学习(一)—— 简介及基本概念一、简介1、网络协议的定义2、网络协议组成要素3、广泛的网络协议类型网络通信协议网络安全协议网络管理协议 4、网络协议模型对比图 二、基本概念1、网络互连模型2、计算机之间的通信基础…...

centos密码过期导致navicat无法通过SSH登录阿里云RDS问题
具体错误提示:2013 - Lost connection to server at "hand hake: reading initial communication packet, system error: 0 解决办法:更新SSH服务器密码...

对于pytorch和对应pytorch网站的探索
一、关于网站上面的那个教程: 适合PyTorch小白的官网教程:Learning PyTorch With Examples - 知乎 (zhihu.com) 这个链接也是一样的, 总的来说,里面讲了这么一件事: 如果没有pytorch的分装好的nn.module用来继承的话,需要设计…...
和AI聊天:动态规划
动态规划 动态规划(Dynamic Programming,简称 DP)是一种常用于优化问题的算法。它解决的问题通常具有重叠子问题和最优子结构性质,可以通过将问题分解成相互依赖的子问题来求解整个问题的最优解。 动态规划算法主要分为以下几个步…...
微信小程序——使用插槽slot快捷开发
微信小程序的插槽(slot)是一种组件化的技术,用于在父组件中插入子组件的内容。通过插槽,可以将父组件中的一部分内容替换为子组件的内容,实现更灵活的组件复用和定制。 插槽的使用步骤如下: 在父组件的wx…...

大数据技术之Hadoop:使用命令操作HDFS(四)
目录 一、创建文件夹 二、查看指定目录下的内容 三、上传文件到HDFS指定目录下 四、查看HDFS文件内容 五、下载HDFS文件 六、拷贝HDFS文件 七、HDFS数据移动操作 八、HDFS数据删除操作 九、HDFS的其他命令 十、hdfs web查看目录 十一、HDFS客户端工具 11.1 下载插件…...
谷歌浏览器插件
项目中有时候会用到插件 sync-cookie-extension1.0.0:开发环境同步测试 cookie 至 localhost,便于本地请求服务携带 cookie 参考地址:https://juejin.cn/post/7139354571712757767 里面有源码下载下来,加在到扩展即可使用FeHelp…...

微信小程序 - 手机震动
一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注:文档 https://developers.weixin.qq…...
【AI学习】三、AI算法中的向量
在人工智能(AI)算法中,向量(Vector)是一种将现实世界中的数据(如图像、文本、音频等)转化为计算机可处理的数值型特征表示的工具。它是连接人类认知(如语义、视觉特征)与…...

《基于Apache Flink的流处理》笔记
思维导图 1-3 章 4-7章 8-11 章 参考资料 源码: https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...

如何在最短时间内提升打ctf(web)的水平?
刚刚刷完2遍 bugku 的 web 题,前来答题。 每个人对刷题理解是不同,有的人是看了writeup就等于刷了,有的人是收藏了writeup就等于刷了,有的人是跟着writeup做了一遍就等于刷了,还有的人是独立思考做了一遍就等于刷了。…...
Webpack性能优化:构建速度与体积优化策略
一、构建速度优化 1、升级Webpack和Node.js 优化效果:Webpack 4比Webpack 3构建时间降低60%-98%。原因: V8引擎优化(for of替代forEach、Map/Set替代Object)。默认使用更快的md4哈希算法。AST直接从Loa…...
Git常用命令完全指南:从入门到精通
Git常用命令完全指南:从入门到精通 一、基础配置命令 1. 用户信息配置 # 设置全局用户名 git config --global user.name "你的名字"# 设置全局邮箱 git config --global user.email "你的邮箱example.com"# 查看所有配置 git config --list…...
Kubernetes 网络模型深度解析:Pod IP 与 Service 的负载均衡机制,Service到底是什么?
Pod IP 的本质与特性 Pod IP 的定位 纯端点地址:Pod IP 是分配给 Pod 网络命名空间的真实 IP 地址(如 10.244.1.2)无特殊名称:在 Kubernetes 中,它通常被称为 “Pod IP” 或 “容器 IP”生命周期:与 Pod …...
【WebSocket】SpringBoot项目中使用WebSocket
1. 导入坐标 如果springboot父工程没有加入websocket的起步依赖,添加它的坐标的时候需要带上版本号。 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId> </dep…...

结构化文件管理实战:实现目录自动创建与归类
手动操作容易因疲劳或疏忽导致命名错误、路径混乱等问题,进而引发后续程序异常。使用工具进行标准化操作,能有效降低出错概率。 需要快速整理大量文件的技术用户而言,这款工具提供了一种轻便高效的解决方案。程序体积仅有 156KB,…...