UdpSocket
相关接口
#include <sys/socket.h> int socket(int domain, int type, int protocol);这个是套接字函数,套接字实际上就是IP+端口,IP用于标识主机,端口用于标识主机上面的进程
返回值:成功返回一个文件描述符,失败返回-1
domain:一般为AF_INET,表示网络通信
type:取值一般为SOCK_STREAM和SOCK_DGRAM,分别表示TCP和UDP,字面意思,引文tcp面向的是字节流,UDP是数据报,字节流就像水流一样,数据想取多少就取多少,数据报类似于快递,发来的是一整块数据
protocol:一般为0,然系统自动匹配默认协议
struct sockaddr_in { sa_family_t sin_family; in_port_t sin_port; struct in_addr sin_addr; };这个表示IPV4,其中用
sin_family:协议家族,一般设置成为AF_INET,表示要进行网络通信
sin_port:端口号
sin_addr:ip号
这个通常和bind函数一起使用,用来绑定自己的设置自己的IP号和端口号,但是使用的时候注意,当在设置的时候需要将端口号转换成为网络字节序,网络字节序是大端,我们自己的IP地址为string类型的,使用的时候也需要将其转化整数,主要用到下面的函数
#include <arpa/inet.h> uint32_t htonl(uint32_t hostlong); //主机转网络,且转为long long uint16_t htons(uint16_t hostshort); //主机转网络,且转为short uint32_t ntohl(uint32_t netlong); //网络转主机,且转为long long uint16_t ntohs(uint16_t netshort); //网络转主机,且为short 上面的函数非常好记忆: 比如:htonl 是 host to net long long 的缩写然后就是IP地址的转化
#include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> int inet_aton(const char *cp, struct in_addr *inp); 成功返回合法Ip,失败返回0 比如:将字符传的IP转化成为网络序号Ip存到addr中 iner_aton("127.0.0.1",&addr.sin_addr); in_addr_t inet_addr(const char *cp);//传入字符串的IP,返回网络序列的IP in_addr_t inet_network(const char *cp); 将字符串的IP转化成为主机序列的IP,所以后又需要化成为网络序列下面介绍bind
#include <sys/socket.h> int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);这个就是将我们设置好的IP和端口号绑定到操作系统,成功返回0,失败返回-1
ssokfd:通过调用socket函数返回的文件描述符
addr:就是我们的sockaddr_in,使用的时候需要强转为const struct addr
addlen:addr的大小
接受消息的函数
#include <sys/socket.h> ssize_t recvfrom(int sockfd, void buf[restrict .len], size_t len, int flags, struct sockaddr *_Nullable restrict src_addr, socklen_t *_Nullable restrict addrlen);成功:返回实际收到的信息的长度,失败:返回-1
sockfd:自己通过sockfd函数创建的
buf:输出型的参数,收到的消息放到这里面
len:buf的长度
flags:为0,表示阻塞
src_addr:输出型的参数,表示从那个主机收到的信息
srclen:src_addr的长度
发送消息的函数
#include <sys/socket.h> ssize_t sendto(int sockfd, const void buf[.len], size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);sockfd:自己通过sockfd函数创建的
buf:输出型的参数,发送的消息放到这里面
len:buf的长度
flags:为0,表示阻塞
src_addr:输出型的参数,表示想发给哪个主机
srclen:src_addr的长度
代码
服务端
hpp文件
#pragma once #include <iostream> #include <cstring> #include <cstdlib> #include <errno.h> #include <string.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <arpa/inet.h> #include <functional> #include "SockAddr.hpp" uint16_t DefaultPort = 8080; std::string DefaultIp = "127.0.0.1"; class Udp_Server { public: Udp_Server(const uint16_t Port = DefaultPort, const std::string Ip = DefaultIp) :_Port(Port) ,_Ip(Ip) ,_IsRuning(false) { } void Init() { _SockFd = socket(AF_INET,SOCK_DGRAM,0); //初始化sockfd if(_SockFd < 0) { std::cout << "sockrt error:" << strerror(errno) << std::endl; exit(1); } std::cout << "socket create success" << std::endl; //绑定 sockaddr_in Local; bzero(&Local,sizeof(Local)); //先将里面的字段全部清零,保险 Local.sin_family = AF_INET; //类型必须和上面socket一样才能绑定成功 Local.sin_port = htons(_Port); //转成网络字节流 Local.sin_addr.s_addr = inet_addr(_Ip.c_str()); Local.sin_addr.s_addr = INADDR_ANY; int n = bind(_SockFd,(const struct sockaddr*)&Local,sizeof(Local));//绑定到操作系统 if(n < 0) { std::cout << "bind error:" << strerror(errno) << std::endl; exit(2); } std::cout << "bind sucess" << std::endl; } void Start() { _IsRuning = true; while(true) { char Buffer[1024]; sockaddr_in src; socklen_t len = sizeof(src); int n = recvfrom(_SockFd,Buffer,sizeof(Buffer)-1,0,(sockaddr*)&src,&len); //接受消息 if(n > 0) { Buffer[n] = 0; std::cout << "From Client: " << Buffer << std::endl; //接受成功发送消息 std::string SendMessage = "return#: "; SendMessage += Buffer; socklen_t Size = sendto(_SockFd,SendMessage.c_str(),SendMessage.size(),0,(sockaddr*)&src,sizeof(src)); } } } private: int _SockFd; //套接字文件描述符号 uint16_t _Port; //端口号 std::string _Ip; //ip地址 bool _IsRuning; //是否运行 };测试文件
#include <memory> #include "Udp_Server.hpp" int main() { std::unique_ptr<Udp_Server> Server = std::make_unique<Udp_Server>(); Server->Init(); Server->Start(); return 0; }客户端
#include <iostream> #include <cstring> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <arpa/inet.h> #include "Udp_Client.hpp" #include "SockAddr.hpp" int main(int argc,char *argv[]) { if(argc!=3) { std::cout << "Usage:./.exe Ip port" << std::endl; exit(1); } std::string Ip = argv[1]; int Port = std::stoi(argv[2]); int SocketFd = socket(AF_INET,SOCK_DGRAM,0); if(SocketFd < 0) { std::cout << "ScoketFd error" << std::endl; exit(2); } //SockAddr SAddr(Ip,Port); sockaddr_in SAddr; SAddr.sin_family = AF_INET; SAddr.sin_addr.s_addr = inet_addr(Ip.c_str()); SAddr.sin_port = htons(Port); while(true) { std::cout << "Please send#"; std::string SendMessage; std::getline(std::cin,SendMessage); ssize_t n = sendto(SocketFd,SendMessage.c_str(),SendMessage.size(),0,(const struct sockaddr*)&SAddr,sizeof(SAddr)); char Buffer[1024]; sockaddr_in src; socklen_t len = sizeof(src); ssize_t Size = recvfrom(SocketFd,Buffer,sizeof(Buffer)-1,0,(sockaddr*)&src,&len); if(Size > 0) { Buffer[Size] = 0; std::cout << Buffer << std::endl; } } return 0; }运行结果
扩展功能
英语词典
客户端发送一个英语单词,服务端将中文意思去
实现这个功能用回调函数
先实现一个词典类
#pragma once #include <iostream> #include <unordered_map> #include <fstream> std::string strDefautPath = "./"; std::string strDefautFile = "Dict.txt"; class Dictionary { public: Dictionary(const std::string &strDicPath = strDefautPath, const std::string &strDicFile = strDefautFile) :_strDicPath(strDicPath) ,_strDicFile(strDicFile) { CreateDic(); } std::string Transtor(std::string strEnglish) { auto it = _umapDict.find(strEnglish); if(it == _umapDict.end()) { return "None"; } return it->second; } private: bool GetWord(std::string strDicContent, std::string inv, std::string *strEngish, std::string *strChinese) { if(strDicContent.empty()) { return false; } size_t pos = strDicContent.find(inv); if(pos == std::string::npos) { return false; } *strEngish = strDicContent.substr(0,pos); *strChinese = strDicContent.substr(pos+1); return true; } void CreateDic() { //打开文件 std::ifstream isFile(_strDicPath+_strDicFile); if(!isFile.is_open()) { std::cout << "文件打开失败" << std::endl; return; } std::string strDicContent; while(std::getline(isFile,strDicContent)) { std::string strEnglish; std::string strChinese; if(GetWord(strDicContent, ":", &strEnglish, &strChinese)) { _umapDict.insert(std::make_pair(strEnglish,strChinese)); } } isFile.close(); } private: std::unordered_map<std::string,std::string> _umapDict; //存储字典 std::string _strDicPath; //路径名称 std::string _strDicFile; //文件名称 };这个类的作用是从文件当中读取单词,将英语和对应的中文意思存储到哈希表中,然后提供翻译的方法,现在在Udp_Server.hpp中添加方法
其中,func_t 的定义如下
然后调用方法
在主函数里面将方法注册进Udp_Server中
完整代码
Udp_Server.hpp
#pragma once #include <iostream> #include <cstring> #include <cstdlib> #include <errno.h> #include <string.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <arpa/inet.h> #include <functional> #include "SockAddr.hpp" #include "Dictionary.hpp" int DefaultPort = 8080; std::string DefaultIp = "127.0.0.1"; using func_t = std::function<std::string(std::string)>; class Udp_Server { public: Udp_Server(func_t Func, const std::string &Ip = DefaultIp, const uint16_t &Port = DefaultPort) :_Addr(Ip,Port) ,_Func(Func) { } void Init() { _SockFd = socket(AF_INET,SOCK_DGRAM,0); if(_SockFd < 0) { std::cout << "sockrt error:" << strerror(errno) << std::endl; exit(1); } std::cout << "socket create success" << std::endl; int n = bind(_SockFd,_Addr.GetSockAddr(),_Addr.GetAddrLen()); if(n < 0) { std::cout << "bind error:" << strerror(errno) << std::endl; exit(2); } std::cout << "bind sucess" << std::endl; } void Start() { _IsRuning = true; while(true) { char Buffer[1024]; sockaddr_in src; socklen_t len = sizeof(src); int n = recvfrom(_SockFd,Buffer,sizeof(Buffer)-1,0,(sockaddr*)&src,&len); if(n > 0) { Buffer[n] = 0; std::cout << Buffer << std::endl; std::string SendMessage = "Chinese meaning: "; std::string Chinese = _Func(Buffer); SendMessage +=Chinese; socklen_t Size = sendto(_SockFd,SendMessage.c_str(),SendMessage.size(),0,(sockaddr*)&src,sizeof(src)); } } } private: int _SockFd; //套接字文件描述符号 SockAddr _Addr; bool _IsRuning; //是否运行 func_t _Func; };Udp_Server.cc
#include <memory> #include "Udp_Server.hpp" #include "Dictionary.hpp" int main() { std::shared_ptr<Dictionary> dict = std::make_shared<Dictionary>(); // 创建 Udp_Server,绑定 Dictionary 的 Transtor 成员函数 std::unique_ptr<Udp_Server> Server = std::make_unique<Udp_Server>( std::bind(&Dictionary::Transtor, dict.get(), std::placeholders::_1) ); Server->Init(); Server->Start(); return 0; }运行结果