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

难道做网站的工资都不高吗网站建设网站形象

难道做网站的工资都不高吗,网站建设网站形象,惠州网站建设 鑫,如何把jQuery特效做网站背景一 预备知识 1 端口号和进程id 主机间的数据传输本质是两个进程在通信#xff0c;就像是我们打开抖音刷视频#xff0c;视频不是都保存在手机上的#xff0c;而是服务器发送给你的#xff0c;这里就是用到了网络。 那如何保证把数据给指定进程呢? 就是用端口号去标识主机中…一 预备知识 1 端口号和进程id 主机间的数据传输本质是两个进程在通信就像是我们打开抖音刷视频视频不是都保存在手机上的而是服务器发送给你的这里就是用到了网络。 那如何保证把数据给指定进程呢? 就是用端口号去标识主机中的进程是个两字节的数据16个比特位的整数。而ip端口号表示互联网中唯一的进程而两个互联网中唯一的进程间通信就是通过套接字通信。 那端口号不能用进程pid吗? 这样会让进程管理和网络管理耦合度提高所以就重新设计出了端口号。显然一个端口号只能被一个进程绑定但一个进程可以关联多个端口号不过如何理解一个进程绑定多个端口号暂时没想到使用场景。端口号本质就是一个hash表的下标下标存着进程pcb指针。 2 初识网络通信 网络的数据是如何给进程的呢? 首先a主机给b主机发消息然后消息经过有限和无线等传输到了b主机此时数据该给谁呢就是通过数据中的目标端口号找到指定进程直接给进程?不是的后面实现我们就知道进程访问网络来的数据实际上是访问文件所以os不是直接把数据给进程的而是找到进程后找到文件描述符表通过套接字找到文件然后找到缓冲区将数据拷贝到缓冲区中。显然套接字就是一个文件描述符。 2 初识UDP和TCP协议 传输层有两个比较重要的协议就是TCP/UDP。先来认识认识TCPTCP协议是自带可靠性的也就是会在数据传输中如果数据丢失了会采用不同的策略来保证数据能传输给接收者。是面向字节流。 而UDP协议则是面向用户数据报协议是个不可靠传输协议也就是不管数据是否传输成功直接把数据给下一层就不管了。(什么叫面向数据报和面向字节流后面我们会理解反正就是收发单位按一个数据报或者按一个字节来计算)  UDP的意义:使用简单而且对服务器压力小不需要数据百分百送到用户手上就可以用udp。 3 网络字节序 大小端:大端将高权值位放低地址大端反之小端。首先数据有高地址和低地址先发送谁如果不定下来对方怎么知道先接收的是高地址还是低地址好吧那就规定先发出低地址的数据这样接收主机就可以按接受顺序还原数据并且由此规定网络中先发的数据是低地址的后发的数据是高地址的 这样还有新问题当a主机发数据到了b主机上如果两个主机字节序不一样在解读网络发来的数据的时候就会出错。因为b主机是按接收顺序按地址还原的数据所以字节序不会改变。        例如A主机发出0x123456会先把低地址上的数据发出去在网络中排列还是小端B主机是大端机B主机在解释这个数据的时候会按大端字节序来解释就解释反了。而且B主机是无法判断来的数据是大端还是小端所以我们统一规定网络中的数据序列必须是大端。 协议没办法解决因为协议本身也是数据接收数据的主机甚至都无法区分哪里是报头哪里是报文。 转换使用的库函数 二 socket接口 下面有不少函数先别急着记忆后面使用再来理解。 上述函数都有个参数好像是个结构体接下来看看这个sockaddr结构体是什么? 这是一个通用的结构体因为pro 6既想提供主机内通信又想提供跨网络通信这些用的都是不用的协议例如网络通信有ipv4和ipv6标准它们的ip地址就不一样所以必定是传递不同的参数可能要实现不同的接口为了维持接口的一致就要先保证传入的类型一样所以就设计出sockaddr这个通用结构体。 为什么不用void*呢?因为设计时还不支持void*当支持时已经晚了。 网络通信就用中间这个这个结构体本地就用右边的那个结构体调用socket接口函数时都要强转成sockaddr传入内部会根据前十六位看看是什么类型然后判断是网络通信还是本地通信。内部拿到指针还要把数据接收过去所以就让我们传大小他们内部先把数据拿过去然后再做对应类型的强转我还想着为什么不是内部通过指针访问前十六位然后就判断大小就不用我们外部传了现在想想这其实是另一种设计思路我想都可以接下来就实现一个简易的客户端和服务端通信程序来理解理解udp通信。 三 实现服务端 1 初始化服务端 目的客户端给服务端写数据然后客户端也能收到服务端发回的数据。 要一次性实现两个可执行文件makefile就要用下面的格式。all是有依赖关系无依赖方法clean是有依赖方法没有依赖关系。 .PHONY:allall:udp_client udp_server udp_client:udp_client.ccg -o $ $^ -stdc11 udp_server:udp_server.ccg -o $ $^ -stdc11 .PHONY:clean clean:rm -rf udp_client udp_server server.cc提供接口如下。 #includefunctional int main() {udp-start();return 0; } start内部做初始化动作。 先来创建套接字来通信本质是打开一个文件。 参数介绍1 :是表明是ip地址类型因为我们是要网络通信所以可以传AF_INET或者AF_INET6有意思的是我们后面bind的时候还要传一次这是为什么呢?后面提。 参数2是传套接字类型有流式套接字和数据报式套接字参数3为协议如果是TCP参数2用下图第一个udp用第二个也可以传个零内部会根据参数2来判断参数3要传什么协议。显然不同的通信方式要用创建不同的套接字。 那要创建不同的套接字给的参数肯定是不同的如下图这些参数是要保存起来的所以创建文件前要提前知道大小给下面的数据开空间例如网络套接字需要ipipv4和ipv6地址大小不一。所以在创建文件前就要说明ip的地址类型。面向字节流的文件和面向数据报的文件据我目前理解是不同的后面看完协议或许能理解区别所以在创建时要说明文件是面向字节流的还是面向数据报的以上我对socket参数的理解。 那什么时候初始化文件传参初始化套接字属性呢? 绑定的时候。 参数1就是上面调用socket返回的文件描述符第二个参数就是那个通用结构体第三个参数就是套接字的大小为什么要传大小也已经提过啦比较复杂的就是初始化这个结构体。我们只要初始化前三个成员即可。 第一个成员比较有意思这个宏实际上是在定义一个变量被替换成sa_family_t   sin_family。 sa_family_t类型恰好就是一个十六位的类型保存的是就是套接字类型标识。我们知道这个标识是用来分清楚传入的指针是sockadd_in还是sockaddr_un为什么bind还要再传套接字标识符呢我们不是已经在创建文件前说好套接字类型了吗那文件属性应该保存了套接字类型了吧所以bind的时候就不用再传套接字属性。 我认为设计者可以获取文件属性知道绑定的文件需要什么类型的参数这样对传入参数直接强转也可以外部指定类型内部做判断强转都可以只是当时用了后者的实现方式。 而且套接字类型标识怎么不是SOCK_STEREAM和SOCK_DGRAN呢? 为什么我们在初始化传的是AF_INET? 我的理解就是套接字需要ip端口还需要标识是面向数据报还是面向字节流这些都是套接字的属性实际上属性就是套接字的类型所以SOCK_STEREAM和SOCK_DGRAN是套接字类型标识AF_INET和AF_UNIX也被称为套接字类型标识。 而在bind的时候传的参数一般是ip和端口例如网络通信要传32的ip地址而ipv6下要传128位主机通信又不用传ip地址所以就用AF_INET和AF_UNIXAF_INET6来区分而不是用SOCK_STEREAM和SOCK_DGRAN这两个宏。 sin_port保存的是端口号有个细节就是我们对这个端口号做了主机转网络序列显然端口号是要被发送到网络上的。 sin_addr是sock结构体内的结构体成员就是一个四字节的ip地址但是我们没有传一个具体的ip地址首先我这代码就是在云服务器下跑的云服务器一般不允许用户bind公网ip地址貌似是和安全有关系而且直接绑定也不好首先云服务器有多个ip地址如果绑定了具体的ip地址我们写的程序就只能接收固定目标ip地址的数据如果我们像上图传个INADDR_ANY(全0没必要转网络字节序)此时这个服务器上收到的消息只要是给我们这个进程对应的端口号那我们就都能接收。我们服务端代码指定端口号这也是个细节下面会再谈。 所以服务器的大致实现如下。 ​const static uint16_t default_port 8080;class UdpServer{public:UdpServer(u_int16_t port default_port)//缺省参数要从左往右给:_port(port){;}~UdpServer(){}void start(){ //打开网络文件socket_ socket(AF_INET,SOCK_DGRAM,0);if(socket_ 0){std::coutcreate socket errorstrerror(errno)std::endl;exit(SOCKET_ERR);}std::coutcreate socket successsstd::endl;//绑定端口号和ip地址struct sockaddr_in sock;//头文件netinet/in.hbzero(sock,sizeof(sock));sock.sin_addr.s_addr INADDR_ANY;//设置ip地址 表示所有的ip的地址sock.sin_port htons(_port);sock.sin_family AF_INET; if(bind(socket_,(sockaddr*)(sock),sizeof(sock)) 0){std::coutbind error strerror(errno)std::endl;exit(BIND_ERR);}std::coutbind successsstd::endl;}private:int socket_;uint16_t _port;};​ 前面说了我们写的服务端ip地址由云服务器上的os来绑定那端口号为什么也是自己指定呢?为什么不能让os随机应变看到哪个端口号空了就把那个端口号给我的服务端因为我们服务端这个进程的端口号就像是报警电话是不能经常变的所以需要公司对服务器的端口号做统一划分这样客户端通信的时候就一直都知道服务端的端口号是什么。 注意exit返回的错误宏统一封装在err.hpp中。 2 服务端工作函数 前面已经说了服务端如何打开文件显然网络中来的数据要被放到这个文件内所以我们接下来就要让服务端从这个文件中读取数据就是用到下面的recvfrom函数。 从sockfd读数据到buf中buf长度为lenflag表示读取方式有阻塞和非阻塞。返回值表示读取的字节。还有两个输入输出型参数意义因为要记录是谁发过来的数据所以需要保存对方的ip端口号下面会有用的。 当我们可以收数据了就可以再把数据发给客户端sendto函数就是发消息的函数后面两个参数就是表示要给谁发数据这就是为什么前面recvfrom函数中要传入一个sock结构体的原因此时就会把我们bind的ip和端口发给客户端。 有意思的是我们这个sock内部保存的客户端的ip和端口是我们先前从网络中读取的此时这个数据是什么序列呢?有兴趣的可以尝试一下就会发现recvfrom函数没帮我们转成主机序列所以我们在将sock内的数据要发送到网络时也就不用再转成网络序列了。 所以按照当前设计就是服务端创建好文件后就死循环地从文件读和写。 四 客户端实现 也要创建套接字来通信 客户端要不要绑定呢? 不用要os自己指定ip和端口号。为什么不要自己绑定呢?因为我们手机上会多个客户端如果每个软件设计者都自己指定端口号那冲突了怎么办? 那服务端的端口号为什么要自己指定呢?免得服务端挂了后端口号就变了那我们的软件难道都要更新服务端的端口号吗? 所以软件服务端的端口号是固定的那服务器上不会有很多个进程多个服务端吗那会不会别的进程在抢端口号导致某个进程起不来不会的这些端口号都会被公司统一管理分配你有见过哪家微信的服务端部署在阿里上吗所以不会出现竞争。 可是要发消息用sendto函数的时候就有点犯难了如何知道服务器的IP和端口显然服务器的ip和端口也是设计者一开始传入的。 客户端如果不给服务端发消息服务端就无法获取客户端的ip和端口也无法给客户端发消息客户端也无法获取服务端ip和端口所以在编写客户端时会传入ip和端口。 ./client 服务端ip port int main(int argc,char*argv[]) {if(argc ! 3){usage(argv[0]);std::coutstage errorstrerror(errno)std::endl;exit(USAGE_ERR);}const std::string ip_ argv[1];u_int16_t port atoi(argv[2]);int socket_ socket(AF_INET,SOCK_DGRAM,0);if(socket_ 0){std::coutcreate socket errorstrerror(errno)std::endl;exit(SOCKET_ERR);}std::coutcreate socket successsstd::endl; //打开网络文件//发消息struct sockaddr_in sock;sock.sin_addr.s_addr inet_addr(ip_.c_str());将字符串类型的地址转为四字节地址,而且是网络字节序了sock.sin_port htons(port);sock.sin_family AF_INET;pthread_t id;pthread_create(id,nullptr,recv,socket_);while(true){std::string msg;std::cout客户端# std::endl;getline(std::cin,msg);sendto(socket_,msg.c_str(),msg.size(),0,(sockaddr*)sock,sizeof(sock));struct sockaddr_in tmp;char buffer[1024] {0};socklen_t len sizeof(tmp);int ret recvfrom(socket_,buffer,sizeof(buffer)-1,0,(sockaddr*)tmp,len);buffer[ret] \0;if(ret)std::coutbufferstd::endl;} return 0; } 收消息并且保存服务端的ip和端口虽然我们已经知道了服务端的ip和端口但是总不能让内部做判断不用接收服务端的ip和端口就为了省几个字节没必要本代码比较简单我们只是先搭个架子能让服务端收到消息客户端也能发消息并且收到回信就好了tmp内容会和msg变量内容一样。 那客户端什么时候绑定的呢显然是在sendto函数时使用样例如下。 127.0.0.1是本地环回ip表示当前主机填这个ip就是告诉os数据不用发到网络你直接按照往网络发的步骤做在发给网络前停住发回给自己因为网络通信中一般是不会出错的所以只要我们的数据能走到往网络发的哪一步我们的代码就没问题。毕竟我们也不好弄出两台主机。 五 应用1 如何将服务器改成群聊模式也就是任意一个人发送的消息结果要给每一个人。这里我们就用到了多线程模式一个线程收消息一个线程发消息给所有的用户这其实就是一个生产消费者模型而缓冲区应该放什么呢收到的消息。而我们先前就实现过了一个环形队列CirQueue现在可以直接拿来用了。所以我们在服务端增加了两个线程这两个线程也是我先前封装的小组件我们后面讲怎么用并且把代码贴出来大家看看内部实现即可。 online_用来保存用户的套接字信息方便发消息给所有用户。为什么要保存构建用户名后面方便输出提示显示是谁发的。 添加用户 void adduser(std::string name, struct sockaddr_in sock){{LockGuard lock(mutex_);if(!online_.count(name))//返回1:存在,返回0:不存在online_[name] sock;}} 线程接收数据。 //开始接受数据void Recv(){while(true){struct sockaddr_in sock;char buffer[1024] {0};socklen_t len sizeof(sock);int n recvfrom(socket_,buffer,sizeof(buffer)-1,0,(sockaddr*)sock,len);buffer[n] \0;std::coutport_ sock.sin_port ip sock.sin_addr.s_addr;std::string clientip inet_ntoa(sock.sin_addr);u_int16_t clientport ntohs(sock.sin_port);std::coutport_ clientport ip clientip;std::string name;name clientip;name -;name to_string(clientport);name #;adduser(name,sock);name buffer;推送到环形队列rp_.push(name);std::coutclientip-clientport# bufferstd::endl;//输出处理前的字符//上面是在接收客户端的ip和端口号// sendto(socket_, name.c_str(),name.size(),0,(sockaddr*)sock,len);//不用转网络序列,是因为我们接收的时候就没转成主机序列}} 开始广播负责调用sendto函数发消息。 void broadcast()//负责给所有的客户端发消息{while(true){std::string msg;rp_.pop(msg);//获取一条消息std::vectorsockaddr_in tmp;{LockGuard lock(mutex_);for(auto e : online_){tmp.push_back(e.second);}}for(auto e : tmp){sendto(socket_, msg.c_str(),msg.size(),0,(sockaddr*)e,sizeof(e)); //不用转网络序列,是因为我们接收的时候就没转成主机序列}}} 不用给队列加锁环形队列内部会用信号量和锁做保护访问online_要加锁保护所以我在类内又增加一个锁成员mutex_。 在加锁时又用到了之前封装的加锁接口此时直接把锁传入内部会自行加锁这对{}一点都不多余是为了给锁划分作用域。 可是为什么要拷贝一份再sendto呢这里就非常巧妙了因为我们如果是直接访问online_那就要先加锁那sendto也在加锁内sendto又不会有线程安全的问题为了符合加锁区域越小越好所以就设计了这样的代码在获取用户ip时加锁免得另一个线程来添加获取完后就可以直接发消息了。 环形队列实现。 templateclass T class CirQueue { public:CirQueue(int n SIZE):vt_(n),head_(0),tail_(0){sem_init(Consumer_,0,0);sem_init(Productor_,0,n);pthread_mutex_init(Con_mutex,nullptr);pthread_mutex_init(Pro_mutex,nullptr);}~CirQueue(){sem_destroy(Consumer_);sem_destroy(Productor_);pthread_mutex_destroy(Con_mutex);pthread_mutex_destroy(Pro_mutex);}void Lock(pthread_mutex_t mutex){pthread_mutex_lock(mutex);}void Unlock(pthread_mutex_t mutex){pthread_mutex_unlock(mutex);}void push(const T data){sem_wait(Productor_);//申请信号量tail_ tail_ % vt_.size();Lock(Con_mutex);//加锁vt_[tail_] data;Unlock(Con_mutex);//解锁sem_post(Consumer_);//释放信号量}void pop(T data){sem_wait(Consumer_);head_ head_ % vt_.size();Lock(Pro_mutex);data vt_[head_];//多线程访问会出问题,这里还要加锁,因为信号量只是起到限制执行流数量Unlock(Pro_mutex);sem_post(Productor_);} public:std::vectorT vt_;int head_;int tail_;sem_t Consumer_;sem_t Productor_;pthread_mutex_t Con_mutex;pthread_mutex_t Pro_mutex; }; 线程创建封装 class Thread { public:typedef enum{NEW 1,RUNING,EXIT}status;// typedef void* (*fun_t)(void*);using fun_t std::functionvoid();Thread(){;}Thread(int num,fun_t fun):id_(0),status_(NEW),fun_(fun){name_ thread- std::to_string(num);}~Thread(){;}static void * threadRun(void*arg){Thread* th (Thread*) arg;// th-fun_(th-arg_);th-fun_();return nullptr;}void Run(){int n pthread_create(id_,nullptr,threadRun,(void*)this);if(n ! 0)//成功返回0不成功返回错误码exit(4);status_ RUNING; }void join(){pthread_join(id_,nullptr);status_ EXIT;}std::string getname(){return name_;}int getstatus(){return status_;}pthread_t getid(){if(status_ RUNING)return id_;else{std::coutname_ not create ;return 1;} }pthread_t id_;std::string name_;//线程名status status_;//线程状态fun_t fun_;//线程执行函数void* arg_;//线程参数 }; 我们前面添加了不少成员构造和析构函数也有不少变化构造函数Thread的构造函数很简单一个数字用来给内部做线程名还有个可调用对象。 释放锁和回收线程。 start函数内让两个线程跑起来。
http://www.zqtcl.cn/news/346787/

相关文章:

  • 微信网站怎么做下载附件wordpress英文主题汉化
  • 桂平网站设计python基础教程第二版
  • wordpress hermit杭州企业seo网站优化
  • 贵州做团队培训的网站法学网站阵地建设
  • 网站死链是什么西宁高端网站开发公司
  • 做团购网站的公司wordpress附件存放位置
  • 成都最专业做网站的仿win8网站模板
  • 国外设计类网站男女做暖暖试看网站
  • 网站设计哪个好珠海微网站进入
  • 云主机开网站教程模板网会员
  • 网站建设无锡虚拟网站官网
  • 品牌网站设计联系东莞网站优化公
  • 自己做整个网站的流程php装修网站源码
  • 天津网站建设班模拟网站建设软件有哪些
  • 服务类的网站怎么做做软件的网站担保网站
  • 最新电子产品网站模板海口网站排名提升
  • 北京社保网站减员怎么做phpcms v9 实现网站搜索
  • 视频运营管理网站济南网站建设 济南货梯
  • html电影网站模板下载工具阿里云网站建设 部署与发布笔记
  • 建设跨境网站微信seo是什么意思
  • 我做彩票网站开发彩票网站搭建织梦如何仿手机网站源码下载
  • 东仓建设网站手机便宜的网站建设
  • 吕梁市住房与城乡建设厅网站wordpress 乐趣公园
  • 沈阳正规制作网站公司吗德成建设集团有限公司网站
  • 做网站标准步骤大学两学一做专题网站
  • 如何在手机上做网站Windows怎么建设网站
  • 专门做稀有产品的网站海口网站制作设计
  • 怎么查看自己的网站是否被百度收录网站的设计制作流程
  • 视觉设计网站芜湖做网站找哪家好
  • flash网站源码带asp后台电子商务有限公司网站