网络编程套接字——UDP

news2024/11/19 7:43:47

一、基础知识

1.区分源地址、目的地址

(1)源IP地址和目的地址:最开始的IP地址与送达数据的地址
(2)源MAC地址和目的MAC地址:相当于上一站的地址与下一站的地址,在不断地变化
socket通信,本质是进程间通信,只是是跨网络的进程间通信(如:客户端进程与服务器端进程的通信)

2.端口号

任何的网络服务与网络客户端,如果要进行正常的数据通信,必须要用端口号来唯一标识自身
(1)端口号是传输层的内容
(2)端口号是一个2字节16bit的整数
(3)端口号用来标识一个进程,告诉OS当前数据交给哪一个进程来处理
(4)IP地址+端口号:标识全网内唯一的一个进程
公网IP:唯一的标识全网内唯一的一台主机
端口号(port):标识一台主机上唯一的一个进程
(5)在同一个OS内,一个进程可以与一个端口号进行绑定,该端口号就在网络层面唯一标识一台主机上的唯一一个进程

ip+port的通信方式就叫做socket通信
进程PID VS 端口号
一台主机可能存在大量的进程(OS层面),但不是所有的进程都要对外进行网络数据请求(网络层面)(身份证号与公司员工号的关系,各有各的机制)

3.源端口号、目的端口号

源端口号:数据是谁发的
目的端口号:数据要发给谁

4.TCP、UDP(后面详细总结TCP、UDP异同)

都是传输层协议

TCPUDP
传输层协议传输层协议
有连接无连接
可靠传输不可靠传输面向数据报
面向字节流面向数据报

5.网络序列

网络上面的数据必须都是大端
(1)概念
大端模式:是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中;
小端模式:是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中
规定:网络上的数据必须都是大端
(2)为什么规定是大端?
发送主机通常将发送缓冲区的数据按内存地址从低到高的顺序发出;
接收主机把从网络接到的数据一次保存在接收缓冲区中,也是按照从低到高的地址顺序保存。

二、socket

1.socket编程接口(常见API)

(1)创建socket文件描述符 (TCP/UDP, 客户端 + 服务器)

函数:int socket(int domain, int type, int protocol);
domain:域,协议家族(16位地址类型)
type:服务类型,分为两种,SOCK_STREAM(TCP),SOCK_DGRAM(UDP)
protocol:协议类别,一般设为0,默认,根据前两个字段自动推断需要的是哪一个协议

(2) 绑定端口号 (TCP/UDP, 服务器)
int bind(int socket, const struct sockaddr address,socklen_t address_len);
(3)开始监听socket (TCP, 服务器)
int listen(int socket, int backlog);
4)接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr
address,socklen_t* address_len);
(5)建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

2.sockaddr结构

在这里插入图片描述

(1)IPv4和IPv6的地址格式定义在netinet/in.h中,IPv4地址用sockaddr_in结构体表示,包括16位地址类型, 16位端口号和32位IP地址.
(2)IPv4、IPv6地址类型分别定义为常数AF_INET、AF_INET6. 这样,只要取得某种sockaddr结构体的首地址,不需要知道具体是哪种类型的sockaddr结构体,就可以根据地址类型字段确定结构体中的内容.
(3)socket API可以都用struct sockaddr *类型表示, 在使用的时候需要强制转化成sockaddr_in; 这样的好处是程序的通用性, 可以接收IPv4, IPv6, 以及UNIX Domain Socket各种类型的sockaddr结构体指针做为参数

3.简单UDP客户端/服务端通信

udp_client.hpp

#pragma once
#include<iostream>
#include<string>
#include<cstring>
#include<sys/socket.h>
#include<sys/types.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<unistd.h>

class UdpClient
{
private:
	int sockfd;
	std::string server_ip;
	int server_port;
public:
	UdpClient(std::string _ip,int _port)
		:server_ip(_ip),server_port(_port)
	{}
	bool InitUdpClient()
	{
		sockfd = socket(AF_INET,SOCK_DGRAM,0);
		if(sockfd < 0)
		{
			std::cerr << "socket error" << std::endl;
			return false;
		}
		//客户端不需要绑定吗?(不需要)需要port吗?(需要)
	}
	void Start()
	{
		struct sockaddr_in peer;
		memset(&peer,0,sizeof(peer));
		
		peer.sin_family = AF_INET;
		peer.sin_port = htons(server_port);
		peer.sin_addr.s_addr = inet.addr(server_ip.c_str());
		
		for(;;)
		{
			std::cout << "Please enter#:" ;
			std::cin >> msg;
			sendto(sockfd,msg.c_str(),msg.size(),0,(struct sockaddr*)&peer,sizeof(peer));

			//读取消息
			char buffer[128];
			struct sockaddr_in temp;
			socklen_t len = sizeof(temp);
			ssize_t size = recvfrom(sockfd,buffer,sizeof(buffer)-1,0,(strucy sockaddr*)&temp,&len);
			if(size > 0) //读取成功
			{
				buffer[size] = 0;
				std::cout << buffer << std::endl;
			}
		}
	}
	~UdpClient()
	{
		if(sockfd >= 0)
			close(sockfd);
	}
};

udp_client.cc

#include"udp_client.hpp"

//3个参数:./udp_client   server_ip   server_port
int main(int agec,char * argv[])
{
	if(argc != 3)
	{
		std::cerr << "Usage:" << argv[0] << "server_ip server_port" << std::endl;
		return 1;
	}
	std::string ip = argv[1];
	int port = atoi(argv[2]);  //字符串转为整型

	UdpClient *ucli = new UdpClient(ip,port);
	ucli->InitUdpClient();

	ucli->Start();
	return 0;
}

udp_server.hpp

#pragma once
#include<iostream>
#include<string>
#include<cstring>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<sys/wait.h>

#define DEFAULT 8081
class UdpServer
{
private:
	//std::string ip;
	int sockfd;
	int port;
public:
	UdpServer(int _port = DEFAULT):port(_port),socket(-1)//,ip(_ip)
	{}
	//初始化
	bool InitUdpServer()
	{
		sockfd = socket(AF_INET, SOCK_DGRAM, 0);  //AF_INET是利用ipv4,也可以使用其它机制
		if(socket < 0)  //创建套接字失败
		{
			std::cerr << "socket error" << std::endl;  
			return false;
		}
		std::cout << "socket create success,sockfd:" << sockfd << std::endl;  //文件描述符是3,0 1  2 默认分配了,从3开始
		
		//绑定,即:文件和网络关联起来
		//sockaddr_in结构体,是bind()函数里的一个参数,需要填充信息
		//此结构体主要包括:地址类型,端口号,IP地址
		struct sockaddr local;
		memset(&local,'\0'.sizeof(local));
		local.sin_family = AF_INET;
		local.sin_port = htons(port);   //port需要发送到网络中,需要设置为网络序列
		//local.sin_addr.s.addr = inet_addr(ip.c_str());  //整数ip转换成4字节ip,inet_addr为C语言接口,则调用使用ip.cstr()(C++风格的转换成C语言字符串以'\0'结尾的)
		local.sin_addr.s_addr = INADDR_ANY;  //绑定0
		
		if(bind(sockfd,(struct sockaddr*)&local,sizeof(local)) < 0)
		{
			std::cerr << "bind error" << std::endl;
			return false;
		}
		std::cout << "bind success" << std::endl;
		return true;
	}
	//启动
	void Start
	{
#define SIZE 128  //定义的缓冲区大小
		char buffer[SIZE] = 0;
		//dup2(sockfd,1);  //重定向
		
		for(;;)
		{
			//读取数据
			struct sockaddr_in peer;
			socklen_t len = sizeof(peer);
			ssize_t size = recvfrom(sockfd,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,&len);
			if(size > 0)  //读取成功
			{
				buffer[size] = 0;
				int _port = ntohs(peer.sin_port);  //网络序列转成主机序列
				std::string _ip = inet_ntoa(peer.sin_addr);  //网络字节IP转换成字符串IP
				std::cout << _ip << ":" << port << "#" << buffer << std::endl;
				
				//其余想做的任务
				std::string cmd = buffer;
				std::string result;
				if(cmd == "ls")
				{
					int pipes[2];
					pipe(pipes);
					pid_t id = fork();
					if(id == 0)
					{
						//child
						close(pipes[0]);
						dup2(pipes[1],1);
						execl("/usr/bin/ls","ls","-a","-l","-i",nullptr);
						exit(-1);
					}
					close(pipes[1]);  //父进程关闭写
					char c;
					while(1)
					{
						if(read(pipes[0],&c,1) > 0)
						{
							result.push_back(c);
						}
						else
						{
							break;
						}
					}
					wait(nullptr);
				}
				std::string echo_msg;
				if(result.empty())
				{
					echo_msg += buffer;
					echo_msg = "server get!";
				}
				else
				{
					echo_msg = result;
				}



				//服务器返回内容
				//std::string echo_msg = "server get!";
				//echo_msg += buffer;
				sendto(sockfd,echo_msg.c_str(),echo_msg.size(),0,(struct sockaddr*)&peer,len);
			}
			else
			{
				std::cerr << "recvfrom error" << std::endl;
			}
		}
	}
	~UdpServer()
	{
		if(sockfd >= 0)
			close(sockfd);
	}	
};

udp_server.cc

#include"udp_server,hpp"
 
 //udp_server ip port  
int main(int argc, char *argv[])
{
	if*(argc != 2)
	{
		std::cerr << "Usage:" << argv[0] << "port" << std::endl;
		return 1;
	}
	std::string ip = "127.0.0.1";  //127.0.0.1是本地环回ip,标识本主机
	int port = argv[1];
	UdpServer *svr = new UdpServer(ip,port);
	svr->InitUdpServer();

	svr->Start();  //服务器启动
	return 0;
} 

udp_server.cc外网访问

#include"udp_server,hpp"
 
 //udp_server ip port  
 //INADDR_ANY->0
int main(int argc, char *argv[])
{
	if*(argc != 2)
	{
		std::cerr << "Usage:" << argv[0] << "port" << std::endl;
		return 1;
	}
	//std::string ip = "127.0.0.1";  //127.0.0.1是本地环回ip,标识本主机
	int port = argv[1];
	//UdpServer *svr = new UdpServer(ip,port);
	UdpServer *svr = new UdpServer(port);
	svr->InitUdpServer();

	svr->Start();  //服务器启动
	return 0;
} 

注意:
(1)netstat:查看当前网络状态
参数:-nlup

参数作用
n能显示成数字就显示成数字
llist
tptcp
upudp
(2)端口号通常是16位,IPV4通常是32比特位的数据
(3)服务器需要绑定,客户端不需要绑定
Ⅰ.服务器是为了给别人提供服务,别人需要知道ip和端口,端口要总所周知,绑定后不能轻易改变
Ⅱ.客户端不绑定,因为客户端访问server,端口号只要是唯一的即可,需要和特定的client进程强相关,client端口可以动态设置
Ⅲ.client需要port,sendto类似的接口,client直接在OS层面会自动给client获取一个唯一的端口
Ⅳ.云服务器的ip,是由对应的云厂商提供的,这个ip不能直接被绑定(直接绑定需要虚拟机或自定义安装的Linux),如果需要bind,需要让外网访问,需要bind 0(INADDR_ANY为0,即服务器可以接受来自任何client的请求)

在这里插入图片描述

Ⅳ.云服务器的ip,是由对应的云厂商提供的,这个ip不能直接被绑定,如果需要bind,需要让外网访问,需要bind 0,INADDR_ANY为0

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据库专辑--SQL分类汇总(group by...with rollup),增加“总计”字段

系列文章 C#底层库–数据库访问帮助类&#xff08;MySQL版&#xff09; 数据库专辑–WITH CHECK OPTION的用法 文章目录系列文章前言一、概念介绍二、测试用例2.1 创建表2.2 初始化数据2.3 数据查询2.4 分析问题2.5 解决问题2.6 推荐另一种写法&#xff0c;使用COALESCE三、用…

如何撰写品牌故事?品牌故事软文撰写技巧分享

你听过哪些有温度的品牌故事&#xff1f;我首先想到的是香奈儿&#xff1a; 我的生活不曾取悦于我&#xff0c;所以我创造了自己的生活。 这是香奈儿的创始人可可香奈儿给世人留下的一句话&#xff0c;也是她一生的真实写照。 她被后人看作女性解放和独立的一个象征&#xf…

查询网站有没有被搜狗收录复杂吗?查询搜狗收录简单的方法

对于网站收录的概念&#xff0c;互联网中或者搜索引擎中已经有大量的相关定义。网站收录&#xff0c;指的是爬虫爬取了网页&#xff0c;并将页面内容数据放入搜索引擎数据库中这一结果。 查询网站有没有被搜狗收录复杂吗&#xff1f; 用网站批量查询工具呀&#xff01;操作超简…

React高级备忘录(生命周期)class component

须知 什么是生命周期?就像人有生老病死,component也有类似这样的概念,了解生命周期可以让我们知道如何在「对」的时间做「对」的事。 — Lieutenant 过! 常用生命周期 可以分为三大部分 创建component (componentDidMount)更新component(componentDidUpdate)销毁compone…

照一次CT,对人体的伤害有多大?终于有医生肯站出来说实话

CT是一种检查身体的方式&#xff0c;对于这项检查项目&#xff0c;一直有都有不好的传言&#xff0c;有的人听说CT有辐射&#xff0c;而且辐射比较大&#xff0c;所以比较排斥。 也有的人听说频繁做CT会致癌&#xff0c;所以不愿意做&#xff0c;还有的人把CT当作筛查癌症的神器…

Spring从入门到精通(二)

文章目录1.动态代理1.1 概念1.2 jdk动态代理&#xff08;重点&#xff09;1.3 基于子类的动态代理&#xff08;了解&#xff09;2.AOP2.1 概念2.2 springAop — 基于AspectJ技术2.2.1 AspectJ使用&#xff08;XML&#xff09;2.2.2 AspectJ使用&#xff08;注解开发&#xff09…

【数据结构】二叉树的实现OJ练习

文章目录前言(一) 二叉树的接口实现构建二叉树前序遍历中序遍历后序遍历层序遍历二叉树的节点个数二叉树叶子节点个数二叉树第K层节点个数二叉树的高度查找指定节点判断完全二叉树销毁二叉树(二) 二叉树基础OJ练习单值二叉树相同的树另一棵树的子树二叉树的前序遍历二叉树的最大…

[oeasy]python0026_刷新时间_延迟时间_time_sleep_死循环_while_True

刷新时间 回忆上次内容 time 是一个 ​​module​ import 他可以做和时间相关的事情time.time() 得到当前时间戳 time.localtime() 得到本地时间元组local为本地 time.asctime() 得到时间日期字符串asc为ascii 简略的写法为 asc_time time.asctime() 在​​time.asctime()​…

python -- PyQt5(designer)中文详细教程(六)控件1

控件1 控件就像是应⽤这座房⼦的⼀块块砖。PyQt5有很多的控件&#xff0c;⽐如按钮&#xff0c;单选框&#xff0c;滑动条&#xff0c;复选框等 等。在本章&#xff0c;我们将介绍⼀些很有⽤的控 件&#xff1a; QCheckBox &#xff0c; ToggleButton &#xff0c; QSlider &a…

关于JavaScript运算符的学习

关于博主每篇博文的浪漫主义 【“仅此105秒&#xff0c;无法超越的绝美画面!&#xff01;”】 https://www.bilibili.com/video/BV1nW4y1x78x/?share_sourcecopy_web&vd_source385ba0043075be7c24c4aeb4aaa73352 “仅此105秒&#xff0c;无法超越的绝美画面!&#xff01;…

应用案例:有源无源电路协同仿真

01 有源无源电路协同仿真 随着电路系统集成度和信号速率的提高&#xff0c;电路中的电磁场效应越来越明显&#xff0c;单纯使用电路分析方法已不能满足仿真评估精度要求&#xff0c;这种情况下必须对问题进行分解&#xff0c;采用三维电磁场全波方法对信号传播路径上的封装与…

[附源码]计算机毕业设计JAVA在线文献查阅系统

[附源码]计算机毕业设计JAVA在线文献查阅系统 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybati…

C# 拖放操作

一 拖放操作 拖放操作Drag and Drop是两个窗口之间传递数据的一种手段。 1 拖放操作两部分&#xff1a;拖Drag、放Drop 几个术语&#xff1a; ① 源窗口&#xff1a;发起拖拽StartDrag; ② 目标窗口&#xff1a;接受拖放AcceptDraop; ③ 拖拽物&#xff1a;即传输的数据Dat…

ChatGPT有多厉害,影响到谷歌地位?

AI神器ChatGPT 火了。 能直接生成代码、会自动修复bug、在线问诊、模仿莎士比亚风格写作……各种话题都能hold住&#xff0c;它就是OpenAI刚刚推出的——ChatGPT。 有脑洞大开的网友甚至用它来设计游戏&#xff1a;先用ChatGPT生成游戏设定&#xff0c;再用Midjourney出图&…

外贸小白,一直不出单怎么办?

米贸搜今天&#xff0c;试着给新人一些方法和技巧&#xff0c;让你尽快在公司立足&#xff01; 事实上&#xff0c;规定几个月内下单的公司&#xff0c;往往都是平台有投资&#xff0c;去展会了&#xff0c;有大量营销费用的公司。当然&#xff0c;老板急着收回成本。对于有足…

网络基本概念

文章目录前言网络分层原因网络分层模型各层大致用途主机网络层网际层传输层应用层总结前言 在日常开发中&#xff0c;大家总是会或多或少的遇到一些网络通信的相关代码&#xff0c;如http请求调用。但是我们却不知道&#xff0c;数据是怎么从一台计算机到另一台计算机的&#…

opcj-如何通过一个项目征服Java

Java早已经不是高大山的稀世珍品了&#xff0c;程序员也不再是高科技工作者&#xff0c;而被称为码农 &#xff0c;为什么呢&#xff1f;因为Java后台的很多基础技术都已经固定了&#xff0c;也就是说主要你从头到尾学一遍就能会 &#xff0c;淘宝双十一搞不定&#xff0c;但是…

2022-12-07 小米pro路由(R3G) 刷固件 openwrt

环境准备&#xff1a;路由开启SSH 1.先登录小米开发者平台&#xff0c;解开小米路由的SSH http://www.miwifi.com/miwifi_open.html 2.路由连接电脑&#xff0c;通过SSH可直接登录到小米路由 本教程以R3G 为例 第一步: 刷入 BREED 如何刷入breed 不同设备方法不同,可以直接U…

通俗地讲讲数据降维的原理

什么是数据降维&#xff1f;关于这个问题&#xff0c;很多专家的说法都非常学术&#xff0c;估计很多普通人听不懂。所以&#xff0c;这里用通俗的语言解释一下&#xff0c;希望有助于更多的入门新人理解。 举一个简单的例子&#xff1a;假设一个小学的班级有5个学生&#xff…

Spring Boot 入门到精通(一)

文章目录一、Spring Boot 介绍1. 概念2.maven构建Spring Boot项目3. 自定义banner4. Spring Boot的全局配置文件5. Spring Boot项目的发布方式二、Spring Boot注入方式1. Value方式2. ConfigurationProperties方式3. SpringBoot默认注入方式4. SpringBoot中的默认配置三、Sprin…