Linux网络编程套接字

目录

前言

一、预备知识

1.1 源IP地址和目的IP地址

1.2 区分端口号和进程ID

1.3 TCP协议和UDP协议

1.4 网络字节序

二、socket编程接口

2.1 socket套接字的概念

2.2 socket常见API

2.3 sockaddr结构

三、关于IP和Port的绑定问题

四、编写简单的UDP服务端和客户端


前言

        在进入本章内容学习前,我们先要了解网络编程是什么:

        实际上网路编程指的是网络上的主机通过不同的进程,以编程的方式实现网络通信。

一、预备知识

1.1 源IP地址和目的IP地址

        在IP数据包头部中有两个IP地址分别叫做IP地址目的IP地址源IP地址和目的IP地址在进行网络通讯时是不会改变的,你可以理解为外出旅行时的出发地和目的地在旅行过程中是不会改变的。

1.2 区分端口号和进程ID

进程PID已经能够标识一台主机上进程的唯一性了,为什么还要搞一个端口号?

  1. 不是所有的进程都要网络通信,但所有的进程都要有PID;
  2. 更重要的说应该是要实现系统和网络功能的解耦。        

        IP标定互联网中唯一一台主机,端口号port用来标识该主机上的唯一的一个进程,因此

IP地址+端口号(port)能够标识全网内唯一的一个进程。

        另外,一个进程可以绑定多个端口号; 但是一个端口号不能被多个进程绑定

        传输层协议(TCP和UDP)的数据段中有两个端口号, 分别叫做源端口号和目的端口号。就是在描述 "数据是谁发的,要发给谁。

1.3 TCP协议和UDP协议

TCP协议(Transmission Control Protocol 传输控制协议):

  • 传输层协议
  • 有连接
  • 可靠传输
  • 面向字节流

UDP协议(User Datagram Protocol 用户数据报协议):

  • 传输层协议
  • 无连接
  • 不可靠传输
  • 面向数据报

1.4 网络字节序

        我们已经知道,内存中的多字节数据相对于内存地址有大端和小端之分,磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分, 网络数据流同样有大端小端之分。那么如何定义网络数据流的地址呢?

  • 发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出;
  • 接收主机把从网络上接到的字节依次保存在接收缓冲区中,也是按内存地址从低到高的顺序保存;
  • 因此,网络数据流的地址应这样规定:先发出的数据是低地址,后发出的数据是高地址.
  • TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节.
  • 不管这台主机是大端机还是小端机, 都会按照这个TCP/IP规定的网络字节序来发送/接收数据;
  • 如果当前发送主机是小端, 就需要先将数据转成大端; 否则就忽略, 直接发送即可。

        为使网络程序具有可移植性, 使同样的 C 代码在大端和小端计算机上编译后都能正常运行 , 可以调用以下库函数做网络字节序和主机字节序的转换:
  • 这些函数名很好记,h表示host,n表示network,l表示32位长整数,s表示16位短整数;
  • 例如htonl表示将32位的长整数从主机字节序转换为网络字节序,例如将IP地址转换后准备发送;
  • 如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回;
  • 如果主机是大端字节序,这些 函数不做转换,将参数原封不动地返回。

二、socket编程接口

2.1 socket套接字的概念

由系统提供用于网络通信的技术,是基于TCP/IP协议的网络通信的基本操作单元。
基于Socket套接字的网络程序开发就是网络编程。

套接字编程的种类:

  • 域间套接字编程(一般用于一个主机内的多个进程通信)
  • 原始套接字编程(通常是网络工具)
  • 网络套接字编程(用户间的网络通信)

2.2 socket常见API

// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);// 绑定端口号 (TCP/UDP, 服务器) 
int bind(int socket, const struct sockaddr *address,socklen_t address_len);// 开始监听socket (TCP, 服务器)
int listen(int socket, int backlog);// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address,socklen_t* address_len);// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);(头文件均为<sys/socket.h>)

 接收:

recvfrom() 函数用于从已连接或未连接的套接字接收数据,并将数据存储到缓冲区 buf 中。它的参数说明如下:

  • sockfd:套接字描述符。
  • buf:指向接收数据的缓冲区。
  • len:缓冲区的大小。
  • flags:额外的选项标志,通常设为 0。
  • src_addr:指向 struct sockaddr 结构体(见2.3)的指针,用于存储发送方的地址信息。
  • addrlen:指向 socklen_t 类型的指针,用于存储 src_addr 的长度。
  • 函数返回值为 ssize_t 类型,表示接收到的数据的字节数。如果返回 -1,则表示接收出错

发送:

sendto() 函数用于向指定的目标地址发送数据。它的参数说明如下:

  • sockfd:套接字描述符。
  • buf:指向要发送数据的缓冲区。
  • len:发送数据的大小。
  • flags:额外的选项标志,通常设为 0。
  • dest_addr:指向 struct sockaddr 结构体(见2.3)的指针,用于目标地址信息。
  • addrlen:指向 socklen_t 类型的指针,用于存储 src_addr 的长度。
  • 函数返回值为 ssize_t 类型,表示实际发送的数据的字节数。如果返回 -1,则表示接收出错

2.3 sockaddr结构

下面三种 sockaddr结构 分别对应:通用的网络地址结构体、IPv4地址结构体和Unix域地址结构体

接下来我们深入看看sockaddr_in的结构:

 框内第一个元素是端口号,第二个元素又是一个结构体类型:

可以看到它其实就是一个四字节的整数。

在使用时端口号是要在网络里来回发送的,因此必须保证端口号是网络字节序列;

同样的,IP如果要被网络使用,那么它也必须是网络字节序列的。

如果要把字符串IP转成四字节整型是不是很麻烦呢?

        其实系统早已为我们封装好了接口:

三、关于IP和Port的绑定问题

首先关于IP的绑定,若为0.0.0.0:

  1. 对于服务器:

    • 当服务器将IP地址绑定为 0.0.0.0 并且指定了特定的端口号时,服务器将会监听该端口号,并可以接受来自任何可用网络接口的连接请求。
    • 这意味着服务器将在所有网络接口上等待连接,包括本地回环接口(127.0.0.1)和物理网络接口。
    • 当有连接请求到达时,服务器可以根据需要选择特定的网络接口来处理连接。
  2. 对于客户端:

    • 当客户端将IP地址绑定为 0.0.0.0 并指定了特定的端口号时,客户端将通过任何可用的网络接口发送数据。
    • 这意味着客户端可以使用任何可用的网络接口来与目标服务器进行通信。

 关于Port的绑定:

        [0,1023]是系统内定的端口号,一般都有固定的应用层协议使用,如http:80 https:443

mysql:3306

四、编写简单的UDP服务端和客户端

UdpServer.hpp:

#pragma once#include <iostream>
#include <string>
#include <strings.h>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <functional>
#include "Log.hpp"// using func_t = std::function<std::string(const std::string&)>;
typedef std::function<std::string(const std::string&)> func_t;Log lg;enum{SOCKET_ERR=1,BIND_ERR
};uint16_t defaultport = 8080;
std::string defaultip = "0.0.0.0";
const int size = 1024;class UdpServer{
public:UdpServer(const uint16_t &port = defaultport, const std::string &ip = defaultip):sockfd_(0), port_(port), ip_(ip),isrunning_(false){}void Init(){// 1. 创建udp socketsockfd_ = socket(AF_INET, SOCK_DGRAM, 0); // PF_INETif(sockfd_ < 0){lg(Fatal, "socket create error, sockfd: %d", sockfd_);exit(SOCKET_ERR);}lg(Info, "socket create success, sockfd: %d", sockfd_);// 2. bind socketstruct sockaddr_in local;bzero(&local, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(port_); //需要保证我的端口号是网络字节序列,因为该端口号是要给对方发送的local.sin_addr.s_addr = inet_addr(ip_.c_str()); //1. string -> uint32_t 2. uint32_t必须是网络序列的 // ??// local.sin_addr.s_addr = htonl(INADDR_ANY);if(bind(sockfd_, (const struct sockaddr *)&local, sizeof(local)) < 0){lg(Fatal, "bind error, errno: %d, err string: %s", errno, strerror(errno));exit(BIND_ERR);}lg(Info, "bind success, errno: %d, err string: %s", errno, strerror(errno));}void Run(func_t func) // 对代码进行分层{isrunning_ = true;char inbuffer[size];while(isrunning_){struct sockaddr_in client;socklen_t len = sizeof(client);ssize_t n = recvfrom(sockfd_, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr*)&client, &len);if(n < 0){lg(Warning, "recvfrom error, errno: %d, err string: %s", errno, strerror(errno));continue;}inbuffer[n] = 0;std::string info = inbuffer;std::string echo_string = func(info);sendto(sockfd_, echo_string.c_str(), echo_string.size(), 0, (const sockaddr*)&client, len);}}~UdpServer(){if(sockfd_>0) close(sockfd_);}
private:int sockfd_;     // 网路文件描述符std::string ip_; // 任意地址bind 0uint16_t port_;  // 表明服务器进程的端口号bool isrunning_;
};

 UdpClient.cpp:

#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>using namespace std;void Usage(std::string proc)
{std::cout << "\n\rUsage: " << proc << " serverip serverport\n"<< std::endl;
}// ./udpclient serverip serverport
int main(int argc, char *argv[])
{if (argc != 3){Usage(argv[0]);exit(0);}std::string serverip = argv[1];uint16_t serverport = std::stoi(argv[2]);struct sockaddr_in server;bzero(&server, sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(serverport); //?server.sin_addr.s_addr = inet_addr(serverip.c_str());socklen_t len = sizeof(server);int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0){cout << "socker error" << endl;return 1;}// client 要bind吗?要!只不过不需要用户显示的bind!一般有OS自由随机选择!// 一个端口号只能被一个进程bind,对server是如此,对于client,也是如此!// 其实client的port是多少,其实不重要,只要能保证主机上的唯一性就可以!// 系统什么时候给我bind呢?首次发送数据的时候string message;char buffer[1024];while (true){cout << "Please Enter@ ";getline(cin, message);// std::cout << message << std::endl;// 1. 数据 2. 给谁发sendto(sockfd, message.c_str(), message.size(), 0, (struct sockaddr *)&server, len);struct sockaddr_in temp;socklen_t len = sizeof(temp);ssize_t s = recvfrom(sockfd, buffer, 1023, 0, (struct sockaddr*)&temp, &len);if(s > 0){buffer[s] = 0;cout << buffer << endl;}}close(sockfd);return 0;
}

main.cpp:

#include "UdpServer.hpp"
#include <memory>
#include <cstdio>void Usage(std::string proc)
{std::cout << "\n\rUsage: " << proc << " port[1024+]\n" << std::endl;
}std::string Handler(const std::string &str)
{std::string res = "Server get a message: ";res += str;std::cout << res << std::endl;return res;
}std::string ExcuteCommand(const std::string &cmd)
{// SafeCheck(cmd);FILE *fp = popen(cmd.c_str(), "r");if(nullptr == fp){perror("popen");return "error";}std::string result;char buffer[4096];while(true){char *ok = fgets(buffer, sizeof(buffer), fp);if(ok == nullptr) break;result += buffer;}pclose(fp);return result;
}// ./udpserver port
int main(int argc, char *argv[])
{if(argc != 2){Usage(argv[0]);exit(0);}uint16_t port = std::stoi(argv[1]);std::unique_ptr<UdpServer> svr(new UdpServer(port));svr->Init(/**/);svr->Run(Handler);return 0;
}
运行结果:

        

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

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

相关文章

基于Python网络爬虫的IT招聘就业岗位可视化分析推荐系统(完整系统源码+数据库+详细文档)

文章目录 基于Python网络爬虫的IT招聘就业岗位可视化分析推荐系统&#xff08;完整系统源码数据库详细文档&#xff09;源码获取在文章末尾项目概述招聘岗位数据爬虫分析系统展示用户注册登录系统首页IT招聘数据开发岗-javaIT招聘数据开发岗-PythonIT招聘数据开发岗-Android算法…

通过Jmeter压测存储过程

一、存储过程准备&#xff1a; 1、建立一个空表&#xff1a; CREATE TABLE test_data ( id NUMBER, name VARCHAR2(50), age NUMBER ); 2、建立一个存储过程&#xff1a; CREATE OR REPLACE PROCEDURE insert_test_data (n IN NUMBER) AS BEGIN--EXECUTE IMMEDIATE truncat…

如何高效率完成软件开发?试试低代码

一、前言 对于一个非技术出身的软件行业从业者而言&#xff0c;理解技术是一道坎&#xff0c;但也是向上发展所必须跨越的一道坎。在一个软件公司&#xff0c;无论是商务、售前还是项目经理亦或者是产品经理&#xff0c;对技术的理解程度&#xff0c;往往意味着他的专业程度。而…

开发Chrome插件,background.js中log打印未出现在控制台

不同于内容脚本&#xff08;通常命名content.js&#xff09;&#xff0c;在后台脚本&#xff08;通常命名background.js或service-worker.js&#xff09;中console.log并不会在控制台中直接显示。 要查看后台脚本上下文的正确控制台&#xff0c;执行如下步骤&#xff1a; 访问…

Facebook Horizon:探索虚拟现实中的社交空间

随着科技的不断进步&#xff0c;虚拟现实&#xff08;VR&#xff09;技术正成为社交互动和娱乐体验的新前沿。在这个数字时代&#xff0c;Facebook作为全球最大的社交媒体平台之一&#xff0c;正在引领虚拟社交的新时代&#xff0c;其推出的虚拟社交平台Facebook Horizon成为了…

ELF文件内容详解——各节内容分析

文章目录 写在前面准备.text节.data节.strtab.symtab.shstrtab.shstrtab之后 写在前面 只看readelf这个工具说实话我感觉还是有点云里雾里&#xff0c;这里就逐字节分析一下ELF文件中text节&#xff08;代码段&#xff09;的内容 本文分析使用的汇编程序ELF文件内容详解这篇文…

Redis之缓存雪崩问题解决方案

文章目录 一、书接上文二、介绍三、解决方案1. 锁2. 不同的过期时间3. 缓存预热和定时任务 一、书接上文 Redis之缓存穿透问题解决方案实践SpringBoot3Docker 二、介绍 缓存雪崩&#xff0c;指大量的缓存失效&#xff0c;大量的请求又同时落在数据库。主要的一种诱因是key设…

【Java面试】MQ(Message Queue)消息队列

目录 一、MQ介绍二、MQ的使用1应用解耦2异步处理3流量削峰4日志处理5消息通讯三、使用 MQ 的缺陷1.系统可用性降低:2.系统复杂性变高3.一致性问题四、常用的 MQActiveMQ:RabbitMQ:RocketMQ:Kafka:五、如何保证MQ的高可用?ActiveMQ:RabbitMQ:RocketMQ:Kafka:六、如何保…

数据湖Iceberg、Hudi和Paimon比较

1.社区发展现状 项目Apache IcebergApache HudiApache Paimon开源时间2018/11/62019/1/172023/3/12LicenseApache-2.0Apache-2.0Apache-2.0Github Watch1481.2k70Github Star5.3k4.9k 1.7k Github Fork1.9k2.3k702Github issue(Open)898481263Github issue(closed)20542410488…

【xss跨站漏洞】xss漏洞利用工具beef的安装

安装环境 阿里云服务器&#xff0c;centos8.2系统&#xff0c;docker docker安装 前提用root用户 安装docker yum install docker 重启docker systemctl restart docker beef安装 安装beef docker pull janes/beef 绑定到3000端口 docker run --rm -p 3000:3000 janes/beef …

小红书x-s算法及补环境 单旋转验证码

前言 大家好呀!新的一年,先祝大家新年快乐咯.祝大家逆向,风控都一把过咯. 新年第一篇文章,后续会持续更新哦! 春晚见证了中国经济的新风口,今年春晚互联网企业赞助商就两家,小红书和京东.小红书类似国外的ins,有预感未来小红书会大火,所以写了这篇文章,有需要的加我,联系方式…

MySQL多实例部署:从概念到实操的全面指南

目录 MySQL多实例管理 单实例 什么是多实例 多实例的好处 多实例的弊端 MySQL多实例用在哪些场景 资金紧张的公司 用户并发访问量不大的业务 大型网站也有用多实例 部署MySQL多实例 rpm和源码的优缺点 二进制方式安装mysql 准备二进制mysql运行所需的环境 准备多…

让 K8s 更简单!8款你不得不知的 AI 工具-Part 2

在 part 1 中&#xff0c;我们探讨了目前比较流行的四种 OpenAI 开源工具。在今天的 part 2 中我们将探究另外三种不同的 OpenAI 开源工具并介绍一些与 Appilot 相关的内容。 Kubectl-GPT Kubectl-GPT 是一个 kubectl 插件&#xff0c;可使用 GPT 模型从自然语言输入生成 kube…

MySQL加锁策略详解

我们主要从三个方面来讨论这个问题&#xff1a; 啥时候加&#xff1f;如何加&#xff1f;什么时候该加什么时候不该加&#xff1f; 1、啥时候加 1.1 显式锁 MySQL 的加锁可以分为显式加锁和隐式加锁&#xff0c;显式加锁我们比较好识别的&#xff0c;因为他往往直接体现在 S…

【Git工具实战】实用真实 Git 开发工作流程

前言 最近工作中发现&#xff0c;很多开发人员连最基本的Git怎么使用都不知道&#xff0c;比如什么时候切分支&#xff0c;什么时候合并代码&#xff0c;代码遇到冲突怎么办&#xff0c;经常出现掉代码&#xff0c;代码合并后丢失的情况。以下为个人总结的常规Git开发工作流程…

创建型设计模式 - 原型设计模式 - JAVA

原型设计模式 一 .简介二. 案例三. 补充知识 前言 这是我在这个网站整理的笔记,有错误的地方请指出&#xff0c;关注我&#xff0c;接下来还会持续更新。 作者&#xff1a;神的孩子都在歌唱 一 .简介 原型模式提供了一种机制&#xff0c;可以将原始对象复制到新对象&#xff0…

什么是车规级芯片?车规级芯片之NCV8450ASTT3G 自保护高压侧驱动器

关于车规级芯片&#xff1a; 关于车规级芯片&#xff08;Automotive Grade Chip&#xff09;&#xff0c;车规级芯片是专门用于汽车行业的芯片&#xff0c;具有高可靠性、高稳定性和低功耗等特点&#xff0c;以满足汽车电子系统的严格要求。这些芯片通常用于车载电子控制单元&…

mysql优化指南之原理篇

之前碰到一个线上问题&#xff0c;在接手一个同事的项目后&#xff0c;因为工期比较赶&#xff0c;我还没来得及了解业务背景和大致实现&#xff0c;只是了解了上线发布的顺序和验证方式就进行了上线&#xff0c;在上线进行金丝雀的时候系统还没发生什么异常&#xff0c;于是我…

数据结构D3作业

1. 2. 按位插入 void insert_pos(seq_p L,datatype num,int pos) { if(LNULL) { printf("入参为空&#xff0c;请检查\n"); return; } if(seq_full(L)1) { printf("表已满&#xff0c;不能插入\n"); …

ArcgisForJS如何使用ArcGIS Server发布的切片地图服务?

文章目录 0.引言1.准备海量地理数据2.ArcGIS Server发布切片地图服务3.ArcgisForJS使用ArcGIS Server发布的切片地图服务 0.引言 ArcGIS Server是一个由Esri开发的地理信息系统&#xff08;GIS&#xff09;服务器软件&#xff0c;它提供了许多功能&#xff0c;包括发布切片地图…