当前位置: 首页 > article >正文

网络--socket编程(2)

Socket 编程 TCP

TCP 网络程序
和刚才 UDP 类似 . 实现一个简单的英译汉的功能
TCP socket API 详解
下面介绍程序中用到的 socket API, 这些函数都在 sys/socket.h 中。
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
指定结构体的长度 ;
listen():
listen()申明处于监听状态 , 并且最多允许有 backlog 个客户端处于连接等待状态, 如果接收到更多的连接请求就忽略 , 这里设置不会太大 ( 一般是 5), 具体细节同学们课后深入研究;
listen() 成功返回 0, 失败返回 -1;
accept():
三次握手完成后 , 服务器调用 accept() 接受连接 ;
如果服务器调用 accept() 时还没有客户端的连接请求 , 就阻塞等待直到有客户端
连接上来 ;
addr 是一个传出参数 ,accept() 返回时传出客户端的地址和端口号 ;
如果给 addr 参数传 NULL, 表示不关心客户端的地址 ;
addrlen 参数是一个传入传出参数 (value-result argument), 传入的是调用者提
供的 , 缓冲区 addr 的长度以避免缓冲区溢出问题 , 传出的是客户端地址结构体的实
际长度 ( 有可能没有占满调用者提供的缓冲区 );
理解 accecpt 的返回值 : 饭店拉客例子
connect
客户端需要调用 connect() 连接服务器 ;
connect bind 的参数形式一致 , 区别在于 bind 的参数是自己的地址 ,
connect 的参数是对方的地址 ;
connect() 成功返回 0, 出错返回 -1;
V1 - Echo Server
TcpServer.hpp
C++
#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, errno
code: %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. 填充本地网络信息并 bind
struct 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 bind
if (bind(_listensock, CONV(&local), sizeof(local)) != 0)
{
lg.LogMessage(Fatal, "bind socket error, errno
code: %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, errno
code: %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];
// 一直进行 IO
while (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, errno
code: %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, errno
code: %d, error string: %s\n", errno, strerror(errno));
continue;
}
lg.LogMessage(Debug, "accept success, get n new
sockfd: %d\n", sockfd);
// 5. 提供服务啊, v1~v4
// v1
Service(sockfd);
close(sockfd);
}
}
~TcpServer()
{
}
private:
uint16_t _port;
int _listensock; // TODO
bool _isrunning;
};
Comm.hpp
C++
#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.cc

C++
#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. 创建 socket
int 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. connect
struct 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(), 内核会自动给服务器分配监听端口, 每次启动服务器时端口号都不一样 , 客户端要连接服务器就会遇到
麻烦 ;
测试多个连接的情况
再启动一个客户端 , 尝试连接服务器 , 发现第二个客户端 , 不能正确的和服务器进行通信 . 分析原因, 是因为我们 accecpt 了一个请求之后 , 就在一直 while 循环尝试 read , 没有继续调用到 accecpt , 导致不能接受新的请求 .
我们当前的这个 TCP , 只能处理一个连接 , 这是不科学的。

相关文章:

网络--socket编程(2)

Socket 编程 TCP TCP 网络程序 和刚才 UDP 类似 . 实现一个简单的英译汉的功能 TCP socket API 详解 下面介绍程序中用到的 socket API, 这些函数都在 sys/socket.h 中。 socket(): • socket() 打开一个网络通讯端口 , 如果成功的话 , 就像 open() 一样返回一个…...

【FreeRTOS进阶】优先级翻转现象详解及解决方案

【FreeRTOS进阶】优先级翻转现象详解及解决方案 接下来我们聊聊优先级翻转这个经典问题。这个问题在实时系统中经常出现&#xff0c;尤其是在任务较多的场景下&#xff0c;而且问题定位起来比较麻烦。 什么是优先级翻转&#xff1f; 优先级翻转的核心定义很简单&#xff1a;…...

结合建筑业务讲述TOGAF标准处理哪种架构

TOGAF标准处理哪种架构 内容介绍业务架构业务策略&#xff0c;治理&#xff0c;组织和关键业务流程数据架构组织的逻辑和物理数据资产以及数据管理资源的结构应用架构待部署的各个应用程序&#xff0c;它们之间的交互以及与组织核心业务流程的关系的蓝图技术架构支持业务&#…...

C++入门小馆: 深入string类(一)

嘿&#xff0c;各位技术潮人&#xff01;好久不见甚是想念。生活就像一场奇妙冒险&#xff0c;而编程就是那把超酷的万能钥匙。此刻&#xff0c;阳光洒在键盘上&#xff0c;灵感在指尖跳跃&#xff0c;让我们抛开一切束缚&#xff0c;给平淡日子加点料&#xff0c;注入满满的pa…...

NHANES指标推荐:WWI

文章题目&#xff1a;Weight-adjusted waist circumference index with hepatic steatosis and fibrosis in adult females: a cross-sectional, nationally representative study (NHANES 2017-2020) DOI&#xff1a;10.1186/s12876-025-03706-4 中文标题&#xff1a;体重调整…...

2025.04.18|【Map】地图绘图技巧全解

Add circles Add circles on a Leaflet map Change tile Several background tiles are offered by leaflet. Learn how to load them, and check the possibilities. 文章目录 Add circlesChange tile 2025.04.18【Map】| 地图绘图技巧全解1. 准备工作2. 地理区域着色图&…...

PR第一课

目录 1.新建 2.PR内部设置 3.导入素材 4.关于素材窗口 5.关于编辑窗口 6.序列的创建 7.视频、图片、音乐 7.1 带有透明通道的素材 8.导出作品 8.1 打开方法 8.2 导出时&#xff0c;需要修改的参数 1.新建 2.PR内部设置 随意点开 编辑->首选项 中的任意内容&a…...

C# 预定义类型全解析

在 C# 编程中&#xff0c;预定义类型是基础且重要的概念。下面我们来详细了解 C# 的预定义类型。 预定义类型概述 C# 提供了 16 种预定义类型&#xff0c;包含 13 种简单类型和 3 种非简单类型。所有预定义类型的名称都由全小写字母组成。 预定义简单类型 预定义简单类型表…...

@EnableAsync+@Async源码学习笔记之六

接上文&#xff0c;我们本文分析 AsyncExecutionAspectSupport 的源码&#xff1a; package org.springframework.aop.interceptor;import java.lang.reflect.Method; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFu…...

Java CMS和G1垃圾回收器

举个真带劲的例子&#xff1a;把JVM内存比作你家的祖传旱厕 想象你有个祖传旱厕&#xff0c;分三个坑&#xff1a; 新坑区&#xff08;年轻代&#xff09;&#xff1a;刚拉的屎热乎着&#xff08;新对象&#xff09;陈年坑&#xff08;老年代&#xff09;&#xff1a;风干的屎…...

Vue+Notification 自定义消息通知组件 支持数据分页 实时更新

效果图&#xff1a; message.vue 消息组件 子组件 <template><div class"custom-notification"><div class"content"><span click"gotoMessageList(currentMessage.split()[1])">{{ currentMessage.split()[0] }}</…...

不规则曲面上两点距离求取

背景 在CT中求皮肤上两点间的弧长。由于人体表面并不是规则的曲面&#xff0c;不可能用圆的弧长求取方法来计算出两点间的弧长。 而在不规则的曲面上求两点的距离&#xff0c;都可以用类似测地线距离求取的方式来求取&#xff08;积分&#xff09;&#xff0c;而转化为搜索路…...

Redis面试问题缓存相关详解

Redis面试问题缓存相关详解 一、缓存三兄弟&#xff08;穿透、击穿、雪崩&#xff09; 1. 穿透 问题描述&#xff1a; 缓存穿透是指查询一个数据库中不存在的数据&#xff0c;由于缓存不会保存这样的数据&#xff0c;每次都会穿透到数据库&#xff0c;导致数据库压力增大。例…...

性能比拼: Elixir vs Go

本内容是对知名性能评测博主 Anton Putra Elixir vs Go (Golang) Performance (Latency - Throughput - Saturation - Availability) 内容的翻译与整理, 有适当删减, 相关指标和结论以原作为准 对比 Elixir 和 Go 简介 许多人长期以来一直要求我对比 Elixir 和 Go。在本视频…...

精益数据分析(6/126):深入理解精益分析的核心要点

精益数据分析&#xff08;6/126&#xff09;&#xff1a;深入理解精益分析的核心要点 在创业和数据驱动的时代浪潮中&#xff0c;我们都在不断探索如何更好地利用数据推动业务发展。我希望通过和大家分享对《精益数据分析》的学习心得&#xff0c;一起在这个充满挑战和机遇的领…...

【Linux网络与网络编程】11.数据链路层mac帧协议ARP协议

前面在介绍网络层时我们提出来过一个问题&#xff1a;主机是怎么把数据交给路由器的&#xff1f;那里我们说这是由数据链路层来做的。 网络上的报文在物理结构上是以mac帧的形式流动的&#xff0c;但在逻辑上是以IP流动的&#xff0c;IP的流动是需要mac帧支持的。 数据链路层解…...

JAVA设计模式:注解+模板+接口

1.基础组件 1.1注解类控制代码执行启动、停止、顺序 /*** author : test* description : 数据同步注解* date : 2025/4/18*/ Target({ElementType.TYPE}) Retention(RetentionPolicy.RUNTIME) Documented public interface SyncMeta {/*** 执行服务名称* return*/String name…...

Linux系统编程 day6 进程间通信mmap

父子共享的信息&#xff1a;文件描述符&#xff0c;mmap建立的共享映射区&#xff08;MAP_SHARED&#xff09; mmap父子间进程通信 var的时候 &#xff1a;读时共享&#xff0c;写时复制 父进程先创建映射区&#xff0c;指定共享MAP_SHARED权限 &#xff0c; fork创建子进程…...

【MySQL】MySQL建立索引不知道注意什么?

基本原则&#xff1a; 1.选择性原则&#xff1a; 选择高选择性的列建立索引(该列有大量不同的值) 2.适度原则&#xff1a;不是越多越好&#xff0c;每个索引都会增加写入开销 列选择注意事项&#xff1a; 1.常用查询条件列&#xff1a;WHERE字句中频繁使用的列 2.连接操作列…...

定制一款国密浏览器(9):SM4 对称加密算法

上一章介绍了 SM3 算法的移植要点,本章介绍对称加密算法 SM4 的移植要点。 SM4 算法相对 SM3 算法来说复杂一些,但还是比较简单的算法,详细算法说明参考《GMT 0002-2012 SM4分组密码算法》这份文档。铜锁开源项目的实现代码在 sm4.c 文件中,直接拿过来编译就可以。 但需要…...

Redis 的持久化机制(RDB, AOF)对微服务的数据一致性和恢复性有何影响?如何选择?

Redis 的持久化机制&#xff08;RDB 和 AOF&#xff09;对于保证 Redis 服务重启或崩溃后数据的恢复至关重要&#xff0c;这直接影响到依赖 Redis 的微服务的数据一致性和恢复能力。 1. RDB (Redis Database Backup) 机制: 在指定的时间间隔内&#xff0c;将 Redis 在内存中的…...

lottie深入玩法

A、json文件和图片资源分开 delete 是json资源名字 /res/lottie/delete_anim_images是图片资源文件夹路径 JSON 中引用的图片名&#xff0c;必须与实际图片文件名一致 B、json文件和图片资源分开&#xff0c;并且图片加载不固定 比如我有7张图片&#xff0c;分别命名1~7&…...

Android学习总结之算法篇七(图和矩阵)

有向图的深度优先搜索&#xff08;DFS&#xff09;和广度优先搜索&#xff08;BFS&#xff09;的示例&#xff0c;以此来模拟遍历 GC Root 引用链这种有向图结构&#xff1a; 一、深度优先搜索&#xff08;DFS&#xff09; import java.util.*;public class GraphDFS {privat…...

docker 大模型

使用 Docker 实现大模型的步骤指南 在今天的文章中&#xff0c;我们将指导刚入行的小白如何使用 Docker 来运行大模型。Docker 是一个开放源代码的平台&#xff0c;允许开发者自动化应用程序的部署、扩展和管理。通过将大模型放入 Docker 容器中&#xff0c;我们可以确保其在各…...

热门与冷门并存,25西电—电子工程学院(考研录取情况)

1、电子工程学院各个方向 2、电子工程学院近三年复试分数线对比 学长、学姐分析 由表可看出&#xff1a; 1、电子科学与技术25年相较于24年上升20分 2、信息与通信工程、控制科学与工程、新一代电子信息技术&#xff08;专硕&#xff09;25年相较于24年下降25分 3、25vs24推…...

Warcraft Logs [Classic] [WCL] BOSS ID query

Warcraft Logs [Classic] [WCL] BOSS ID query 所有副本BOSSID查询 https://wowpedia.fandom.com/wiki/DungeonEncounterID#Retail IDNameMapInstanceIDPatch227High Interrogator GerstahnBlackrock Depths230228Lord RoccorBlackrock Depths230229Houndmaster GrebmarBlackro…...

python录屏工具实现

python录屏工具实现 实现一 按Ctrl+Shift+8开始录制,按Ctrl+Shift+9结束录制,视频保存到“ d:\录屏视频”目录中。 先看用了哪些库 import cv2: 引入 OpenCV 库,这是一个开源计算机视觉库,用于图像和视频处理。在这个程序中,它用于创建视频文件、处理图像等。需要安装ope…...

架构师面试(三十一):IM 消息收发逻辑

问题 今天聊一下 IM 系统最核心的业务逻辑。 在上一篇短文《架构师面试&#xff08;三十&#xff09;&#xff1a;IM 分层架构》中详细分析过&#xff0c;IM 水平分层架构包括&#xff1a;【入口网关层】、【业务逻辑层】、【路由层】和【数据访问层】&#xff1b;除此之外&a…...

基于若依框架前后端分离的项目部署

文章目录 单项目的部署项目目录后端打包上传前端打包上传配置nginx服务器打开防火墙完成 两个项目的部署两个项目介绍后端打包并上传前端打包并上传nginx配置服务器端口开放完成 腾讯云服务器 之 环境搭建 单项目的部署 项目目录 后端打包上传 查看端口号 在ruoyi-admin的appl…...

黑马Java基础笔记-1

JVM&#xff0c;JDK和JRE JDK是java的开发环境 JVM虚拟机&#xff1a;Java程序运行的地方 核心类库&#xff1a;Java已经写好的东西&#xff0c;我们可以直接用。 System.out.print中的这些方法就是核心库中的所包含的 开发工具: javac&#xff08;编译工具&#xff09;、java&…...