网络 UDP协议(C++ 代码通过udp协议实现客户端与服务端之间的通信)_c++ udp(1)

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

服务端操作流程:
  1. 创建套接字端口:在内核中创建socket结构体,关联进程与网卡之间的联系
  2. 为套接字绑定地址信息:网络通信中的数据都必须带有源端IP、源端端口、对端IP、对端端口、协议。在内核创建的socket结构体中描述IP地址端口以及协议,(必须主动绑定,告诉客户端自己的地址信息,如果不绑定客户端就不知道该发往哪个服务端了)为了告诉操作系统发往哪个IP地址,哪个端口的数据是交给我来处理的
  3. 接收数据:告诉操作系统发往哪个地址和端口的数据应该交给我处理,操作系统一旦接收到发往这个地址和端口的数据,就会将这条数据放到对应的socket的接收缓冲区中,然后服务端从对应的socket的接收缓冲区中取出数据。
  4. 发送数据:将数据写入内核中的socket发送缓冲区中,操作系统选择合适的时候将数据封装发送出去
  5. 关闭套接字:释放资源

在这里插入图片描述

客户端操作流程:第2,3步与服务端不同
  1. 创建套接字:在内核中创建socket结构体,关联进程与网卡之间的联系
  2. 为套接字绑定地址信息:描述在内核中创建的socket结构体的源端地址信息;发送的数据中源端地址信息就是绑定的地址信息(不推荐主动绑定地址,降低端口冲突的概率,从而确保数据发送的安全性)
  3. 发送数据:将数据放到socket的发送缓冲区中,操作系统选择合适时候封装数据并发送数据。若socket发送数据的时候还没绑定地址,则操作系统会选择合适的地址进行绑定。
  4. 接收数据:将数据写入内核中的socket发送缓冲区中,操作系统选择合适的时候将数据封装发送出去
  5. 关闭套接字:释放资源

socket接口介绍

1、创建套接字int socket(int domain, int type, int protocol) 参数内容(domian:地址域(本地通信-AF_LOCAL、IPv4-AF_INET、IPv6-AF_INET6等)确定本次socket通信使用哪种协议版本的地址结构,不同的协议版本有不同的地址结构;type:套接字类型(流式套接字-SOCK_STREAM、数据报套接字-SOCK_DGRAM等);protocol:协议类型(TCP-IPPROTO_TCP、UDP-IPPROTO_UDP) ,默认为0-流式默认TCP,数据报默认UDP)
返回值:文件描述符-非负整数, 套接字所有其他接口的操作句柄,失败返回-1

2、为套接字绑定地址信息int bind(int sockfd, struct sockaddr *addr, socklen_t len)参数内容(sockfd:创建套接字返回的操作句柄;addr:要绑定的地址信息;len:要绑定的地址信息长度)
中间的参数结构体有很多种

struct sockaddr
{
	sa_family_t sa_family;
	char sa_data[14];
};

struct sockaddr_in
{
	sa_family sin_family;//地址域
	in_port_t sin_port;//端口号
	struct in_addr sin_addr;//IP地址
};

struct sockaddr_in6
{
	sa_family sin_family;//地址域
	in_port_t sin6_port;//端口号
	uint32_t sin6_flowinfo;
	struct in6_addr, sin6_addr;//IP地址
	unit32_t sin6_scope_id;
}

struct sockaddr_un
{
	__SOCKADDR_COMMON (sun_);
	char sun_path[108];
};

在这里插入图片描述

不同的地址结构,有统一的一个信息:前两个字节是地址域类型,bind可以绑定不同的地址结构,为了实现接口统一,因此用户定义地址结构的时候,需要定义自己需要的地址结构,例如IPv4就使用struct sockaddr_in,但是进行绑定的时候,统一类型强转成为sockaddr*类型
简单解析bind接口的实现

bind(fd,addr, len)
{
	if(addr->sa_family==AF_INET)
	{
		//绑定IPv4地址信息,这个结构体按照sockaddr\_in进行解析
	}
	else if (addr->sa_family==AF_INET6)
	{
		//绑定IPv6地址信息,这个结构体按照sockaddr\_in6进行解析
	}
	else if ...
}

3、接收数据,接收发送者地址便于回复ssize_t recvfrom(int sockfd, char *buf, int len, int flag, struct sockaddr *peer_addr, socklen *addrlen) 参数内容(sockfd:创建套接字返回的操作句柄;buf:一块缓冲区,用于接收从接收缓冲区中取出数据;len:想要接收的数据长度;flag:操作选项标志,默认为0,表示阻塞操作;peer_addr:发送方的地址信息;addrlen:想要获取的地址信息长度以及返回实际长度)
返回值:成功返回实际接收到的数据字节长度,失败返回-1

4、发送数据ssize_t sendto(int sockfd, char *data, int len, int flag, struct sickaddr *peer_addr, socklen_t addrlen) 参数内容(socket:socket操作句柄;data:要发送的数据地址;len:要发送数据长度;flag:默认为0,表示阻塞操作;peer_addr:接收方的地址信息;addrlen:地址信息长度)
返回值:成功返回实际发送的数据字节长度,失败返回-1

5、关闭套接字int close(int fd)

网络字节序的转换接口
uint32_t htonl(uint32_t hostlong) 主机字节序到网络字节序的转换
uint16_t htons(uint16_t hostshort)
uint32_t ntohl(uint32_t netlong)网络字节序到主机字节序的转换
uint16_t ntohs(uint16_t netshort)

in_addr_t inet_addr(const char *cp)将字符串的点分十进制IP地址转换成为网络字节序的整数IP地址
char *inet_ntoa(struct in_addr in)将网络字节序的整数IP地址转化为字符串点分十进制IP地址
const char *inet_ntop(int af, const void *src,char *dst, socklen_t size)将网络字节序的整数IP地址转化为字符串IP地址-兼容IPv4和IPv6
int inet_pton(int af, const char *src, void *dst)将字符串的IP地址转换成为网络字节序的整数IP地址-兼容IPv4和IPv6

udp客户服务端代码实现

使用c++封装UdpSocket类,实例化的每一个对象都是一个udp通信套接字,并且能够通过成员接口实现udp通信流程
udpsocket.hpp

//udpsocket.hpp
#include <cstdio>
#include <string>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
using namespace std;
class UdpSocket
{
	public:
		UdpSocket()
			:\_sockfd(-1)
		{}
		//创建套接字
		bool Socket()
		{
			_sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
			if (_sockfd < 0)
			{
				perror("socket error");
				return false;
			}
			return true;
		}
		//为套接字绑定地址信息
		bool Bind(const string &ip, uint16_t port)
		{
			//定义IPv4地址结构体
			struct sockaddr_in addr;
			//地址信息赋值
			addr.sin_family = AF_INET;
			addr.sin_port = htons(port);//将主机字节序短整型型数据转化为网络字节序数据
			addr.sin_addr.s_addr = inet\_addr(ip.c\_str());//将字符串IP地址转化为网络字节序IP地址
			socklen_t len = sizeof(struct sockaddr_in);
			int ret = bind(_sockfd, (struct sockaddr\*)&addr, len);
			if (ret < 0)
			{
				perror("bind error");
				return false;
			}
			return true;
		}
		//接收数据,获取发送端地址信息
		bool Recv(string \*buf, string \*ip=NULL, uint16_t \*port=NULL)
		{
			struct sockaddr_in peer_addr;//用于接收发送端的地址信息
			socklen_t len = sizeof(struct sockaddr_in);
			char tmp[4096] = {0};
			int ret = recvfrom(_sockfd, tmp, 4096, 0, (struct sockaddr\*)&peer_addr, &len);
			if (ret < 0)
			{
				perror("recvfrom error");
				return false;
			}
			buf->assign(tmp, ret);//assign从指定字符串中截取指定长度的数据到buf中
			if (port != NULL)
			{
				\*port = ntohs(peer_addr.sin_port);//网络字节序到主机字节序的转换
			}
			if (ip != NULL)
			{
				\*ip = inet\_ntoa(peer_addr.sin_addr);//网络字节序到字符串IP地址的转换
			}
			return true;
		}
		//发送数据
		bool Send(const string &data, string &ip, const uint16_t port)
		{
			struct sockaddr_in addr;
			addr.sin_family = AF_INET;
			addr.sin_port = htons(port);
			addr.sin_addr.s_addr = inet\_addr(ip.c\_str());
			socklen_t len = sizeof(struct sockaddr_in);
			int ret = sendto(_sockfd, data.c\_str(), data.size(), 0, (struct sockaddr\*)&addr, len);
			if (ret < 0)
			{
				perror("sendto error");
				return false;
			}
			return true;
		}
		//关闭套接字
		bool Close()
		{
			if (_sockfd > 0)
			{
				close(_sockfd);
				_sockfd = -1;
			}
			return true;
		}
	private:
		int _sockfd;
};



udp_srv.cpp

#include <iostream>
#include <string>
#include "udpsocket.hpp"
using namespace std;

#define CHECK\_RET(q) if((q)==false){return false;}
int main(int argc, char \*argv[])
{
	//运行时有三个参数 udp\_src 192.168.73.29 4096
	if (argc != 3)
	{
		cout << "Usage: ./udp\_srv ip prot\n";
		return -1;


![img](https://img-blog.csdnimg.cn/img_convert/f51548d34a394c6a8fc5e41b244e6d69.png)
![img](https://img-blog.csdnimg.cn/img_convert/b091d741840469825b43b2b303e1c8aa.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618668825)**

86931559)]
[外链图片转存中...(img-DSPRQO1f-1715886931559)]

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618668825)**

  • 13
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的基于UDP协议客户端和服务器端代码示例,用于实现机器人一问一答: 客户端代码: ```c++ #include <iostream> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <string.h> using namespace std; int main() { int sockfd; struct sockaddr_in server_addr; // 创建socket if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("socket failed"); exit(EXIT_FAILURE); } // 设置服务器地址 memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(8080); server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 发送数据 char buffer[1024]; cout << "请输入问题:" << endl; cin.getline(buffer, 1024); sendto(sockfd, buffer, strlen(buffer), 0, (struct sockaddr *) &server_addr, sizeof(server_addr)); // 接收答案 memset(buffer, 0, sizeof(buffer)); socklen_t len = sizeof(server_addr); int n = recvfrom(sockfd, buffer, 1024, 0, (struct sockaddr *) &server_addr, &len); buffer[n] = '\0'; cout << "答案:" << buffer << endl; close(sockfd); return 0; } ``` 服务器端代码: ```c++ #include <iostream> #include <sys/socket.h> #include <netinet/in.h> #include <unistd.h> #include <string.h> #include <fstream> using namespace std; int main() { int sockfd; struct sockaddr_in server_addr, client_addr; // 创建socket if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("socket failed"); exit(EXIT_FAILURE); } // 绑定端口号和IP地址 memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(8080); server_addr.sin_addr.s_addr = INADDR_ANY; if (bind(sockfd, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) { perror("bind failed"); exit(EXIT_FAILURE); } // 接收数据 char buffer[1024]; socklen_t len = sizeof(client_addr); int n = recvfrom(sockfd, buffer, 1024, 0, (struct sockaddr *) &client_addr, &len); buffer[n] = '\0'; cout << "问题:" << buffer << endl; // 查找答案 ifstream file("qa.txt"); string question, answer; while (getline(file, question)) { getline(file, answer); if (question == buffer) { strcpy(buffer, answer.c_str()); break; } } // 发送答案 sendto(sockfd, buffer, strlen(buffer), 0, (struct sockaddr *) &client_addr, sizeof(client_addr)); close(sockfd); return 0; } ``` 以上代码的机器人回答是根据本地的`qa.txt`文档中的内容进行查找和返回的。每行包含一个问题和一个答案,用冒号分隔。您可以根据自己的需求和业务逻辑进行修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值