1.基于TCP的简单套接字服务器实现

目录

1. TCP通信流程

2. 服务器端的通信流程

2.1 创建用于监听的套接字

2.2 绑定本地IP地址和端口

2.3 设置监听

2.4 等待接受客户端的连接请求

2.5 与客户端进行通信

2.6 客户端连接服务器

3.代码实现

client.cpp

server.cpp

运行方式


在本文中,我们将深入了解套接字(socket)及其在网络通信中的应用,特别是如何在服务器端创建一个基于TCP的简单通信框架。套接字是程序员进行网络通信的一组接口,主要分为客户端和服务器端。在这篇文章中,我们将重点关注服务器端的实现。

1. TCP通信流程

TCP(传输控制协议)是一个面向连接、安全可靠的流式传输协议,它位于传输层,确保数据的准确传输。

2. 服务器端的通信流程

下面是整个服务器端的通信流程:

2.1 创建用于监听的套接字

首先,我们需要创建一个套接字来监听客户端的连接请求。代码示例如下:

// 创建一个套接字,函数原型
int socket(int domain, int type, int protocol);// 使用
int fd = socket(AF_INET, SOCK_STREAM, 0);

包含的头文件: #include <sys/socket.h>

参数说明:

  • domain:地址族协议(如 AF_INET 表示使用IPv4,AF_INET6 表示使用IPv6)
  • type:数据传输协议(SOCK_STREAM 表示TCP,SOCK_DGRAM 表示UDP)
  • protocol:一般设为0,表示使用默认协议

返回值: 成功返回可用于套接字通信的文件描述符,失败返回 -1。

2.2 绑定本地IP地址和端口

接下来,我们将文件描述符与本地IP地址和端口进行绑定。

// 函数原型
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);// 使用
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(10000);
addr.sin_addr.s_addr = htonl(INADDR_ANY);int ret = bind(fd, (struct sockaddr*)&addr, sizeof(addr));

参数说明:

  • sockfd:监听的文件描述符
  • addr:要绑定的IP和端口信息
  • addrlenaddr指向的内存大小(sizeof(struct sockaddr)

返回值: 成功返回0,失败返回 -1。

2.3 设置监听

绑定后,接下来需要设置监听。

// 函数原型
int listen(int sockfd, int backlog);// 使用
int ret = listen(fd, 128);

参数说明:

  • sockfd:之前绑定的文件描述符
  • backlog:指定最大连接请求数

返回值: 成功返回0,失败返回 -1。

2.4 等待接受客户端的连接请求

现在我们需要等待并接受客户端的连接请求。

// 函数原型
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);// 使用
struct sockaddr caddr;
memset(&caddr, 0, sizeof(caddr));
socklen_t len = sizeof(caddr);
int cfd = accept(fd, (struct sockaddr*)&caddr, &len);

参数说明:

  • sockfd:之前创建的文件描述符
  • addr:传出参数,存储客户端的地址信息
  • addrlen:传出参数,存储地址大小

返回值: 成功返回一个文件描述符,用于与客户端通信,失败返回 -1。注意,这个函数是阻塞的,直到有新的连接请求到来。

2.5 与客户端进行通信

接下来,可以通过readwrite函数与客户端进行通信。

接收数据:

ssize_t read(int sockfd, void *buf, size_t size);
// 使用
char buf[1024] = {0};
int len = read(cfd, buf, sizeof(buf));

发送数据:

ssize_t write(int fd, const void *buf, size_t len);
// 使用
char msg[] = "Hello, Client!";
int size = write(cfd, msg, strlen(msg));

返回值说明:

  • 接收数据(read):成功时返回接收到的字节数,连接断开返回0,失败返回-1。
  • 发送数据(write):成功返回实际发送的字节数,失败返回-1。

2.6 客户端连接服务器

客户端时使用connect()函数连接服务器,代码示例如下:

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);// 使用
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(10000);
inet_pton(AF_INET, "1.1.1.1", &addr.sin_addr.s_addr); // 服务器IP地址int ret = connect(fd, (struct sockaddr*)&addr, sizeof(addr));

3.代码实现

 server.cpp

#include <stdlib.h>      // 提供exit函数
#include <stdio.h>       // 提供printf和perror函数
#include <unistd.h>      // 提供close函数
#include <arpa/inet.h>   // 提供socket、bind、listen、accept等函数
#include <string.h>      // 提供memset函数int main()
{// 1. 创建用于监听的套接字int fd = socket(AF_INET, SOCK_STREAM, 0);if (fd == -1) {perror("socket"); // 输出错误信息exit(1);          // 退出程序}// 2. 绑定IP地址和端口struct sockaddr_in saddr;memset(&saddr, 0, sizeof(saddr)); // 清空结构体saddr.sin_family = AF_INET; // 使用IPv4saddr.sin_port = htons(10000); // 监听端口,使用网络字节序saddr.sin_addr.s_addr = INADDR_ANY; // 绑定到所有可用的接口int ret = bind(fd, (struct sockaddr*)&saddr, sizeof(saddr));if (ret == -1) {perror("bind"); // 输出错误信息exit(1);        // 退出程序}// 3. 设置监听ret = listen(fd, 128); // 监听套接字,最大连接请求数为128if (ret == -1) {perror("listen"); // 输出错误信息exit(1);          // 退出程序}// 4. 等待并接受客户端的连接struct sockaddr_in cliaddr; // 保存客户端的地址信息socklen_t len = sizeof(cliaddr); // 地址结构体的大小int cfd = accept(fd, (struct sockaddr*)&cliaddr, &len); // 阻塞等待连接if (cfd == -1) {perror("accept"); // 输出错误信息exit(1);          // 退出程序}// 打印新连接的客户端信息char ip[64] = { 0 };printf("new client fd:%d ip:%s, port:%d\n", cfd,inet_ntop(AF_INET, &cliaddr.sin_addr.s_addr, ip, sizeof(ip)), // 获取客户端IPntohs(cliaddr.sin_port)); // 获取客户端端口// 5. 与客户端进行通信char buf[512];while (1) {memset(buf, 0, sizeof(buf)); // 清空缓冲区int len = read(cfd, buf, sizeof(buf)); // 从客户端读取数据if (len > 0) { // 成功接收到数据printf("client says: %s\n", buf); // 打印客户端发送的消息write(cfd, buf, len); // 将相同的数据回发给客户端}else if (len == 0) { // 客户端关闭了连接printf("client is disconnect..\n");break; // 退出循环}else { // 读取失败perror("read"); // 输出错误信息break; // 退出循环}}close(fd); // 关闭监听套接字close(cfd); // 关闭客户端连接套接字return 0; // 程序结束
}

client.cpp

#include <stdlib.h>      // 提供exit函数
#include <stdio.h>       // 提供printf和perror函数
#include <unistd.h>      // 提供close函数
#include <arpa/inet.h>   // 提供socket、connect等函数
#include <string.h>      // 提供memset和strlen函数int main()
{// 1. 创建套接字用于连接服务器int fd = socket(AF_INET, SOCK_STREAM, 0);if (fd == -1) {perror("socket");  // 输出错误信息exit(1);          // 退出程序}// 2. 定义服务器地址和端口struct sockaddr_in saddr; // 创建一个结构体用于存储服务器地址memset(&saddr, 0, sizeof(saddr)); // 清空该结构体saddr.sin_family = AF_INET; // 使用IPv4地址saddr.sin_port = htons(10000); // 设置监听端口,使用网络字节序inet_pton(AF_INET, "127.0.0.1", &saddr.sin_addr.s_addr); // 设置服务器IP地址为localhost// 3. 连接服务器int ret = connect(fd, (struct sockaddr*)&saddr, sizeof(saddr));if (ret == -1) {perror("connect");  // 输出错误信息exit(1);            // 退出程序}// 4. 与服务器进行通信int n = 0; // 计数器while (1){// 发送数据char buf[512] = { 0 }; // 初始化缓冲区sprintf(buf, "hi, I am client...%d\n", n++); // 格式化字符串write(fd, buf, strlen(buf)); // 发送数据到服务器// 接收服务器反馈memset(buf, 0, sizeof(buf)); // 清空缓冲区int len = read(fd, buf, sizeof(buf)); // 从服务器读取数据if (len > 0) // 成功接收到数据{printf("server say: %s\n", buf); // 打印服务器返回的数据}else if (len == 0) // 服务器关闭了连接{printf("server disconnect...\n");break; // 退出循环}else // 读取失败{perror("read"); // 输出错误信息break; // 退出循环}sleep(1);   // 每隔1秒发送一条数据}close(fd); // 关闭套接字return 0; // 程序结束
}

运行方式

  1. 编译代码: 在终端中使用 g++ 编译器编译 client.cppserver.cpp。假设在同一目录下,输入以下命令:

    g++ server.cpp -o server
    g++ client.cpp -o client
    
  2. 运行服务器: 首先打开一个终端,运行服务器程序:

    ./server
    
  3. 运行客户端: 然后打开另一个终端,运行客户端程序:

    ./client
    
  4. 观察输出: 客户端将每隔一秒发送一条消息,服务器将在终端显示接收到的消息,并回发给客户端。您可以观察客户端和服务器的输出信息。

  5. 结束运行: 在客户端和服务器之间的通信完成后,可以在客户端终端按 Ctrl+C 结束客户端,服务器将自动检测到客户端的断开并输出相应消息。

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

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

相关文章

【大模型基础_毛玉仁】5.3 附加参数法:T-Patcher

目录 5.3 附加参数法&#xff1a;T-Patcher5.3.1 补丁的位置1&#xff09;键值存储体2&#xff09;补丁设计 5.3.2 补丁的形式5.3.3 补丁的实现1&#xff09;准确性2&#xff09;局部性 5.3 附加参数法&#xff1a;T-Patcher 附加参数法&#xff1a;通过引入可训练的额外参数实…

【原理系列】计算机组成原理-第一遍阅读总结

目录 创作灵感&#xff1a; 总览&#xff1a; 1、计算机抽象与技术 2、ISA指令集架构 3、CPU处理器 4、存储器 创作灵感&#xff1a; 夯实计算机原理&#xff0c;构建知识框架 此系列将总结和记录我对 【深入浅出计算机组成原理-台湾科技大学/元智大学的刘一宇教授】 的…

React程序打包与部署

===================== 推荐超级课程: 本地离线DeepSeek AI方案部署实战教程【完全版】Docker快速入门到精通Kubernetes入门到大师通关课AWS云服务快速入门实战目录 为生产环境准备React应用最小化和打包环境变量错误处理部署到托管服务部署到Netlify探索高级主题:Hooks、Su…

C++运算符重载、类的转换构造函数和类型转换函数的基础练习

练习1&#xff1a;&#xff08;困难&#xff09; 建立一个矩阵类&#xff0c;可以完成指定的操作或运算。 说明&#xff1a; &#xff08;1&#xff09;、矩阵为2行3列&#xff0c;基类型为整型&#xff1b; &#xff08;2&#xff09;、操作或运算&#xff1a;初始化&…

PERL开发环境搭建>>Windows,Linux,Mac OS

特点 简单 快速 perl解释器直接对源代码程序解释执行,是一个解释性的语言, 不需要编译器和链接器来运行代码>>速度快 灵活 借鉴了C/C, Basic, Pascal, awk, sed等多种语言, 定位于实用性语言,既具备了脚本语言的所有功能,也添加了高级语言功能 开源.免费 没有&qu…

docker(2) -- 启动后修改目录和网络

1. 前言 docker启动前是image文件&#xff0c;启动后是container文件&#xff0c;启动的时候我们可以指定容器的挂载目录以及网络类型&#xff0c;但启动后&#xff0c;这些信息都以配置文件的形式保存在container中&#xff0c;container重新启动时无法重新指定这些信息&…

Flutter开发中如何避免UI显示溢出的问题

文章目录 1. 使用灵活的布局组件2. 允许内容滚动3. 处理文本溢出4. 检查父容器约束5. 使用自适应布局6. 调试工具7. 其他技巧总结 在 Flutter 开发中&#xff0c;UI 溢出&#xff08;如 A RenderFlex overflowed 错误&#xff09;通常是由于布局空间不足或子组件尺寸超出父容器…

使用FastExcel时的单个和批量插入的问题

在我们用excel表进行插入导出的时候&#xff0c;通常使用easyexcel或者FastExcel&#xff0c;而fastexcel是easy的升级版本&#xff0c;今天我们就对使用FastExcel时往数据库插入数据的业务场景做出一个详细的剖析 场景1 现在我们数据库有一张组织表&#xff0c;组织表的字段…

【力扣刷题|第十七天】0-1 背包 完全背包

目标和 力扣题目网址:目标和 这道题我们先用回溯的思想来做。首先我们设正数和为S&#xff0c;数组和为N&#xff0c;目标值为T&#xff0c;那么S-(N-S)T化简之后可以得S(TN)/2即选择的正数个数为偶数&#xff0c;而且NT也为偶数&#xff0c;那么第一个判断条件我们就有了&…

深入浅出 Embedding

1. 什么是 Embedding? Embedding(嵌入)是一种将高维数据映射到低维连续空间的技术,用于表达数据的语义关系。简单来说,它是一种向量化表示,将文本、图像、用户行为等信息转换为数值向量,使得相似的数据在向量空间中距离更近。 2. 如何理解 Embedding? 2.1 浅显易懂的…

【云服务器】在Linux CentOS 7上快速搭建我的世界 Minecraft Fabric 服务器搭建,Fabric 模组详细搭建教程

【云服务器】在Linux CentOS 7上快速搭建我的世界 Minecraft Fabric 服务器搭建&#xff0c;Fabric 模组详细搭建教程 一、 服务器介绍二、安装 JDK 21三、搭建 Minecraft 服务端四、本地测试连接五、如何添加模组&#xff08;mods&#xff09;六、添加服务&#xff0c;并设置开…

【MLP-BEV(10)】BEVPooling V1和BEVPooling V2的view_transformer,进行鱼眼图片实践

文章目录 先说说 BEVPoolv1步骤1:3D点生成步骤2 2D特征采样和BEV特征生成特点再谈谈BEVPoolv2步骤1:3D点生成步骤2: 计算索引关系步骤3: `voxel_pooling`计算鱼眼图片进行实践步骤1、3D点生成(基于Kannala-Brandt 进行调整)步骤2、2D特征采样和BEV特征生成(1) 体素化 (Voxe…

鸿蒙项目源码-天气预报app-原创!原创!原创!

鸿蒙天气预报项目源码包运行成功含文档ArkTS语言。 我半个月写的原创作品&#xff0c;请尊重原创。 原创作品&#xff0c;盗版必究&#xff01;&#xff01;&#xff01;&#xff01; 原创作品&#xff0c;盗版必究&#xff01;&#xff01;&#xff01;&#xff01; 原创作品…

告别桌面杂乱与充电焦虑,移速165W百变桌面充电站首发体验

告别桌面杂乱与充电焦虑&#xff0c;移速165W百变桌面充电站首发体验 哈喽小伙伴们好&#xff0c;我是Stark-C~ 先如今&#xff0c;家里的电子产品越来越多&#xff0c;手机、平板、电脑三件套已经是基础配置&#xff0c;还有相机、Switch、智能手表等&#xff0c;这些产品用…

skill插件教程——skill程序的组成以及调用方法

skill程序的基本组成 1、基础的程序文件 插件运行的基础——就是你写程序的文件&#xff0c;格式为il文件&#xff0c;就是文本文件格式 2、调用程序的文件——allegro.ilint 文件申明在那个位置——在这个文件夹下&#xff0c;写入你调用的函数。 例如load&#xff08;“…

解决Dubbo3调用Springcloud接口报No provider available from registry RegistryDirectory

解决Dubbo调用Springcloud接口报No provider available from registry RegistryDirectory 问题发现问题解决 问题发现 在学习Dubbo过程中&#xff0c;Dubbo官网有一篇文章《微服务最佳实践&#xff0c;零改造实现 Spring Cloud & Apache Dubbo 互通》&#xff0c;跟着示例…

基于RFID技术建筑物资材料智能管理解决方案

建筑行业仓库和物资材料管理面临诸多挑战&#xff0c;如工程设备重复利用的管理需求、物资出入库管理不规范、账物不符、物资丢失等问题。特别是在复杂多变的工地环境中&#xff0c;对物资进行科学规范的管理难度极大。上海岳冉基于RFID技术的建筑物资材料智能管理解决方案聚焦…

WSL系统找不到指定的文件

问题介绍 在尝试使用linux子系统时&#xff0c;发现无法打开 在尝试使用docker时无法使用 在命令行cmd或者powershell使用wls相关命令时&#xff0c;报错 相关错误提示均为&#xff1a; 系统找不到指定的文件 解决方法 试了各种方法无效。 直接到github下载最新版的wsl安装…

海量数据处理

1.海量数据处理问题 给两个文件&#xff0c;分别有100亿个query&#xff0c;只有1G内存&#xff0c;如何找到两个文件交集&#xff1f; 解决方案一&#xff1a; 可以先用布隆过滤器&#xff0c;一个文件的query放进布隆过滤器&#xff0c;另一个文件依次查找&#xff0c;在的…

英伟达GB300新宠:新型LPDDR5X SOCAMM内存

随着人工智能&#xff08;AI&#xff09;、机器学习&#xff08;ML&#xff09;和高性能计算&#xff08;HPC&#xff09;应用的快速发展&#xff0c;对于高效能、大容量且低延迟内存的需求日益增长。NVIDIA在其GB系列GPU中引入了不同的内存模块设计&#xff0c;以满足这些严格…