【在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 年,语言模…...
Linux应用开发之网络套接字编程(实例篇)
服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...
SciencePlots——绘制论文中的图片
文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了:一行…...
遍历 Map 类型集合的方法汇总
1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...
2024年赣州旅游投资集团社会招聘笔试真
2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...
【第二十一章 SDIO接口(SDIO)】
第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...
postgresql|数据库|只读用户的创建和删除(备忘)
CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...
oracle与MySQL数据库之间数据同步的技术要点
Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异,它们的数据同步要求既要保持数据的准确性和一致性,又要处理好性能问题。以下是一些主要的技术要点: 数据结构差异 数据类型差异ÿ…...
OkHttp 中实现断点续传 demo
在 OkHttp 中实现断点续传主要通过以下步骤完成,核心是利用 HTTP 协议的 Range 请求头指定下载范围: 实现原理 Range 请求头:向服务器请求文件的特定字节范围(如 Range: bytes1024-) 本地文件记录:保存已…...
MySQL用户和授权
开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务: test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...
初学 pytest 记录
安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...
