UdpSocket

UdpSocket
相关接口#include sys/socket.h int socket(int domain, int type, int protocol);这个是套接字函数套接字实际上就是IP端口IP用于标识主机端口用于标识主机上面的进程返回值成功返回一个文件描述符失败返回-1domain:一般为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失败返回-1ssokfd通过调用socket函数返回的文件描述符addr:就是我们的sockaddr_in使用的时候需要强转为const struct addraddlenaddr的大小接受消息的函数#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);成功返回实际收到的信息的长度失败返回-1sockfd:自己通过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_ptrUdp_Server Server std::make_uniqueUdp_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(pos1); 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_mapstd::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::functionstd::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_ptrDictionary dict std::make_sharedDictionary(); // 创建 Udp_Server绑定 Dictionary 的 Transtor 成员函数 std::unique_ptrUdp_Server Server std::make_uniqueUdp_Server( std::bind(Dictionary::Transtor, dict.get(), std::placeholders::_1) ); Server-Init(); Server-Start(); return 0; }运行结果群聊功能