旅游 便宜 网站建设,中国建设银行网站特点,怎么样开网站,温州苍南网站建设Linux网络——UDP的运用 文章目录 Linux网络——UDP的运用一、引入二、服务端实现2.1 创建socket套接字2.2 指定网络接口并bind2.3 接收数据并处理2.4 整体代码2.5 IP的绑定的细节 三、用户端实现3.1 创建套接字3.2 指定网络接口3.3 发生数据并接收3.4 绑定问题 四、代码五、UD…Linux网络——UDP的运用 文章目录 Linux网络——UDP的运用一、引入二、服务端实现2.1 创建socket套接字2.2 指定网络接口并bind2.3 接收数据并处理2.4 整体代码2.5 IP的绑定的细节 三、用户端实现3.1 创建套接字3.2 指定网络接口3.3 发生数据并接收3.4 绑定问题 四、代码五、UDP实现网络聊天室简易版 一、引入
在上一篇Linux网络——网络套接字中我们简述了TCP/UDP协议
其实网络通信的本质就是进程间通信而进程间通信无非就是读和写IO
这篇文章我们借助UDP实现服务端server和客户端client进行通信 将所学理论运用到实践中
二、服务端实现
由于服务端是面向广大用户的如果我们将IP地址写死那么服务器将只能从我们写的哪个IP地址中接收信息 所以启动服务器是不需要IP地址的它默认接收所以IP地址发来的信息
//static const std::string default_ip 0.0.0.0;
static const uint16_t default_port 8888;2.1 创建socket套接字
调用系统接口socket函数帮助我们创建套接字本质是把文件和网卡关联起来
参数介绍 domain一个域标识了这个套接字的通信类型网络或者本地 只用关注上面三个类第一个与第二个AF_UNIX/AF_LOCAL表示本地通信而AF_INET表示网络通信 type套接字提供服务的类型 我们用UDP实现所以使用SOCK_DGRAM protocol想使用的协议默认为0即可因为前面的两个参数决定了就已经决定了是TCP还是UDP协议了 返回值 成功则返回打开的文件描述符指向网卡文件其实就是文件描述符失败返回-1 创建套接字的本质其实就是创建了一个文件描述符并返回该文件描述符的值 只是该文件描述符是用于对应服务的网路数据传输 _sockfd socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd 0){lg.LogMessgae(Fatal, socket error, sockfd : %d\n, _sockfd);exit(1);}lg.LogMessgae(Info, socket success, sockfd : %d\n, _sockfd);2.2 指定网络接口并bind 参数介绍 socket创建套接字的返回值 address通用结构体上一章Linux网络——网络套接字有详细介绍 address_len传入结构体的长度 我们要先定义一个sockaddr_in结构体将结构体内对应的字段填充好再将结构体作为参数传递
struct sockaddr_in {short int sin_family; // 地址族一般为AF_INET或PF_INETunsigned short int sin_port; // 端口号网络字节序struct in_addr sin_addr; // IP地址unsigned char sin_zero[8]; // 用于填充使sizeof(sockaddr_in)等于16
};创建结构体后要先清空数据初始化我们可以用memset也可以用系统接口
#include strings.hvoid bzero(void *s, size_t n);填充端口号的时候要注意端口号是两个字节的数据涉及到大小端问题 在计算机中的普遍规定在网络中传输的数据都是大端的 所以为了统一无论我们机器是大端还是小端在调用接口的时候都将IP与端口号从主机序列转化为网路序列
端口号的接口
#include arpa/inet.h
// 主机序列转网络序列
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
// 网络序列转主机序列
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);IP的接口 对于IP其实有两步首先将字符串转换为整型再解决大小端问题 系统给了直接能解决这两个问题的接口
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.hint inet_aton(const char *cp, struct in_addr *inp);in_addr_t inet_addr(const char *cp);// 点分十进制字符串in_addr_t inet_network(const char *cp);char *inet_ntoa(struct in_addr in);struct in_addr inet_makeaddr(int net, int host);in_addr_t inet_lnaof(struct in_addr in);in_addr_t inet_netof(struct in_addr in);这里的inet_addr就是把一个点分十进制的字符串转化成整数再进行大小端处理
我们在启动服务器的时候只需要传入端口号即可IP默认为INADDR_ANY 即默认接收所有的IP
代码 // 2. 指定网络接口// 初始化结构体struct sockaddr_in local;bzero(local, sizeof(local)); // memsetlocal.sin_family AF_INET;local.sin_port htons(_port);local.sin_addr.s_addr INADDR_ANY;int n bind(_sockfd, (struct sockaddr *)local, sizeof(local));if (n ! 0){lg.LogMessgae(Fatal, bind error\n);exit(2);}2.3 接收数据并处理
服务端要获取到用户端发送过来的数据 参数说明 sockfd从哪个套接字读 buf数据放入的缓冲区 len缓冲区长度 flags读取方式 0代表阻塞式读取 src_addr和addrlen输出型参数返回对应的消息内容是从哪一个客户端发出的。第一个是自己定义的结构体第二个是结构体长度 void Start(){char buffer[1024];for (;;){struct sockaddr_in peer;socklen_t len sizeof(peer);ssize_t n recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)peer, len);if (n 0){InetAddr addr(peer);buffer[n] 0;std::cout client[ addr.PrintDebug() ]# buffer std::endl;std::string task _BackMessage(buffer);sendto(_sockfd, task.c_str(), task.size(), 0, (struct sockaddr *)peer, len);}}}现在我们想要知道是谁发送过来的消息信息都被保存到了peer结构体中我们知道IP信息在peer.sin_addr.s_addr中 这是一个网络序列要转成主机序列其次为了方便观察要把它转换成点分十进制 而这两个操作系统给了一个接口能够解决
char *inet_ntoa(struct in_addr in);同样获取端口号的时候也要由网络序列转成主机序列
uint16_t ntohs(uint16_t netshort);这里我对struct sockaddr_in进行了封装 单独写了一个类InetAddr用来专门读取struct sockaddr_in内的ip和端口号
#pragma once
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.h
#include stringclass InetAddr
{public:InetAddr(struct sockaddr_in addr):_addr(addr){_ip inet_ntoa(addr.sin_addr);_port ntohs(addr.sin_port);}std::string Ip(){return _ip;}uint16_t Port(){return _port;}sockaddr_in Addr(){return _addr;}std::string PrintDebug(){std::string info _ip;info :;info std::to_string(_port); // 127.0.0.1:4444return info;}~InetAddr(){}private:std::string _ip;uint16_t _port;struct sockaddr_in _addr;
};2.4 整体代码
#pragma once#include iostream
#include string
#include vector
#include unistd.h
#include nocopy.hpp
#include Log.hpp
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.h
#include cerrno
#include strings.h
#include string.h
#include InetAddr.hpp
#include stdlib.h
#include functional//static const std::string default_ip 0.0.0.0;
static const uint16_t default_port 8888;
static const int default_fd -1;
static const int default_size 1024;using func_t functionstd::string(std::string);class UdpServer : public nocopy
{
public:UdpServer(func_t BackMessage,const uint16_t port default_port): _port(port), _sockfd(default_fd),_BackMessage(BackMessage){}void Init(){ // 1. 创建socket_sockfd socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd 0){lg.LogMessgae(Fatal, socket error, sockfd : %d\n, _sockfd);exit(1);}lg.LogMessgae(Info, socket success, sockfd : %d\n, _sockfd);// 2. 指定网络接口// 初始化结构体struct sockaddr_in local;bzero(local, sizeof(local)); // memsetlocal.sin_family AF_INET;local.sin_port htons(_port);local.sin_addr.s_addr INADDR_ANY;int n bind(_sockfd, (struct sockaddr *)local, sizeof(local));if (n ! 0){lg.LogMessgae(Fatal, bind error\n);exit(2);}}~UdpServer(){}private:std::string _ip;uint16_t _port;int _sockfd;func_t _BackMessage;
};2.5 IP的绑定的细节
如果我们将IP绑定为127.0.0.1这个IP地址叫做本地环回地址
它的作用就是用来做服务器代码测试的 意思就是如果我们绑定的IP是127.0.0.1的话在应用层发送的消息不会进入物理层也就不会发送出去
当我们运行起来后想要查看网络情况就可以用指令netstat 后边也可以附带参数 -a显示所有连线中的Socket -e显示网络其他相关信息 -i显示网络界面信息表单 -l显示监控中的服务器的Socket -n直接使用ip地址(数字)而不通过域名服务器 -p显示正在使用Socket的程序识别码和程序名称 -t显示TCP传输协议的连线状况 -u显示UDP传输协议的连线状况 三、用户端实现
要发送数据给服务器就得知道服务器的IP和端口号 这里的IP就必须指明用来表示我是连接哪台服务器上的哪个进程
uint16_t _serverport;
std::string _serverip;
int _sockfd;这里的IP和port指的是要发送给谁也就是需要服务器的IP和端口号
创建套接字就跟前面的一样 //1. 创建socketint _sockfd socket(AF_INET,SOCK_DGRAM,0);if(_sockfd 0){lg.LogMessgae(Fatal, socket error, sockfd : %d\n, _sockfd);exit(1);}lg.LogMessgae(Info, socket success, sockfd : %d\n, _sockfd);3.1 创建套接字 //1. 创建socketint _sockfd socket(AF_INET,SOCK_DGRAM,0);if(_sockfd 0){lg.LogMessgae(Fatal, socket error, sockfd : %d\n, _sockfd);exit(1);}lg.LogMessgae(Info, socket success, sockfd : %d\n, _sockfd);3.2 指定网络接口 //2. 指定网络接口struct sockaddr_in send;memset(send,0,sizeof(send));send.sin_family AF_INET;send.sin_port htons(stoi(argv[2]));send.sin_addr.s_addr inet_addr(argv[1]);3.3 发生数据并接收 这里的参数和上面的recvfrom差不多而这里的结构体内部我们要自己填充目的IP和目的端口号 也就是服务器的IP和端口号 //3. 发送消息while(true){std::string buffer;std::coutPlease Enter# ;getline(std::cin,buffer);//coutbufferendl;ssize_t n sendto(_sockfd,buffer.c_str(),buffer.size(),0,(struct sockaddr*)send,sizeof(send));if(n -1){couterrorendl;}if(n 0){char rec[1024];//没用但有必要struct sockaddr_in tmp;socklen_t len sizeof(tmp);ssize_t m recvfrom(_sockfd, rec, sizeof(rec) - 1, 0, (struct sockaddr *)tmp, len);if(m0){rec[m] 0;InetAddr addr(tmp);std::coutserver[addr.PrintDebug()]# recstd::endl;}else{break;}}else{break;}}3.4 绑定问题
首先bind的作用是允许应用程序指定一个端口号用于监听传入的数据报或数据流
对于服务端 需要绑定一个公开的端口号允许大家访问如果是随机的其他人不知道也就访问不了所以服务端需要绑定
对于客户端 客户端在给服务器发信息的同时服务器也可能给客户端发信息所以客户端为了监听服务器有没有给自己回信息也需要bind一个端口号用于监听但客户端不需要显式的bind因为客户端的端口号不会被所有人访问别人不需要知道所以OS自动帮我们bind一个随机的端口号另外也是为了防止端口号重复避免破坏唯一性
那么为什么前面服务端必须显示的绑定port呢 因为服务器的端口号是众所周知的不能改变如果变了就找不到服务器了 而客户端只需要有就可以只用标识唯一性即可 举个例子 我们手机上有很多的app而每个服务端是一家公司写的但是客户端却是多个公司写的 如果我们绑定了特定的端口万一两个公司都用了同一个端口号呢这样就直接冲突了 OS会自动填充主机IP和随机生成端口号进行绑定在发送数据的时候自动绑定 所以创建客户端我们只用创建套接字即可
四、代码
UdpServer
#pragma once#include iostream
#include string
#include vector
#include unistd.h
#include nocopy.hpp
#include Log.hpp
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.h
#include cerrno
#include strings.h
#include string.h
#include InetAddr.hpp
#include stdlib.h
#include functional//static const std::string default_ip 0.0.0.0;
static const uint16_t default_port 8888;
static const int default_fd -1;
static const int default_size 1024;using func_t functionstd::string(std::string);class UdpServer : public nocopy
{
public:UdpServer(func_t BackMessage,const uint16_t port default_port): _port(port), _sockfd(default_fd),_BackMessage(BackMessage){}void Init(){ // 1. 创建socket_sockfd socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd 0){lg.LogMessgae(Fatal, socket error, sockfd : %d\n, _sockfd);exit(1);}lg.LogMessgae(Info, socket success, sockfd : %d\n, _sockfd);// 2. 指定网络接口// 初始化结构体struct sockaddr_in local;bzero(local, sizeof(local)); // memsetlocal.sin_family AF_INET;local.sin_port htons(_port);local.sin_addr.s_addr INADDR_ANY;int n bind(_sockfd, (struct sockaddr *)local, sizeof(local));if (n ! 0){lg.LogMessgae(Fatal, bind error\n);exit(2);}}void Start(){char buffer[1024];for (;;){struct sockaddr_in peer;socklen_t len sizeof(peer);ssize_t n recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)peer, len);if (n 0){InetAddr addr(peer);buffer[n] 0;std::cout client[ addr.PrintDebug() ]# buffer std::endl;std::string task _BackMessage(buffer);sendto(_sockfd, task.c_str(), task.size(), 0, (struct sockaddr *)peer, len);}}}~UdpServer(){}private:std::string _ip;uint16_t _port;int _sockfd;func_t _BackMessage;
};#include UdpServer.hpp
#include memoryvoid Usage(const std::string s)
{std::cout Usagr: s local_port std::endl;
}std::string BackMessage(const string s)
{return Back Message# s;
}int main(int argc, char *argv[])
{if (argc ! 2){Usage(argv[0]);return 1;}std::unique_ptrUdpServer usver(new UdpServer(BackMessage,std::stoi(argv[1])));usver-Init();usver-Start();return 0;
}UdpClient
#include iostream
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.h
#include Log.hpp
#include memory.h
#include InetAddr.hpp
#include cstdio
#include string
void usage(std::string s)
{std::coutUsagr: s server_ip server_portstd::endl;
}int main(int argc,char *argv[])
{if(argc ! 3){usage(argv[0]);return 0;}//1. 创建socketint _sockfd socket(AF_INET,SOCK_DGRAM,0);if(_sockfd 0){lg.LogMessgae(Fatal, socket error, sockfd : %d\n, _sockfd);exit(1);}lg.LogMessgae(Info, socket success, sockfd : %d\n, _sockfd);//2. 指定网络接口struct sockaddr_in send;memset(send,0,sizeof(send));send.sin_family AF_INET;send.sin_port htons(stoi(argv[2]));send.sin_addr.s_addr inet_addr(argv[1]);//3. 发送消息while(true){std::string buffer;std::coutPlease Enter# ;getline(std::cin,buffer);//coutbufferendl;ssize_t n sendto(_sockfd,buffer.c_str(),buffer.size(),0,(struct sockaddr*)send,sizeof(send));if(n -1){couterrorendl;}if(n 0){char rec[1024];//没用但有必要struct sockaddr_in tmp;socklen_t len sizeof(tmp);ssize_t m recvfrom(_sockfd, rec, sizeof(rec) - 1, 0, (struct sockaddr *)tmp, len);if(m0){rec[m] 0;InetAddr addr(tmp);std::coutserver[addr.PrintDebug()]# recstd::endl;}else{break;}}else{break;}}return 0;
}运行图 五、UDP实现网络聊天室简易版
篇幅有限只放gitee链接UDP实现网络聊天室简易版