在本地端模拟实现一个udp通信的代码
1.服务器端--初始化
我们运行程序的时候,希望的运行方式为 ./serve 127.0.0.1 8080
所以就需要我们在main函数接收命令行传递过来的参数
创建步骤以及注意事项
1.1 创建套接字
函数原型:
int socket(int domain, int type, int protocol);
参数
domian:域 ,说明了套接字是用来网络通讯还是本地通讯
红圈圈出来的三个为最常用的。
type 类型---选择在网络中的通讯是流式通讯还是数据报式的通讯.
这里使用的是udp通讯,所以这里选择使用数据报式
数据报套接字提供一种无连接、不可靠的服务。该服务并不能保证数据传输的可靠性,数据有可能在传输过程中丢失或出现数据重复,且无法保证顺序地接收到数据。SOCK——DGRAM
protocol :protocol: 一般忽略即可 即为0
返回值 返回值类型为Int,返回值为-1时代表创建套接字失败,同时设置error,成功后返回sock
1.2绑定端口与ip
函数原型:
int bind(int socket, const struct sockaddr *address,socklen_t address_len);
参数:
socket:套接字 ,传递我们上方创建的套接字,将端口与ip与其绑定
const struct sockaddr *address :
结构体内有如下成员
sin_port 表示了端口号
sin_addr表示了ip地址 由定义可以发现ip地址为32位的
我们要将此作为参数传递,那么就需要为该结构体赋值。不过在使用之前,最好将其清零
可以使用memset、bzero等函数
在网络传输中一切以大端进行传输,所以在初始化该结构体时,需要转为大端,系统为我们提供了一个接口:htons(端口)
主机转网络字节序因为我们的_port是我们自己定义的,有大小端之分,所以就需要转为大端 htons-----主机序列转网络序列,又因为port为短整型,为2字节,所以使用htons即可。
sin_addr : 平常使用的ip为诸如192.168.1.1 这些为点分十进制风格的。
ip地址是一个32位的整数,正好可以使用4字节(32位)表示。原理:
因为ip地址在我们看来是192.188.1.1 是点分十进制风格的,
IP地址是一个32位的二进制数,通常被分割为4个“8位二进制数”。
每段8位二进制数之间用点(.)隔开,以点分十进制的形式书写,
所以ip地址占4个字节。
那ip地址是给人看的,我们在网络中传输的话,就需要相应的转换
1.把它转为二进制
2.再使用主机转网络,修正其大小端问题。
系统为我们提供了一个函数,可以同时完成上述两个任务。
inet_addr();
注意:接收的参数类型位struct sockaddr *
而我们传递的类型位struct sockaddr_in * 需要强制类型转换?
为什么不使用void*? -->因为这一套流程出来的时候,c语言还不支持void*
返回值:返回值小于0时表示绑定失败,同时设置error
socklen_t address_len: 结构体的大小
2.服务器端--接收数据recvfrom
接收数据使用的常用的接口为
ssize_t recvfrom(int socket, void *restrict buffer, size_t length,
int flags, struct sockaddr *restrict address,
socklen_t *restrict address_len);socket:套接字,使用上面创建出来的套接字即可
buffer:接收到的数据存放的buffer
length 该buffer的大小
flags 默认设置为0
重点介绍 struct sockaddr *restrict address
在这里,它是输出型参数,用于获取向我发送信息的ip地址与端口号。
address_len 是输入输出形参数
输入输出型参数 做输入型参数时,传递的是buffer的大小
做输出型参数时,返回实际接收的字节大小
socklen_t len = sizeof(buffer);
返回值 : 返回接收到的数据个数
3.发送数据sendto
ssize_t sendto(int socket, const void *message, size_t length,
int flags, const struct sockaddr *dest_addr,
socklen_t dest_len);
#ifndef __UDP_SERVE__H_
#define __UDP_SERVE__H_
#include <iostream>
#include "log.hpp"
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <cstring>
#include <netinet/in.h>
#include <arpa/inet.h>
class serve
{
public:
serve(uint16_t port=8080) : _port(port), _sock(-1)
{
}
void serveInit()
{
// 1.创建套接字
_sock = socket(AF_INET, SOCK_DGRAM, 0);
if (_sock == -1)
{
message(FATAL, "%d-%s", errno, strerror(errno));
}
// 2.绑定端口与ip
struct sockaddr_in local;
bzero(&local, sizeof(local)); // 将local内存设置为0
local.sin_family = AF_INET;
// 2.1 绑定端口 --需要主机转网络字节序
/*
主机转网络字节序
因为我们的_port是我们自己定义的,有大小端之分,所以就需要转为大端
htons-----主机序列转网络序列,又因为port为短整型,为2字节,所以使用htons即可
*/
local.sin_port = htons(_port);
// 2.2 绑定Ip地址
/*
因为ip地址在我们看来是192.188.1.1 是点分十进制风格的,
IP地址是一个32位的二进制数,通常被分割为4个“8位二进制数”。
每段8位二进制数之间用点(.)隔开,以点分十进制的形式书写,
所以ip地址占4个字节。
那ip地址是给人看的,我们在网络中传输的话,是不是要1.把它转为二进制
2.再使用主机转网络,修正其大小端问题。
系统为我们提供了一个函数,可以同时完成上述两个任务。
inet_addr
*/
local.sin_addr.s_addr =_ip.empty()? INADDR_ANY : inet_addr(_ip.c_str());
if (bind(_sock, (struct sockaddr *)&local, sizeof(local)) < 0)
{
message(FATAL, "[%d-%s]", errno, strerror(errno));
exit(2);
}
message(NORMAL, "[%s-%u-%s]", _ip.c_str(), _port, "success!");
}
void start()
{
// 1.读取数据
char buffer[1024];
while (1)
{
// 输出型参数,用于获取向我发送信息的ip地址与端口号
struct sockaddr_in peer;
bzero(&peer, sizeof(peer));
// 输入输出型参数 做输入型参数时,传递的是buffer的大小
// 做输出型参数时,返回实际接收的字节大小
socklen_t len = sizeof(buffer);
ssize_t s = recvfrom(_sock, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)(&peer), &len);
if (s > 0)
{
buffer[s]=0;
// 获取端口号
// 因为从网络中来,大小端字节序需要重新更正---
// 网络变主机
uint16_t cliport = ntohs(peer.sin_port);
std::string cliip = inet_ntoa(peer.sin_addr);
// 分析数据
// 写回数据
std::cout<<"ip: "<<cliip<<"port: "<<cliport<<": "<<buffer<<std::endl;
sendto(_sock,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,sizeof(peer));
}
}
}
private:
std::string _ip;
uint16_t _port; // port为16位的整数
int _sock;
};
#endif
需要重点注意的是
std::cout<<"ip: "<<cliip<<"port: "<<cliport<<": "<<buffer<<std::endl;
如果我们在向控制台输出时,不加后面的end,不会刷新缓冲区文章来源:https://www.toymoban.com/news/detail-842521.html
所以需要使用end 或者fflush(stdout)。文章来源地址https://www.toymoban.com/news/detail-842521.html
到了这里,关于linux网络--简单udp代码的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!