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

udp网络通信 socket

套接字是实现进程间通信的编程。IP可以标定主机在全网的唯一性,端口可以标定进程在主机的唯一性,那么socket通过IP+端口号就可以让两个在全网唯一标定的进程进行通信。

套接字有三种:

域间套接字:实现主机内部的进程通信的编程

原始套接字:使用网络层或者数据链路层的接口进行编程,更难更底层,例如制作抓包等网络工具

网络套接字:实现用户通信的编程

udp网络通信

服务端server

分析:

服务端最少需要有两个接口,一个用来初始化服务器,一个用来运行服务器

一:初始化服务器接口

创建socket,绑定端口

1.创建socket

socket接口

fedd87d5c4ed451a98cd741758e1fd65.png

第一个参数是域,用来确定通信类型27575ae93e4e46ccb56a96cff6bdffbb.png 

AF_UNIX就是域间套接字,AF_INET/AF_INET6是IPv4/IPv6的网络套接字

第二个参数是传输的数据种类 

1885a033313f4789a12b066d2b3a3cf2.png

最常用的是前两个,SOCK_STREAM是面向字节流(TCP),SOCK_DGRAM是面向数据报(UDP)

第三个参数传0即可,这里用不到 

0ddec27613af48f6ad4685f1ac6a541c.png

返回值返回一个文件描述符 

2.绑定端口

bind接口

1dac49aa4920483c96a1505b8fcbfff7.png

第一个参数是socket返回的文件描述符

第二个参数是一个包含通信属性的结构体,例如通信域,ip地址和端口号。这是一个输出型参数,而且不同的通信类型的结构体不一样,传参时要进行类型转换。

47ab09aafd6d47729a411520d3c0fba1.png

c84c77ee04cf4e0b821d436be4789ae2.png

sockaddr_in有三个成员需要我们初始化:
1.sin_family:通信域,网络通信使用AF_INET

这个成员使用了宏函数,宏传入参数sin_,所以sa_prefix##family等价于sin_family,它是一个无符号短整型

2.sin_port:端口号 

端口号会在网络间传输,需要对我们传入的端口号进行处理,使其符合网络字节序,要使用htons()接口

3.sin_addr:ip地址

这个成员是一个结构体,结构体内有一个无符号整形的成员。但是我们一般的ip地址是一个字符串例如(192.168.33.131),所以我们要先将字符串转化为数字,再将数字变为网络字节序。这要用到inet_addr(char*)接口

第三个参数时结构体大小,直接计算即可

注意:一般服务器bind的ip地址是0.0.0.0,如果一个服务是绑定到 0.0.0.0 ,那么外部机器访问该机器上所有 IP 都可以访问该服务。如果服务绑定到的是特定的 ip,则只有访问该 ip 才能访问到服务。

 初始化服务器代码样例:

class udpserver{
public:void init(){// 创建udp serversockfd_ = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd_ < 0){exit(1);}// bind socked//初始化结构体struct sockaddr_in local;local.sin_family = AF_INET;local.sin_port = htons(port_);                  // 转化成网络字节序local.sin_addr.s_addr = inet_addr(ip_.c_str()); // 将ip转化成数字,再将数字转化为网络字节序//bindif (bind(sockfd_, (struct sockaddr *)&local, sizeof(local)) < 0){exit(2);}}udpserver(uint16_t port = 8080, string ip = "0.0.0.0"): port_(port), ip_(ip){}~udpserver(){if (sockfd_ > 0)close(sockfd_);}
private:int sockfd_;uint16_t port_;string ip_;
};

二:运行服务器接口

接收数据,对数据做处理,最后发送数据

1.接收数据

recvfrom接口

c0a1d65b28404ddba7faec9ae3462b78.png

90acb0527a354fff915010617d47967d.png

第一个参数是socket返回的文件描述符,第二个是接收的数据存放的位置,第三个参数是buf空间的大小,第四个参数在本文中这里只要传0即可满足需求。

第五个参数是用来存储数据发送方(客户端)属性的结构体,第六个参数是结构体大小的指针

为什么要记录客户端的通信属性呢?因为服务端在对数据做处理后还要发送回客户端。 

 2.对数据做处理

需要根据实际需求进行处理,可以通过传函数指针,实现代码分层。见后面的代码示例。

3.发送数据

sendto接口

a2a60ee6474c46c883e5e79d428a34a5.png

5faa90bb08204028a1a991b756f61956.png

 第一个参数是socket的文件描述符,第二个参数是要发送的数据的地址,第三个参数是发送数据的大小,第四个参数传0。

第五个参数是数据接收方(客户端)属性的结构体,第六个参数是结构体大小。

运行服务器代码样例(包括前面的代码):

//udpserver.hpp
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <functional>
using func = function<string(char*)>;
class udpserver{
public:void init(){// 创建udp serversockfd_ = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd_ < 0){exit(1);}// bind socked//初始化结构体struct sockaddr_in local;local.sin_family = AF_INET;local.sin_port = htons(port_);                  // 转化成网络字节序local.sin_addr.s_addr = inet_addr(ip_.c_str()); // 将ip转化成数字,再将数字转化为网络字节序//bindif (bind(sockfd_, (struct sockaddr *)&local, sizeof(local)) < 0){exit(2);}}void run(func fun){char buf[1024] = {0};while (1){struct sockaddr_in client; // 客户端的信息socklen_t len = sizeof(client);// 收数据ssize_t n = recvfrom(sockfd_, buf, sizeof(buf), 0, (struct sockaddr *)&client, &len);if (n < 0){cout << "receice fail" << endl;continue;}buf[n] = 0;cout << "server get:" << buf << endl;// 处理数据,通过传递的函数实现数据处理的和网络通信解耦string ret = fun(buf);// 发送数据n = sendto(sockfd_, ret.c_str(), ret.size(), 0, (struct sockaddr *)&client, len);}}udpserver(uint16_t port = 8080, string ip = "0,0,0,0"): port_(port), ip_(ip){}~udpserver(){if (sockfd_ > 0)close(sockfd_);}
private:int sockfd_;uint16_t port_;string ip_;
};//main.cpp
#include"udpserver.hpp"
#include<memory>using namespace std;
string datagram(char* data)
{string ret = "Get message:";ret += data;return ret;
}
int main()
{unique_ptr<udpserver> server(new udpserver());server->init();server->run(datagram);return 0;
}

客户端client

分析:

客户端需要初始化客户端,发送数据,接收数据

一:初始化客户端

创建socket和服务端一样,不同的是客户端的bind是操作系统完成的不需要我们操作,客户端会在发送数据时由OS随机bind一个端口。因为客户端的设备可能同时请求多个服务端,例如手机会同时运行很多app,客户端的程序很难为服务端留一个固定的端口(不同公司之间开发软件不可能商量谁要用哪个客户端的端口),而且服务端没必要第一时间知道客户端的端口,客户端发送数据请求时可以获取到客户端的属性

二:发送数据

使用sendto()接口,和服务端一样。

三:接收数据

使用recvfrom()接口,和服务端一样。

客户端代码样例:

#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;
int main(int argc, char *argv[])
{//获取命令行参数,ip和端口号if(argc != 3){cout << "Please input in this way" << endl;cout << "./udpclient" << " [ip]" << " [port]" << endl;}string ip = argv[1];uint16_t port = stoi(argv[2]);// 创建socketint sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0){cout << "client create fail" << endl;}//客户端不需要bind,但是客户端会在发送数据时由OS随机bind一个端口,因为客户端设备同时请求多个服务端,服务端很难让客户端的程序为其留一个固定的端口,而且没必要第一时间知道客户端的端口,客户端发送数据请求时可以获取到// 创建服务端信息的结构体struct sockaddr_in server;server.sin_port = htons(port);server.sin_family = AF_INET;server.sin_addr.s_addr = inet_addr(ip.c_str());socklen_t len = sizeof(server);char buf[1024] = {0};string message;while (1){// 发送数据cout << "Please Enter@";getline(cin, message);sendto(sockfd, message.c_str(), message.size(), 0, (struct sockaddr *)&server, len);// 接收数据struct sockaddr_in tem;socklen_t len_tem = sizeof(tem);ssize_t n = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&tem, &len_tem);if (n < 0){cout << "client receive fail" << endl;continue;}buf[n] = 0;cout << buf << endl;}
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 简单说说关于shell中zsh和bash的选择
  • Linux之ansible的playbook剧本(yaml文件)
  • VMwareWorkstation安装UOS系统(1070版本)教程
  • 如何识别和防范跨站请求伪造(CSRF)?
  • YOLO改进模块后不出现Glops参数问题
  • 【408 数据结构】第2章 线性表
  • PHP-SER-libs靶场通关(1-9)
  • 数据结构基础详解(C语言): 栈的括号匹配(实战)与栈的表达式求值特殊矩阵的压缩存储
  • 国家新标准引领,油烟净化器为烟火气添清新活力
  • 网络安全(sql注入2,less3)
  • 苹果的“AI茅”之路只走了一半
  • TeamTalk数据库代理服务器
  • AI问答-数据库:理解头表和行表
  • ModuleNotFoundError: No module named ‘keras.layers.core‘怎么解决
  • 【CSS】mask-image属性的详细介绍
  • JS中 map, filter, some, every, forEach, for in, for of 用法总结
  • 【EOS】Cleos基础
  • 【跃迁之路】【444天】程序员高效学习方法论探索系列(实验阶段201-2018.04.25)...
  • 【跃迁之路】【463天】刻意练习系列222(2018.05.14)
  • 03Go 类型总结
  • Angular 4.x 动态创建组件
  • gops —— Go 程序诊断分析工具
  • PHP 使用 Swoole - TaskWorker 实现异步操作 Mysql
  • unity如何实现一个固定宽度的orthagraphic相机
  • 百度小程序遇到的问题
  • 闭包--闭包作用之保存(一)
  • 力扣(LeetCode)965
  • 算法系列——算法入门之递归分而治之思想的实现
  • 学习使用ExpressJS 4.0中的新Router
  • 栈实现走出迷宫(C++)
  • 追踪解析 FutureTask 源码
  • 回归生活:清理微信公众号
  • ​MySQL主从复制一致性检测
  • #define MODIFY_REG(REG, CLEARMASK, SETMASK)
  • #单片机(TB6600驱动42步进电机)
  • #我与虚拟机的故事#连载20:周志明虚拟机第 3 版:到底值不值得买?
  • (1)(1.8) MSP(MultiWii 串行协议)(4.1 版)
  • (152)时序收敛--->(02)时序收敛二
  • (3)Dubbo启动时qos-server can not bind localhost22222错误解决
  • (C语言)求出1,2,5三个数不同个数组合为100的组合个数
  • (编程语言界的丐帮 C#).NET MD5 HASH 哈希 加密 与JAVA 互通
  • (面试必看!)锁策略
  • (五)IO流之ByteArrayInput/OutputStream
  • (一)Spring Cloud 直击微服务作用、架构应用、hystrix降级
  • (一)为什么要选择C++
  • .NET Core 和 .NET Framework 中的 MEF2
  • .NET Core 项目指定SDK版本
  • .Net Redis的秒杀Dome和异步执行
  • 。。。。。
  • [20150904]exp slow.txt
  • [④ADRV902x]: Digital Filter Configuration(发射端)
  • [ABP实战开源项目]---ABP实时服务-通知系统.发布模式
  • [Assignment] C++1
  • [Bada开发]初步入口函数介绍
  • [C语言]——分支和循环(4)