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

北京市朝阳区网站制作公司京津冀网站建设公司

北京市朝阳区网站制作公司,京津冀网站建设公司,如何搭建钓鱼网站,网站栏目和版块的设计心得⭐小白苦学IT的博客主页 ⭐初学者必看#xff1a;Linux操作系统入门 ⭐代码仓库#xff1a;Linux代码仓库 ❤关注我一起讨论和学习Linux系统 1.前言 网络编程前言 网络编程是连接数字世界的桥梁#xff0c;它让计算机之间能够交流信息#xff0c;为我们的生活和工作带来便利… ⭐小白苦学IT的博客主页 ⭐初学者必看Linux操作系统入门 ⭐代码仓库Linux代码仓库 ❤关注我一起讨论和学习Linux系统 1.前言 网络编程前言 网络编程是连接数字世界的桥梁它让计算机之间能够交流信息为我们的生活和工作带来便利。从简单的网页浏览到复杂的分布式系统网络编程无处不在。 然而网络编程涉及诸多复杂概念和技术如IP地址、端口号、Socket、TCP/UDP协议等需要我们深入学习和掌握。同时网络环境的复杂性、数据安全性等问题也带来了挑战。 但正是这些挑战让网络编程充满了无限可能。掌握网络编程技术我们可以开发出各种创新应用为人们提供更高效、智能的服务。 本文旨在介绍网络编程的Socket编程接口及其技术分享实用经验帮助读者打下坚实的网络编程基础。 1.socket编程接口 socket常见API // 创建 socket 文件描述符 (TCP/UDP, 客户端 服务器) int socket(int domain, int type, int protocol); // 绑定端口号 (TCP/UDP, 服务器) int bind(int socket, const struct sockaddr *address,socklen_t address_len); // 开始监听socket (TCP, 服务器) int listen(int socket, int backlog); // 接收请求 (TCP, 服务器) int accept(int socket, struct sockaddr* address,socklen_t* address_len); // 建立连接 (TCP, 客户端) int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen); sockaddr结构 socket API是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4、IPv6,以及后面要谈的UNIX DomainSocket. 然而, 各种网络协议的地址格式并不相同. IPv4和IPv6的地址格式定义在netinet/in.h中,IPv4地址用sockaddr_in结构体表示,包括16位地址类型, 16位端口号和32位IP地址。IPv4、IPv6地址类型分别定义为常数AF_INET、AF_INET6. 这样,只要取得某种sockaddr结构体的首地址,不需要知道具体是哪种类型的sockaddr结构体,就可以根据地址类型字段确定结构体中的内容。socket API可以都用struct sockaddr *类型表示, 在使用的时候需要强制转化成sockaddr_in; 这样的好处是程序的通用性, 可以接收IPv4, IPv6, 以及UNIX Domain Socket各种类型的sockaddr结构体指针做为参数。 sockaddr 结构  sockaddr 是一个通用的套接字地址结构它用于表示各种类型的套接字地址。但是sockaddr 结构本身并不包含足够的信息来确定地址的类型因此它通常被更具体的结构如 sockaddr_in所替代。sockaddr 结构的主要作用是为不同的地址结构提供一个统一的接口。 通用性sockaddr是一个通用的套接字地址结构设计初衷是为了能够表示各种类型的套接字地址包括IPv4、IPv6以及其他可能的地址类型。这种通用性使得sockaddr能够作为许多网络编程函数的参数如bind、connect、recvfrom、sendto等用于指明地址信息。扩展性通过定义sa_family字段sockaddr能够区分不同类型的地址结构。这使得在未来引入新的地址类型时不需要修改现有函数的接口只需定义新的地址结构并设置相应的sa_family即可。 sockaddr_in 结构 sockaddr_in 是 sockaddr 结构的一个特例用于表示 IPv4 地址和端口号。它包含了 IP 地址和端口号的信息以及地址族和协议信息。 IPv4特化尽管sockaddr具有通用性但在实际编程中特别是在处理IPv4地址时直接使用sockaddr结构会显得过于复杂和冗余。sockaddr_in结构是针对IPv4地址设计的它包含了IPv4地址和端口号等必要信息并且以更直观和易于操作的方式呈现这些信息。便利性sockaddr_in提供了专门的字段来存储IPv4地址sin_addr和端口号sin_port这使得在处理IPv4网络编程任务时更加方便和高效。同时通过类型转换sockaddr_in结构可以很容易地转换为sockaddr结构从而与需要sockaddr参数的函数兼容。 in_addr结构  in_addr 结构用于表示一个 IPv4 地址。它通常与 sockaddr_in 结构一起使用作为 sin_addr 字段的类型。 在这个结构中s_addr 是一个无符号长整数表示 IPv4 地址。在实际使用中我们通常不会直接操作这个长整数而是使用诸如 inet_pton 和 inet_ntop 这样的函数来将点分十进制格式的 IP 地址如 192.168.1.1转换为 in_addr 结构或者将 in_addr 结构转换为点分十进制格式的字符串。  IPv4地址表示in_addr结构专门用于表示IPv4地址。它通过一个无符号长整数s_addr来存储IPv4地址这种表示方式在网络编程中非常常见。尽管IPv4地址通常以点分十进制的形式表示如192.168.1.1但在内部处理和网络传输时它们通常被转换为这种整数形式。转换方便in_addr结构使得在点分十进制格式和内部整数格式之间转换IPv4地址变得相对简单。通过调用如inet_pton和inet_ntop这样的函数可以轻松实现这两种格式之间的转换从而方便网络编程中的地址处理。 总结一下就是 sockaddr 是一个通用的套接字地址结构用于表示各种类型的地址。sockaddr_in 是 sockaddr 的一个特例用于表示 IPv4 地址和端口号。in_addr 用于表示 IPv4 地址。 这三种结构的存在是为了满足不同网络编程需求和提高编程效率。sockaddr提供了通用性和扩展性sockaddr_in则针对IPv4地址提供了更直观和便利的操作方式而in_addr则专门用于表示和转换IPv4地址。在实际编程中根据具体需求选择合适的结构进行处理可以提高代码的可读性和可维护性。 2.简单UDP的echo服务器(代码实现) 封装 UdpSocket UdpServer.hpp 默认ip用 0.0.0.0 端口8080 对udp服务器进行封装 #pragma once #include Log.hpp #include string #include strings.h #include cstring #include sys/types.h #include sys/socket.h #include netinet/in.h #include arpa/inet.h #includeerrno.h #includefunctionalusing func_t std::functionstd::string (const std::string,uint16_t ,const std::string);uint16_t defaultport 8080; std::string defaultip 0.0.0.0; const int size 1024;enum {SOCKET_ERR 1,BIND_ERR};class UdpServer { public:UdpServer(const uint16_t port defaultport, const std::string ip defaultip):_sockfd(-1), _port(port), _ip(ip),_isrunning(false){}void Init(){//1.创建udp socket_sockfd socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd 0){log.LogMessage(FATAL, socket create error,_sockfd: %d, _sockfd);exit(SOCKET_ERR);}log.LogMessage(INFO, socket create success, _sockfd: %d , _sockfd);//2.bind socketstruct sockaddr_in local;bzero(local,sizeof(local));local.sin_family AF_INET;local.sin_port htons(_port);//需要保证我的端口号是网络字节序列因为该端口号是要给对方发送的。local.sin_addr.s_addr inet_addr(_ip.c_str()); //1.string-uint32_t 2.uint32_t 必须是网络序列的//local.sin_addr.s_addr htonl(INADDR_ANY);if(bind(_sockfd,(const struct sockaddr *)local,sizeof(local))0){log.LogMessage(FATAL,bind error , error: %d, error string : %s,errno,strerror(errno));exit(BIND_ERR);}log.LogMessage(INFO,bind success , error: %d, error string : %s,errno,strerror(errno));}void Run(func_t func){_isrunning true;char inbuffer[size];while(_isrunning){struct sockaddr_in client;socklen_t len sizeof(client);std::coutserver is run!!!std::endl;ssize_t n recvfrom(_sockfd,inbuffer,sizeof(inbuffer) - 1,0,(struct sockaddr *)client,len);if(n0){log.LogMessage(WARNING,recvfrom error, errno: %d ,errno string : %s,errno,strerror(errno));continue;}uint16_t clientport ntohs(client.sin_port);std::string clientip inet_ntoa(client.sin_addr);inbuffer[n] 0;//充当了一次数据的处理std::string info inbuffer;std::string echo_string func(info,clientport,clientip);sendto(_sockfd,echo_string.c_str(),echo_string.size(),0,(struct sockaddr*)client,len);}}~UdpServer() {if(_sockfd0) close(_sockfd);}private:int _sockfd;//网络文件描述符std::string _ip;//字符串类型ip地址uint16_t _port;//服务器进程的端口号 bool _isrunning; }; Main.cc #includeUdpServer.hpp #includememory #includeiostream #includecstdio #includevectorvoid Usage(std::string proc) {std::cout\n\rUsage: proc port[1024]\nstd::endl; }std::string Handler(const std::string str,uint16_t clientport,const std::string clientip) {std::cout[ ip: clientip port: clientport ]# ;std::string res server get a message: ;resstr;std::coutresstd::endl;return res; }bool SafeCheck(const std::string cmd) {std::vectorstd::string key_word {rm,mv,cp,kill,sudo,unlink,uninstall,yum,top};for(auto word:key_word){auto pos cmd.find(word);if(pos!std::string::npos) return false;}return true; }std::string ExcuteCommand(const std::string cmd) {if(!SafeCheck(cmd)) return bad man;FILE* fp popen(cmd.c_str(),r);if(nullptr fp){perror(popen error);return error;}std::string result;char buffer[4096];while(true){char * getc fgets(buffer,sizeof(buffer),fp);if(nullptr getc){break;}resultbuffer;}pclose(fp);return result;}int main(int argc,char* argv[]) {if(argc!2){Usage(argv[0]);exit(1);}uint16_t port std::stoi(argv[1]);std::unique_ptrUdpServer svr(new UdpServer(port));svr-Init();svr-Run(Handler); return 0; } UdpClient.cc(客户端代码) #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.husing namespace std;void Usage(std::string proc) {std::cout \n\rUsage: proc serverip serverport\n std::endl; }// ./udpclient serverip serverport int main(int argc, char *argv[]) {if (argc ! 3){Usage(argv[0]);exit(0);}std::string serverip argv[1];uint16_t serverport std::stoi(argv[2]);struct sockaddr_in server;bzero(server, sizeof(server));server.sin_family AF_INET;server.sin_port htons(serverport); //?server.sin_addr.s_addr inet_addr(serverip.c_str());socklen_t len sizeof(server);int sockfd socket(AF_INET, SOCK_DGRAM, 0);if (sockfd 0){cout socker error endl;return 1;}// client 要bind吗要只不过不需要用户显示的bind一般有OS自由随机选择// 一个端口号只能被一个进程bind对server是如此对于client也是如此// 其实client的port是多少其实不重要只要能保证主机上的唯一性就可以// 系统什么时候给我bind呢首次发送数据的时候string message;char buffer[1024];while (true){cout Please Enter ;getline(cin, message);coutmessageendl;// std::cout message std::endl;// 1. 数据 2. 给谁发sendto(sockfd, message.c_str(), message.size(), 0, (const sockaddr *)server, len);struct sockaddr_in temp;socklen_t len sizeof(temp);ssize_t s recvfrom(sockfd, buffer, 1023, 0, (struct sockaddr*)temp, len);if(s 0) {buffer[s] 0;cout buffer endl;}}close(sockfd);return 0; } 日志类 #pragma once#include iostream #include cstdarg #include ctime #include string #include unistd.h #include fstream #include sys/types.h #include sys/stat.h #include fcntl.henum {DEBUG 0,INFO,WARNING,ERROR,FATAL };enum {Screen 10,Onefile,Classfile };std::string LevelToString(int level) {switch (level){case DEBUG:return Debug;case INFO:return Info;case WARNING:return Warning;case ERROR:return Error;case FATAL:return Fatal;default:return Unknown;} }const int defaultstyle Screen; const std::string default_filename log.; const std::string logdirlog;class Log { public:Log():style(defaultstyle),filename(default_filename){mkdir(logdir.c_str(),0775);}void Enable(int sty){style sty;}std::string TimestampToLocalTime(){time_t curr time(nullptr);struct tm *currtime localtime(curr);char time_buffer[128];snprintf(time_buffer, sizeof(time_buffer), %d-%d-%d %d:%d:%d,currtime-tm_year 1900, currtime-tm_mon, currtime-tm_mday, currtime-tm_hour,currtime-tm_min, currtime-tm_sec);return time_buffer;}void WriteLog(const std::string levelstr, const std::string message){switch (style){case Screen:std::cout messagestd::endl;break;case Onefile:WriteLogToOnefile(all, message);break;case Classfile:WriteLogToClassfile(levelstr, message);break;default:break;}}void WriteLogToOnefile(const std::string logname, const std::string message){umask(0);int fd open(logname.c_str(),O_CREAT | O_WRONLY | O_APPEND,0666);if(fd0)return;write(fd,message.c_str(),message.size());close(fd);// std::ofstream out(logname);// if (!out.is_open())// return;// out.write(message.c_str(), message.size());// out.close();}void WriteLogToClassfile(const std::string levelstr, const std::string message){std::string logname logdir;logname/;lognamefilename;logname levelstr;WriteLogToOnefile(logname, message);}void LogMessage(int level, const char *format, ...) // 类c的日志接口{char rightbuffer[1024];va_list args;va_start(args, format);vsnprintf(rightbuffer, sizeof(rightbuffer), format, args);va_end(args);char leftbuffer[1024];std::string curtime TimestampToLocalTime();std::string levelstr LevelToString(level);std::string idstr std::to_string(getpid());snprintf(leftbuffer, sizeof(leftbuffer), [%s][%s][%s],levelstr.c_str(), curtime.c_str(), idstr.c_str());std::string logInfo leftbuffer;logInfo rightbuffer;WriteLog(levelstr, logInfo);}~Log() {}private:int style;std::string filename; };Log log;class Conf { public:Conf(){log.Enable(Screen);}~Conf(){} };Conf conf; Makefile .PHONY:all all:udpserver udpclientudpserver:Main.ccg -o $ $^ -g -stdc11 udpclient:UdpClient.ccg -o $ $^ -g -stdc11.PHONY:clean clean:rm -rf udpserver udpclient 运行结果 实现了客户端服务端双方交互当然我们这只是简单的进行数据处理其实还可以通过实现其他功能这里可以发挥自己的想象去写。 地址转换函数 这里只介绍基于IPv4的socket网络编程,sockaddr_in中的成员struct in_addr sin_addr表示32位 的IP 地址但是我们通常用点分十进制的字符串表示IP 地址,以下函数可以在字符串表示 和in_addr表示之间转换; 字符串转in_addr的函数: in_addr转字符串的函数: 其中inet_pton和inet_ntop不仅可以转换IPv4的in_addr,还可以转换IPv6的in6_addr,因此函数接口是void*addrptr。 关于inet_ntoa inet_ntoa这个函数返回了一个char*, 很显然是这个函数自己在内部为我们申请了一块内存来保存ip的结果. 那么是否需要调用者手动释放呢? man手册上说, inet_ntoa函数, 是把这个返回结果放到了静态存储区. 这个时候不需要我们手动进行释放. 那么问题来了, 如果我们调用多次这个函数, 会有什么样的效果呢? 参见如下代码: #includestdio.h #includenetinet/in.h #includearpa/inet.hint main() {struct sockaddr_in addr1;struct sockaddr_in addr2;addr1.sin_addr.s_addr0;addr2.sin_addr.s_addr0xffffffff;char* ptr1 inet_ntoa(addr1.sin_addr);char* ptr2 inet_ntoa(addr2.sin_addr);printf(ptr1: %s,ptr2: %s\n,ptr1,ptr2);return 0; } 运行结果 因为inet_ntoa把结果放到自己内部的一个静态存储区, 这样第二次调用时的结果会覆盖掉上一次的结果 如果有多个线程调用 inet_ntoa, 是否会出现异常情况呢?在APUE中, 明确提出inet_ntoa不是线程安全的函数;但是在centos7上测试, 并没有出现问题, 可能内部的实现加了互斥锁;在多线程环境下, 推荐使用inet_ntop, 这个函数由调用者提供一个缓冲区保存结果, 可以规避线程安全问题; 如果测试如下代码 #include stdio.h #include unistd.h #include sys/socket.h #include netinet/in.h #include arpa/inet.h #include pthread.h void *Func1(void *p) {struct sockaddr_in *addr (struct sockaddr_in *)p;while (1){char *ptr inet_ntoa(addr-sin_addr);printf(addr1: %s\n, ptr);}return NULL; } void *Func2(void *p) {struct sockaddr_in *addr (struct sockaddr_in *)p;while (1){char *ptr inet_ntoa(addr-sin_addr);printf(addr2: %s\n, ptr);}return NULL; } int main() {pthread_t tid1 0;struct sockaddr_in addr1;struct sockaddr_in addr2;addr1.sin_addr.s_addr 0;addr2.sin_addr.s_addr 0xffffffff;pthread_create(tid1, NULL, Func1, addr1);pthread_t tid2 0;pthread_create(tid2, NULL, Func2, addr2);pthread_join(tid1, NULL);pthread_join(tid2, NULL);return 0; } 运行结果: 这段代码试图创建两个线程Func1 和 Func2它们分别无限循环地打印两个 sockaddr_in 结构的 IP 地址。这两个 sockaddr_in 结构addr1 和 addr2被初始化为具有特定的 sin_addr.s_addr 值。 addr1.sin_addr.s_addr 被初始化为 0这在 IPv4 地址中通常表示一个未指定的地址或者说是无效的地址。 addr2.sin_addr.s_addr 被初始化为 0xffffffff这在 IPv4 地址中通常表示广播地址。 然而代码中有一些需要注意的地方 inet_ntoa的静态缓冲区inet_ntoa 函数使用静态缓冲区来存储转换后的字符串。这意味着如果两个线程同时调用 inet_ntoa它们可能会覆盖彼此的缓冲区导致不可预测的结果。因此在多线程环境中使用 inet_ntoa 是不安全的。无限循环两个线程都包含一个无限循环这会导致程序永远不会退出除非被外部因素如用户终止中断。pthread_join虽然代码中包含了 pthread_join 调用但由于线程中的无限循环这些调用实际上永远不会返回因此 main 函数也永远不会结束。 测试这段代码时你会看到两个线程分别不停地打印出相同的 IP 地址字符串但由于 inet_ntoa 的问题这些字符串可能会被互相覆盖导致输出变得混乱。 此外具体的输出取决于操作系统的具体实现和线程调度的行为。在某些情况下你可能会看到 addr1 和 addr2 交替出现而在其他情况下你可能会看到某个地址连续出现多次然后被另一个地址覆盖。 总的来说这段代码并不是一个好的示例因为它在多线程环境中不正确地使用了 inet_ntoa并且包含了无限循环这会导致程序行为不可预测且难以管理。 如果你需要在多线程环境中处理 IP 地址建议使用更安全的函数如 inet_ntop并确保正确管理线程的生命周期和同步。
http://www.dnsts.com.cn/news/240722.html

相关文章:

  • 做短链的网站做一个网站难不难
  • 门户网站如何做谷歌seo上海网站建设空间
  • 百度站长工具如何使用雅安市建设网站
  • 重庆专业网站推广报价跨境电商怎么做无货源模式
  • 英文视频网站如何做外链制作表白网站的软件
  • 网站上传大马后怎么做春节彩灯制作公司
  • 我想建个自己的网站聊城高新区建设局网站
  • 天门市规划建设局网站如何做积分商城网站
  • 什么网站是用php做的宁波网站推广制作
  • 重庆云阳网站建设公司推荐青岛网络公司有哪些
  • asp网站变成php公司都是自己制作网站
  • 西安做网站的公司有网站百度不收录的原因
  • 做网站 客户大概会有那些问题山东省建设工程信息网站
  • mixkitcom素材网站买个天猫店多少钱一个
  • 上海网站关键词排名优化报价深圳公司视频制作
  • 美容院门户网站开发成都快速做网站
  • 谷歌官方网站登录入口丹阳建设工程管理处网站
  • 网站页面创意sns网站开发
  • 成都网站制作系统网站功能定位分析
  • 做微信头图的网站wordpress自定义小工具
  • 响应式网站开发遇到的问题wordpress搭建问题
  • 营销型网站建设吉林邢台市做网站电话
  • 物流百度推广怎么做网站南通市住房和城乡建设厅网站
  • 网站系统分析的步骤有哪些青山做网站
  • 科迪兔网站建设松江新城做网站公司
  • 帮企业建设网站销售wordpress pdf缩略图不显示
  • 深圳电子网站建设购物网站可行性分析报告
  • 江阴公司网站建设息县网站建设公司
  • 做网站如何适配手机百度权重高的发帖网站
  • 拼多多网站建设过程最新国家大事件