网络编程之套接字

端口 && IP

在学习套接字编程之前,我们必须了解一下前缀知识。首先是IP和端口的作用。

在这之前,我们要明白一件事。那就是把数据从一台主机发送到另一台主机,是目的吗???当然不是!!我们要把数据从一台主机发送到另一台主机指定中的一个进程,再由这个进程对数据进行处理后返回给用户。这才是目的!!那么我们通过IP找到了指定的主机,如何找到主机上指定的进程呢?? 那么我们就需要用到端口号(port)。

IP : 标识全网唯一一台主机(不太准确,暂时这么理解)

port 端口号 : 标识主机中唯一的一个进程

所以,IP + port 是不是就可以确定全网唯一 一个进程? 因为 IP确定全网唯一一台主机,端口号确定主机上唯一的一个进程。所以 IP + port 就可以确定全网唯一 一个进程。

而这个时候,我们主机与主机之间的通信,就变成了进程与进程之间的通信!进程间通信的本质是什么?是让多个进程可以看到同一份资源!在本地主机中这份资源可以是内存,也可以是文件。但是在多台主机中,这份资源就是我们熟悉的网络!!

所以我们可以用server把数据发送到网络中,再由对端的client从网络中读。就类似于读文件和写文件的操作一样,就可以实现跨网络的进程间通信!!

注意!

端口一旦确定,那么就不能改变!!因为在对应的客户端必须要指定服务器的端口,而此时如果服务器改变了自己的端口号,那么客户端也必须做到相应的改变!但显然客户并不会意识到这一点,所以端口号一旦确定,不要轻易改变!!!

TCP && UDP

接下来我们再浅浅了解一下TCP和UDP 。

TCP协议的特点

  • 传输层协议
  • 会建立连接
  • 可靠传输
  • 面向字节流

UDP协议的特点

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

有连接 && 无连接

显然,UDP和TCP都是传输层协议,那么建立连接这个怎么理解呢?这就很类似两个人打电话,必须要先有一方给另一方播电话。而另一方看到电话来了只有在接通之后两人才能进行通信。而这个过程就类似于TCP的连接过程。

而UDP是无连接的,就意味着你给对方发送数据压根就不用等对方确认。就相当于你给对方打电话,对方不能自己接通,而是电话自动接通。这就是无连接 。

**可靠传输 && 不可靠传输 **

首先我们要知道,可靠和不可靠在这里都是一个中性词。并不是一个贬义词,因此不能因为UDP是不可靠传输就说UDP差。我们要知道,保证可靠性是有代价的!! 因为TCP提供了各种可靠性机制,所以这也就意味着TCP会非常复杂!而UDP是不可靠性传输,就意味着它比较简单。各有各的优势,还是看场景进行选择。

注意!!无论是UDP和TCP,没有明显的谁慢谁快。因为跨网络传输看的是两台主机之间的距离和当前的网络状态!!与协议关系并不大。

面向字节流 && 面向数据报

面向字节流就是以字节流的形式进行报文的发送,那么这就意味着发送的时候,报文可能并不完整!比如你主机的传输层缓冲区已经满了,但是你的报文只接收了前面一点点。就只能等缓冲区被提取后才会接收后面的数据。这种现象就是面向字节流的!

面向数据报则是每次发送/接收,都必须是完整的报文!不存在断截之后再续上的现象,这就叫做面向数据报传输!!

网络字节序

我们都知道内存的字节存储方式有大端和小端。

大端存储 高位放在低字节序,低位放在高字节序 如:0X11223344 在内存中存储是 11 22 33 44

小端存储 低位放在低字节序,高位放在高字节序 如:0X11223344 在内存中存储是 44 33 22 11

既然不同的机器有不同的存储方式,那你怎么保证你小端发送的数据和大端接收的数据是匹配的?

很简单!!只要规定在网络上传输的数据都必须按大端字节序来存储!!发送发如果是小端则只需要转到大端发送即可,接收方是小端再由大端转回小端即可,如果是大端则不需要操作。

但是如果让我们自己来转换,那是不是太麻烦了呢?所以操作系统提供了下面的接口来供我们转换序列。

#include <arpa/inet.h>uint32_t htonl(uint32_t hostlong); //主机转网络序列,转成long类型
uint16_t htons(uint16_t hostshort); //主机转网络序列,转成short类型
uint32_t ntohl(uint32_t netlong); //网络序列转主机序列,转成long类型
uint16_t ntohs(uint16_t netshort); //网络序列转主机序列,转成short类型

其中的h代表主机host,to就是到的意思,n则是network网络的意思。l对应long,s对应short。所以上面的函数其实很好记忆。

Socket套接字编程

Socket是一个套接字,它的本质就是一个文件描述符。对应的是网卡文件,我们只需要往文件描述符里面写入,就可以把数据发送到网络。

函数介绍

套接字的创建(UDP/TCP/服务端/客户端)

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>int socket(int domain, int type, int protocol);

int domain : 通信范围,例如AF_UNIX,AF_LOCAL是本地通信,AF_INET是IPV4,AF_INET6是IPV6
int type: 通信类型,最常见的是TCP的SOCK_STREAM面向字节流,UDP的SOCK_DGRAM面向数据报
int protocol: 确定socket支持的哪个协议,一般默认为0即可。
返回值:一个文件描述符,小于0则代表创建套接字(打开文件)失败

端口号绑定(UDP/TCP/服务端/客户端)

对套接字绑定端口,一般服务器显示绑定。客户端OS自动绑定。

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>int bind(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);

*int sockfd: 要绑定的套接字文件描述符
const struct sockaddr addr :一个结构体的指针,该结构体包含了domain,port,ip等信息…
socklen_t addrlen:结构体体长度
返回值:成功返回0,绑定失败返回-1,或者返回错误码

这个结构体的首元素是一个16位地址类型,虽然下面的内容都不一样,但只要统一强转成 struct sockaddr,那么就可以拿到前16位。再根据前16位判断出结构体的类型,再做出对应的处理。我们可以把sockaddr 看成是一个父类,sockaddr_in 和 sockaddr_un 是它的子类。而无论我们是使用sockaddr_in还是sockaddr_un,我们都可以用sockaddr来接收。再根据前16位判断地址类型,做出选择。这就很像我们的多态,因为这个功能出来的时候,C语言没有void*指针这个功能,所以我们传入时必须要进行类型强转。

在这里插入图片描述

监听套接字(TCP/服务端)

UDP不会用到监听套接字这个函数,因为UDP是无连接的。而TCP是有连接的,所以必须把这个套接字设置为监听状态。

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>int listen(int sockfd, int backlog);

int sockfd:要监听的套接字
int backlog:连接同时存在的最大数量(因为监听是有消耗的,如果大量连接过来,监听不过来了,那么就会把这些连接暂时存储起来,而backlog就是存储的最大数量。)
返回值: 成功返回0,监听失败返回-1,或者返回错误码

接收连接请求(TCP/服务端)

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
int sockfd  监听后的套接字

**struct sockaddr addr: 存储对端domain,port,ip的结构体
socklen_t addrlen: 结构体的大小
返回值: 一个可以直接通信的套接字

建立连接(TCP/客户端)

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>int connect(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);

int sockfd:的套接字

**const struct sockaddr *addr: ** 对端的addr结构体,可以提取到对端的IP和端口

**socklen_t addrlen: ** 要接收的长度。

UDP通信测试

介绍2个函数,UDP传输要用的两个函数。 因为udp是无连接的,所以需要特定的函数来进行通信。

sendto函数

#include <sys/types.h>
#include <sys/socket.h>
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);

该函数的功能是往网络套接字中发送数据,如果调用该函数时套接字还没有绑定端口,那么该函数会自动为套接字随机分配端口。

**int sockfd: 套接字 **

*void buf, size_t len: 发送的数据

size_t len:发送数据的大小

int flags:发送方式,0为阻塞发送

*const struct sockaddr dest_addr:该参数包含着目标主机的IP和端口,根据该参数找到服务器

socklen_t addrlen:传入的sockaddr的长度

recvfrom函数

#include <sys/types.h>
#include <sys/socket.h>
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);

该函数的功能是往从网络套接字中读取数据。

int sockfd: 套接字

*void buf, size_t len: 读取数据的缓冲区

size_t len:读取数据的大小

int flags:是否阻塞读取,0为阻塞

*const struct sockaddr dest_addr:输出型参数,该参数对端主机的IP和端口,可以用来获取对端的主机IP和端口

socklen_t addrlen:接收的sockaddr 的长度

udp代码演示

服务器代码

server.cc文件

#include "server.hpp"
#include <memory>int main(int argc , char* argv[])
{if(argc != 2) //命令行参数不为2就退出{std::cout << "Usage : " << argv[0] << "   bindport" << std::endl;  //打印使用手册exit(1);}uint16_t port = atoi(argv[1]); //命令行传的端口转换成16位整形std::unique_ptr<UdpServer> s(new UdpServer(port)); //创建UDP服务器s->init(); //初始化服务器,创建 + 绑定s->start(); //运行服务器
}

server.hpp 代码:

#pragma once
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <iostream>
#include <unistd.h>
#include <arpa/inet.h>class UdpServer
{
private:int _sock; uint16_t _port;
public:UdpServer(uint16_t port): _port(port) { }~UdpServer() { close(_sock); }void init(){_sock = socket(AF_INET,SOCK_DGRAM,0);  //创建套接字if(_sock < 0){//创建失败std::cout << "create socket failed...." << std::endl;abort();}//绑定 struct sockaddr_in ser; ser.sin_port = htons(_port);  //填入端口ser.sin_family = AF_INET; // 填入地址类型ser.sin_addr.s_addr = INADDR_ANY; //填入IP地址if(bind(_sock,(sockaddr*)&ser,sizeof ser) != 0) //绑定{//绑定失败std::cout << "bind socket failed...." << std::endl;abort();}}void start(){struct sockaddr_in peer; //对端socklen_t peer_len = sizeof peer;char buff[1024] = {0};   while(1){//接收对端发来的消息int n = recvfrom(_sock,buff,1023,0,(struct sockaddr*)&peer,&peer_len); buff[n] = 0;if(read == 0) //对端关闭了{std::cout << "one client quit..." << std::endl;continue;}else if(read < 0) //读取出错了{std::cout << "read error..." << std::endl;break;}//读取正常...std::string clientip = inet_ntoa(peer.sin_addr); //获取对端的IPuint16_t clientport = ntohs(peer.sin_port);// 获取对端的端口//打印信息....std::cout << "["<< clientip <<"] "<< clientport << ": say : " << buff << std::endl; }} 
};

客户端代码

client.cc:

#include "client.hpp"
#include <memory>int main(int argc , char* argv[])
{if(argc != 3) //命令行参数少于3个退出,因为必须 ./client 服务器ip 端口号  才可以调用{std::cout << "Usage : " << argv[0] << "   serverip  serverport" << std::endl; exit(1);}uint16_t port = atoi(argv[2]); //获取服务器端口std::string ip = argv[1]; //获取服务器IPstd::unique_ptr<UdpClient> cli(new UdpClient(port,ip)); //创建客户端cli->init(); //初始化客户端cli->start(); //客户端运行
}

client.hpp

#pragma once
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <iostream>
#include <arpa/inet.h>class UdpClient
{
public: UdpClient(uint16_t port , const std::string& ip) : _port(port), _svr_ip(ip){}~UdpClient(){ close(_sock); }void init(){//创建套接字_sock = socket(AF_INET,SOCK_DGRAM,0); if(_sock < 0){std::cout << "create socket failed...." << std::endl;abort();}   }void start(){struct sockaddr_in svr; //该结构体填入服务器的端口和IPsvr.sin_port = htons(_port);  //填入端口svr.sin_addr.s_addr = inet_addr(_svr_ip.c_str());  //填入IPsvr.sin_family = AF_INET; //设置地址类型,ipv4int i = 1;  //打印数字,随意,可要可不要//char buff[1024] = {0};  如果需要读数据可用的输出缓冲区while(1){std::string message = "hello server " + std::to_string(i++); //构建发送消息//发送消息sendto(_sock,message.c_str(),message.size(),0,(struct sockaddr*)&svr,sizeof svr);//可以通过下面代码再接收服务器发送的数据,如果服务器有发送的话// int n = recvfrom(_sock,buff,1023,0,nullptr,nullptr);// buff[n] = 0 ;// std::cout << "server say# " << buff << std::endl;sleep(1); // 睡眠1s}}private: int _sock; //套接字uint16_t _port; //服务器的端口std::string _svr_ip; //服务的IP};

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

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

相关文章

socket.io介绍

1. 使用的技术 Socket.IO 是一个封装了 Websocket、基于 Node 的 JavaScript 框架&#xff0c;包含 client 的 JavaScript 和 server 的 Node。其屏蔽了所有底层细节&#xff0c;让顶层调用非常简单。 另外&#xff0c;Socket.IO 还有一个非常重要的好处。其不仅支持 WebSocket…

leetcode:232. 用栈实现队列

一、题目 原题链接&#xff1a;232. 用栈实现队列 - 力扣&#xff08;LeetCode&#xff09; 函数原型&#xff1a; typedef struct //我的队列结构定义 { } MyQueue; MyQueue* myQueueCreate() //我的队列创建及其初始化 void myQueuePush(MyQueue* obj, int x) //我的队…

生成对抗网络——研讨会

时隔一年&#xff0c;再跟着李沐大师学习了GAN之后&#xff0c;仍旧没能在离散优化中实现通用的应用&#xff0c;实在惭愧&#xff0c;借着组内研讨会的机会&#xff0c;再队GAN的前世今生做一个简单的综述。 GAN产生的背景 目前与GAN相关的应用 去reddit社区的机器学习板块…

〖大前端 - 基础入门三大核心之JS篇㊺〗- 定时器和延时器

说明&#xff1a;该文属于 大前端全栈架构白宝书专栏&#xff0c;目前阶段免费&#xff0c;如需要项目实战或者是体系化资源&#xff0c;文末名片加V&#xff01;作者&#xff1a;不渴望力量的哈士奇(哈哥)&#xff0c;十余年工作经验, 从事过全栈研发、产品经理等工作&#xf…

基于helm的方式在k8s集群中部署gitlab - 升级(三)

接上一篇 基于helm的方式在k8s集群中部署gitlab - 部署&#xff08;一&#xff09;&#xff0c;本篇重点对gitlab在k8s集群中进行升级 文章目录 1. gitlab 升级1.1 获取release1.2 下载目前版本的gitlab charts1.3 获取当前的values文件1.4 升级 2. gitlab数据库升级2.1 备份数…

力扣题:字符串的反转-11.22

力扣题-11.22 [力扣刷题攻略] Re&#xff1a;从零开始的力扣刷题生活 力扣题1&#xff1a;541. 反转字符串 II 解题思想&#xff1a;进行遍历翻转即可 class Solution(object):def reverseStr(self, s, k):""":type s: str:type k: int:rtype: str"&quo…

简单搭建Python开发环境

Python环境安装 Python官网: Welcome to Python.org 1. 选择Python3.x版本下载&#xff0c;建议使用稳定版3.9.13&#xff08;Stable Releases&#xff09;&#xff0c;绝大数库对3.9版本Python已良好支持&#xff0c;但对3.10及以上支持不完全&#xff1a; https://www.…

SSM框架(六):SpringBoot技术及整合SSM

文章目录 一、概述1.1 简介1.2 起步依赖1.3 入门案例1.4 快速启动 二、基础配置2.1 三种配置文件方式2.2 yaml文件格式2.3 yaml读取数据方式&#xff08;3种&#xff09; 三、多环境开发3.1 yml文件-多环境开发3.2 properties文件-多环境开发3.3 多环境命令行启动参数设置3.4 多…

2023年腾讯云双12优惠活动整理汇总

2023年双12腾讯云推出了年末感恩回馈活动&#xff0c;年度爆款2核2G4M云服务器118元/年&#xff0c;新老用户同享&#xff0c;还可领取总面值2000元代金券&#xff0c;老用户服务器续费4折起。本文为大家整理汇总腾讯云双12优惠活动。 活动地址&#xff1a; 点此直达腾讯云双1…

JOSEF 快速中间继电器 KZJ-4H-L DC220V 导轨安装

快速中间继电器KZJ-4H-LDC220V导轨安装导轨安装是广泛用于电力系统&#xff0c;能够断货开或开通大负载&#xff0c;并且具有较强的断弧能力&#xff0c;适用于交流50/60Hz。电压24380V,直流电压24280V自动控制电路中以增加保护和控制回路的触点数量与触点容量。 KZJ系列快速中…

“B2B+OMS方案”,赋能家电巨头构建BC订单一体化能力,促进业务增长|徐礼昭

某国际知名家电电器品牌&#xff0c;年营收超过5000亿元。该电器企业其整体业务分三大类&#xff1a;线上线下B2B2C业务、线下B2B业务以及DTC零售业务。 随着业务的发展&#xff0c;该电器品牌对2B业务及DTC业务的数字化系统能力支撑需要更加全面和立体&#xff0c;以适应业务…

【Matlab】如何快速入门一项新技能-以Matlab/Simulink入门为例

目录 1. 引言 2. 背景 3. 快速学习并完成开发 3.1 了解需求&#xff0c;知道要干什么 3.2 了解Matlab/Simulink基本功能 第一步&#xff0c;查看Matlab的中文网站中文网站https://www.ilovematlab.cn/resources/对Matlab/Simulink有了一个初步认识。 3.3 实现一个最简单…

技术阅读周刊第第8️⃣期

技术阅读周刊&#xff0c;每周更新。 历史更新 20231103&#xff1a;第四期20231107&#xff1a;第五期20231117&#xff1a;第六期20231124&#xff1a;第七期 Prometheus vs. VictoriaMetrics (VM) | Last9 URL: https://last9.io/blog/prometheus-vs-victoriametrics/?refd…

msyql迁移到mongodb

关系型数据库迁移到mongodb的理由 高并发需求&#xff0c;关系型数据库不容易扩展 快速迭代 灵活的json模式 大数据量需求 应用迁移难度&#xff1a; 关系型到关系 oracle-》mysql oracle -》 postgresql 关系到文档- oracle -》 mongodb 需要考虑&#xff1a; 总体架构&#…

【科技素养】蓝桥杯STEMA 科技素养组模拟练习试卷14

单选题 1、下列现象中有化学变化发生的是 A、蜡烛融化 B、冰块融化 C、电磁炉烧开水 D、铁生锈 答案&#xff1a;D 2、把左边的图形用剪刀剪开&#xff0c;拼成右边的正方形&#xff0c;至少剪几刀 A、1 B、2 C、3 D、4 答案&#xff1a;B 3、能够检验土壤中有沙和粘…

Vue---Echarts

项目需要用echarts来做数据展示&#xff0c;现记录vue3引入并使用echarts的过程。 1. 使用步骤 安装 ECharts&#xff1a;使用 npm 或 yarn 等包管理工具安装 ECharts。 npm install echarts 在 Vue 组件中引入 ECharts&#xff1a;在需要使用图表的 Vue 组件中&#xff0c;引入…

【Vulnhub 靶场】【HackathonCTF: 2】【简单】【20210620】

1、环境介绍 靶场介绍&#xff1a;https://www.vulnhub.com/entry/hackathonctf-2,714/ 靶场下载&#xff1a;https://download.vulnhub.com/hackathonctf/Hackathon2.zip 靶场难度&#xff1a;简单 发布日期&#xff1a;2021年06月20日 文件大小&#xff1a;2.6 GB 靶场作者&…

VS安装QT VS Tools编译无法通过

场景&#xff1a; 项目拷贝到虚拟机内部后&#xff0c;配置好相关环境后无法编译&#xff0c;安装QT VS Tools后依旧无法编译&#xff0c;查找资料网上说的是QT工具版本不一致导致的&#xff0c;但反复试了几个版本后依旧无法编译通过。错误信息如下&#xff1a; C:\Users\Ad…

奇葩问题:arp缓存、ip地址冲突(实际是ip地址被占用导致arp缓存出现问题)

文章目录 今天遇到个奇葩的问题 今天遇到个奇葩的问题 今天遇到个奇葩的问题&#xff0c;我把我们192.168.1.116的盒子ip改成192.168.2.116后&#xff0c;再改回来&#xff0c;发现我们盒子的http服务始终无法访问&#xff0c;用Advanced IP Scanner扫描一下&#xff0c;发现就…

【Qt开发流程】之自定义语法高亮和使用HTML语法

描述 语法高亮&#xff08;Syntax Highlighting&#xff09;是一种在编辑器中突出显示代码语法元素的技术&#xff0c;使其更易于阅读和理解。 Qt提供了一个功能齐全的语法高亮框架&#xff0c;支持多种语言和格式&#xff0c;可以自定义颜色和样式。 对于使用Qt的开发人员来说…