【在Linux世界中追寻伟大的One Piece】Socket编程TCP
目录
1 -> TCP socket API
2 -> V1 -Echo Server
2.1 -> 测试多个连接的情况
1 -> TCP socket API
socket():
- socket()打开一个网络通讯端口,如果成功的话,就像open()一样返回一个文件描述符。
- 应用程序可以像读写文件一样用read/write在网络上收发数据。
- 如果socket()调用出错则返回-1。
- 对于IPv4,family参数指定为AF_INET。
- 对于TCP协议,type参数指定为SOCK_STREAM,表示面向流的传输协议。
- protocol参数的介绍从略,指定为0即可。
bind():
- 服务器程序所监听的网络地址和端口号通常是固定不变的,客户端程序得知服务器程序的地址和端口号后就可以向服务器发起连接;服务器需要调用bind绑定一个固定的网络地址和端口号。
- bind()成功返回0,失败返回-1。
- bind()的作用是将参数sockfd和myaddr绑定在一起,使sockfd这个用于网络通讯的文件描述符监听myaddr所描述的地址和端口号。
- struct sockaddr *是一个通用指针类型,myaddr参数实际上可以接受多种协议的sockaddr结构体,而它们的长度各不相同,所以需要第三个参数addrlen指定结构体的长度。
我们的程序中对myaddr参数是这样初始化的:
- 将整个结构体清零。
- 设置地址类型为AF_INET。
- 网络地址为INADDR_ANY,这个宏表示本地的任意IP地址,因为服务器可能有多个网卡,每个网卡也可能绑定多个IP地址,这样设置可以在所有的IP地址上监听,直到与某个客户端建立了连接时才确定下来到底用哪个IP地址。
- 端口号为SERV_PORT,定义为9999。
listen():
- listen()声明sockfd处于监听状态,并且最多允许有backlog个客户端处于连接等待状态,如果接收到更多的连接请求就忽略,这里设置不会太大(一般是5)。
- listen()成功返回0,失败返回-1。
accept():
- 三次握手完成后,服务器调用accept()接受连接。
- 如果服务器调用accept()时还没有客户端的连接请求,就阻塞等待直到有客户端连接上来。
- addr是一个传出参数,accept()返回时传出客户端的地址和端口号。
- 如果给addr参数传NULL,表示不关心客户端的地址。
- addrlen参数是一个传入传出参数(value-result argument),传入的是调用者提供的,缓冲区addr的长度以避免缓冲区溢出问题,传出的是客户端地址结构体的实际长度(有可能没有占满调用者提供的缓冲区)。
我们的服务器程序结构是这样的:
connect
- 客户端需要调用connect()连接服务器。
- connect和bind的参数形式一致,区别在于bind的参数是自己的地址,而connect的参数是对方的地址。
- connect()成功返回0,出错返回-1。
2 -> V1 -Echo Server
nocopy.hpp
#pragma once
#include <iostream>class nocopy
{
public:nocopy() {}nocopy(const nocopy&) = delete;const nocopy& operator = (const nocopy&) = delete;~nocopy() {}
};
TcpServer.hpp
#pragma once
#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Log.hpp"
#include "nocopy.hpp"
#include "Comm.hpp"const static int default_backlog = 6; // TODO
class TcpServer : public nocopy
{
public:TcpServer(uint16_t port) : _port(port), _isrunning(false){}// 都是固定套路void Init(){// 1. 创建 socket, file fd, 本质是文件_listensock = socket(AF_INET, SOCK_STREAM, 0);if (_listensock < 0){lg.LogMessage(Fatal, "create socket error, errnocode: % d, error string : % s\n", errno, strerror(errno));exit(Fatal);}int opt = 1;setsockopt(_listensock, SOL_SOCKET, SO_REUSEADDR |SO_REUSEPORT, &opt, sizeof(opt));lg.LogMessage(Debug, "create socket success,sockfd: % d\n", _listensock);// 2. 填充本地网络信息并 bindstruct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(_port);local.sin_addr.s_addr = htonl(INADDR_ANY);// 2.1 bindif (bind(_listensock, CONV(&local), sizeof(local)) != 0){lg.LogMessage(Fatal, "bind socket error, errnocode: % d, error string : % s\n", errno, strerror(errno));exit(Bind_Err);}lg.LogMessage(Debug, "bind socket success, sockfd: %d\n",_listensock);// 3. 设置 socket 为监听状态,tcp 特有的if (listen(_listensock, default_backlog) != 0){lg.LogMessage(Fatal, "listen socket error, errnocode: % d, error string : % s\n", errno, strerror(errno));exit(Listen_Err);}lg.LogMessage(Debug, "listen socket success,sockfd: % d\n", _listensock);}// Tcp 连接全双工通信的.void Service(int sockfd){char buffer[1024];// 一直进行 IOwhile (true){ssize_t n = read(sockfd, buffer, sizeof(buffer) - 1);if (n > 0){buffer[n] = 0;std::cout << "client say# " << buffer <<std::endl;std::string echo_string = "server echo# ";echo_string += buffer;write(sockfd, echo_string.c_str(),echo_string.size());}else if (n == 0) // read 如果返回值是 0,表示读到了文件结尾(对端关闭了连接!){lg.LogMessage(Info, "client quit...\n");break;}else{lg.LogMessage(Error, "read socket error, errnocode: % d, error string : % s\n", errno, strerror(errno));break;}}}void Start(){_isrunning = true;while (_isrunning){// 4. 获取连接struct sockaddr_in peer;socklen_t len = sizeof(peer);int sockfd = accept(_listensock, CONV(&peer), &len);if (sockfd < 0){lg.LogMessage(Warning, "accept socket error, errnocode: % d, error string : % s\n", errno, strerror(errno));continue;}lg.LogMessage(Debug, "accept success, get n newsockfd: % d\n", sockfd);// 5. 提供服务, v1~v4// v1// Service(sockfd);close(sockfd);}}~TcpServer(){}
private:uint16_t _port;int _listensock; // TODObool _isrunning;
};
Comm.hpp
#pragma once
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>enum
{Usage_Err = 1,Socket_Err,Bind_Err,Listen_Err
};#define CONV(addr_ptr) ((struct sockaddr *)addr_ptr)
TcpClient.hpp
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Comm.hpp"
using namespace std;void Usage(const std::string& process)
{std::cout << "Usage: " << process << " server_ip server_port"<< std::endl;
}// ./tcp_client serverip serverport
int main(int argc, char* argv[])
{if (argc != 3){Usage(argv[0]);return 1;}std::string serverip = argv[1];uint16_t serverport = stoi(argv[2]);// 1. 创建 socketint sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){cerr << "socket error" << endl;return 1;}// 2. 要不要 bind?必须要有 Ip 和 Port, 需要 bind,但是不需要用户显示的 bind,client 系统随机端口// 发起连接的时候,client 会被 OS 自动进行本地绑定// // 2. connectstruct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(serverport);// p:process(进程), n(网络) -- 不太准确,但是好记忆inet_pton(AF_INET, serverip.c_str(), &server.sin_addr); // 1. 字符串 ip->4 字节 IP 2. 网络序列int n = connect(sockfd, CONV(&server), sizeof(server)); // 自动进行 bind 哦!if (n < 0){cerr << "connect error" << endl;return 2;}// 并没有向 server 一样,产生新的 sockfd.未来我们就用 connect 成功的sockfd 进行通信即可.while (true){string inbuffer;cout << "Please Enter# ";getline(cin, inbuffer);ssize_t n = write(sockfd, inbuffer.c_str(),inbuffer.size());if (n > 0){char buffer[1024];ssize_t m = read(sockfd, buffer, sizeof(buffer) - 1);if (m > 0){buffer[m] = 0;cout << "get a echo messsge -> " << buffer <<endl;}else if (m == 0 || m < 0){break;}}else{break;}}close(sockfd);return 0;
}
由于客户端不需要固定的端口号,因此不必调用bind(),客户端的端口号由内核自动分配。
注意:
- 客户端不是不允许调用bind(),只是没有必要显示的调用bind()固定一个端口号。否则如果在同一台机器上启动多个客户端,就会出现端口号被占用导致不能正确建立连接。
- 服务器也不是必须调用bind(),但如果服务器不调用bind(),内核会自动给服务器分配监听端口,每次启动服务器时端口号都不一样,客户端要连接服务器就会遇到麻烦。
2.1 -> 测试多个连接的情况
再启动一个客户端,尝试连接服务器,发现第二个客户端,不能正确的和服务器进行通信。
分析原因,是因为我们accecpt了一个请求之后,就在一直while循环尝试read,没有继续调用到 accecpt,导致不能接受新的请求。
我们当前的这个TCP,只能处理一个连接,这是不科学的。
感谢各位大佬支持!!!
互三啦!!!
相关文章:

【在Linux世界中追寻伟大的One Piece】Socket编程TCP
目录 1 -> TCP socket API 2 -> V1 -Echo Server 2.1 -> 测试多个连接的情况 1 -> TCP socket API socket(): socket()打开一个网络通讯端口,如果成功的话,就像open()一样返回一个文件描述符。应用程序可以像读写文件一样用r…...
进入半导体行业需要具备哪些能力?
要进入半导体公司,尤其是从事工艺流程设计和制程优化的岗位,需要具备一定的跨学科背景。 以某公司招聘要求为例: **公司 招聘岗位:工艺工程师 该公司是一家从事半导体设备、工艺与材料研发、生产和销售的公司,面向…...

Nature重磅:AI化学家再升级!大幅提升实验效率,推动化学合成进入“智能化”新阶段
人工智能(AI)驱动的机器人,正在我们的生活中扮演着越来越重要的角色,而在化学合成实验室内,它们也在悄然改变着传统实验方式。 如今,科学家们在智能化学领域取得了新突破—— 来自英国利物浦大学的研究团…...

源代码泄漏怎么办?SDC沙盒成为破局利器
在数字化时代,源代码安全已成为企业关注的焦点。源代码的泄露不仅可能导致知识产权的损失,还可能被竞争对手利用,给企业带来巨大的经济损失和法律风险。因此,采取有效的源代码防泄漏措施至关重要。深信达的SDC沙盒防泄密软件&…...

【论文复现】基于图卷积网络的轻量化推荐模型
本文所涉及所有资源均在这里可获取。 📕作者简介:热爱跑步的恒川,致力于C/C、Java、Python等多编程语言,热爱跑步,喜爱音乐、摄影的一位博主。 📗本文收录于论文复现系列,大家有兴趣的可以看一看…...

使用ssh-key免密登录服务器或免密连接git代码仓库网站
ssh登录服务器场景 假设有两台机器,分别是: 源机器:主机A(hostA),ip:198.168.0.1 目标机器:主机B(hostB),ip:192.168.0.2 ssh-key免…...

自由学习记录(19)
unity核心也算是看完了吧,但觉得的确是少了点东西,之后再看mvc框架,和网络开发,,感觉有必要想想主次顺序了,毕竟在明年的3月之前尽量让自己更有贴合需求的能力 先了解一些相关概念,不用看懂&am…...

Elasticsearch中时间字段格式用法详解
Elasticsearch中时间字段格式用法详解 攻城狮Jozz关注IP属地: 北京 2024.03.18 16:27:51字数 758阅读 2,571 Elasticsearch(简称ES)是一个基于Lucene构建的开源、分布式、RESTful搜索引擎。它提供了全文搜索、结构化搜索以及分析等功能,广泛…...

蓝桥杯-网络安全比赛题目-遗漏的压缩包
小蓝同学给你发来了他自己开发的网站链接, 他说他故意留下了一个压缩包文件,里面有网站的源代码, 他想考验一下你的网络安全技能。 (点击“下发赛题”后,你将得到一个http链接。如果该链接自动跳转到https,…...

ES海量数据插入如何优化性能?
2024年10月NJSD技术盛典暨第十届NJSD软件开发者大会、第八届IAS互联网架构大会在南京召开。百度文心快码总经理臧志分享了《AI原生研发新范式的实践与思考》,探讨了大模型赋能下的研发变革及如何在公司和行业中落地,AI原生研发新范式的内涵和推动经验。 …...

遥控救生圈,水上应急救援的新革命_鼎跃安全
水上事故发生后,时间就是生命。每一秒钟的延误,都可能增加溺水者失去生命的风险。传统的救援方式往往依赖人工迅速反应,但在大规模的紧急事件中,人工救援速度难以满足需求。而遥控救生圈的出现改变了这一切,它的作用在…...

【flask开启进程,前端内容图片化并转pdf-会议签到补充】
flask开启进程,前端内容图片化并转pdf-会议签到补充 flask及flask-socketio开启threading页面内容转图片转pdf流程前端主js代码内容转图片-browser端browser端的同步编程flask的主要功能route,def 总结 用到了pdf,来回数据转发和合成,担心flask卡顿,响应差,于是刚好看到threadi…...

Docker在CentOS上的安装与配置
前言 随着云计算和微服务架构的兴起,Docker作为一种轻量级的容器技术,已经成为现代软件开发和运维中的重要工具。本文旨在为初学者提供一份详尽的指南,帮助他们在CentOS系统上安装和配置Docker及相关组件,如Docker Compose和私有…...

【笔记】开关电源变压器设计 - 工作磁通的选择原则
变压器设计中有一个重要的输入参数,是选定电路工作的磁路参数。涉及到磁场的上下震荡最高幅度。如上图所示。磁场的方向有正负,所以如果电流在越过零点震荡,只考虑半周来和Bs或者Bmax比对即可。Bs,Bmax与特定材料有关。材料给出的最大Bmax,或…...

【VScode】如何在VSCode中配置Python开发环境:从零开始的完整指南
文章目录 前言软件准备软件安装1. 安装Python2. 检查Python是否安装成功3. 安装第三方包4. 安装VSCode 配置VSCode1. 安装Python插件2. 创建项目,配置工作区域3. 编写Python文件4. 配置Python编译设置5. 使用代码格式化工具yapf 更多文章结尾 前言 在当今的编程世界…...
Copy From 勇哥的机器视觉实验项目
形成一种有规划的学习模式,节省时间提升效率。 (一) 单相机标定 (1)halcon的标定助手 这个助手是常用工具,要注意的主要问题是 "品质问题","标定的精度"。 使用的标定板有两种类型,多种材质。选择时要配合灯光来选择材质。 例如玻璃标定板只适合背…...

在IDEA中使用Git
一、准备工作 这里我们使用 Gitee 做例子,使用 SSH 协议。看这个文章前最好看一下《》这个文章,了解一下 SSH。 1、生成秘钥对 首先要到 ~/.ssh 目录下进行操作,因为生成的公钥和私钥一般放在这个目录下,Windows 就是在用户目…...
分布式锁(防止同时操作同一条数据)实现分析
1. deleteLocked 方法: public R deleteLocked(String id, String username) {String examReportUserKey "examReportId_" id "_" username;stringRedisTemplate.delete(examReportUserKey);return R.ok(); } 功能:删除指定用户…...
【已解决,含泪总结】Ubuntu18.04下非root用户Anaconda3卸载重装,conda install终于不再报错
为什么要卸载重装 因为我最初安装的Anaconda3的版本是5.2.0,适合python3.6.5,其下的conda版本是4.5.4 我一开始本着能用则用,毕竟不是很懂的原则,尽量不要卸掉重来 但。。。bug像滚雪球一样,越来越多 conda install指…...

大语言模型(LLM)量化基础知识(一)
请大家关注我的知乎博客:- 派神 - - 知乎 随着大型语言模型 (LLM) 的参数数量的增长,与其支持硬件(加速器内存)增长速度之间的差距越来越大,如下图所示: 上图显示,从 2017 年到 2022 年,语言模…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式
一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明:假设每台服务器已…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻
在如今就业市场竞争日益激烈的背景下,越来越多的求职者将目光投向了日本及中日双语岗位。但是,一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧?面对生疏的日语交流环境,即便提前恶补了…...
【Linux】shell脚本忽略错误继续执行
在 shell 脚本中,可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行,可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令,并忽略错误 rm somefile…...

CTF show Web 红包题第六弹
提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...

微信小程序 - 手机震动
一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注:文档 https://developers.weixin.qq…...
第25节 Node.js 断言测试
Node.js的assert模块主要用于编写程序的单元测试时使用,通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试,通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...

成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战
在现代战争中,电磁频谱已成为继陆、海、空、天之后的 “第五维战场”,雷达作为电磁频谱领域的关键装备,其干扰与抗干扰能力的较量,直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器,凭借数字射…...
【Java学习笔记】BigInteger 和 BigDecimal 类
BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点:传参类型必须是类对象 一、BigInteger 1. 作用:适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...

论文笔记——相干体技术在裂缝预测中的应用研究
目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术:基于互相关的相干体技术(Correlation)第二代相干体技术:基于相似的相干体技术(Semblance)基于多道相似的相干体…...
return this;返回的是谁
一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...