国产亚洲精品福利在线无卡一,国产精久久一区二区三区,亚洲精品无码国模,精品久久久久久无码专区不卡

當(dāng)前位置: 首頁(yè) > news >正文

廣安網(wǎng)站制作設(shè)計(jì)掃描圖片找原圖

廣安網(wǎng)站制作設(shè)計(jì),掃描圖片找原圖,福建漳州網(wǎng)站建設(shè)價(jià)格,網(wǎng)頁(yè)版微信傳文件UDP構(gòu)建服務(wù)器 x 預(yù)備知識(shí) 認(rèn)識(shí)UDP協(xié)議 此處我們也是對(duì)UDP(User Datagram Protocol 用戶數(shù)據(jù)報(bào)協(xié)議)有一個(gè)直觀的認(rèn)識(shí); 后面再詳細(xì)討論. 傳輸層協(xié)議無(wú)連接不可靠傳輸面向數(shù)據(jù)報(bào) 網(wǎng)絡(luò)字節(jié)序 我們已經(jīng)知道,內(nèi)存中的多字節(jié)數(shù)據(jù)相對(duì)于內(nèi)存地址有大端和小端之分, 磁盤文件中的…

UDP構(gòu)建服務(wù)器 x

預(yù)備知識(shí)

認(rèn)識(shí)UDP協(xié)議

此處我們也是對(duì)UDP(User Datagram Protocol 用戶數(shù)據(jù)報(bào)協(xié)議)有一個(gè)直觀的認(rèn)識(shí); 后面再詳細(xì)討論.

  • 傳輸層協(xié)議
  • 無(wú)連接
  • 不可靠傳輸
  • 面向數(shù)據(jù)報(bào)
網(wǎng)絡(luò)字節(jié)序

我們已經(jīng)知道,內(nèi)存中的多字節(jié)數(shù)據(jù)相對(duì)于內(nèi)存地址有大端和小端之分, 磁盤文件中的多字節(jié)數(shù)據(jù)相對(duì)于文件中的偏
移地址也有大端小端之分, 網(wǎng)絡(luò)數(shù)據(jù)流同樣有大端小端之分. 那么如何定義網(wǎng)絡(luò)數(shù)據(jù)流的地址呢?

  • 發(fā)送主機(jī)通常將發(fā)送緩沖區(qū)中的數(shù)據(jù)按內(nèi)存地址從低到高的順序發(fā)出;
  • 接收主機(jī)把從網(wǎng)絡(luò)上接到的字節(jié)依次保存在接收緩沖區(qū)中,也是按內(nèi)存地址從低到高的順序保存;
  • 因此,網(wǎng)絡(luò)數(shù)據(jù)流的地址應(yīng)這樣規(guī)定:先發(fā)出的數(shù)據(jù)是低地址,后發(fā)出的數(shù)據(jù)是高地址.
  • TCP/IP協(xié)議規(guī)定,網(wǎng)絡(luò)數(shù)據(jù)流應(yīng)采用大端字節(jié)序,即低地址高字節(jié).
  • 不管這臺(tái)主機(jī)是大端機(jī)還是小端機(jī), 都會(huì)按照這個(gè)TCP/IP規(guī)定的網(wǎng)絡(luò)字節(jié)序來(lái)發(fā)送/接收數(shù)據(jù);
  • 如果當(dāng)前發(fā)送主機(jī)是小端, 就需要先將數(shù)據(jù)轉(zhuǎn)成大端; 否則就忽略, 直接發(fā)送即可;
   #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);
  • 這些函數(shù)名很好記,h表示host(本地),n表示network(網(wǎng)絡(luò)),l表示32位長(zhǎng)整數(shù),s表示16位短整數(shù)
  • 例如htonl表示將32位的長(zhǎng)整數(shù)從主機(jī)字節(jié)序轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)序,例如將IP地址轉(zhuǎn)換后準(zhǔn)備發(fā)送。
  • 如果主機(jī)是小端字節(jié)序,這些函數(shù)將參數(shù)做相應(yīng)的大小端轉(zhuǎn)換然后返回;
  • 如果主機(jī)是大端字節(jié)序,這些 函數(shù)不做轉(zhuǎn)換,將參數(shù)原封不動(dòng)地返回 。
socket編程接口

``// 創(chuàng)建 socket 文件描述符 (TCP/UDP, 客戶端 + 服務(wù)器) int socket(int domain, int type, int protocol);`

// 綁定端口號(hào) (TCP/UDP, 服務(wù)器)
int bind(int socket, const struct sockaddr *address,socklen_t address_len);*

// 開始監(jiān)聽socket (TCP, 服務(wù)器)
int listen(int socket, int backlog);

// 接收請(qǐng)求 (TCP, 服務(wù)器)
int accept(int socket, struct sockaddr* address,socklen_t* address_len);

// 建立連接 (TCP, 客戶端)
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

sockaddr結(jié)構(gòu)
  • Pv4和IPv6的地址格式定義在netinet/in.h中,IPv4地址用sockaddr_in結(jié)構(gòu)體表示,包括16位地址類型, 16位端口號(hào)和32位IP地址.1

  • IPv4、 IPv6地址類型分別定義為常數(shù)AF_INET、 AF_INET6. 這樣,只要取得某種sockaddr結(jié)構(gòu)體的首地址,不需要知道具體是哪種類型的sockaddr結(jié)構(gòu)體,就可以根據(jù)地址類型字段確定結(jié)構(gòu)體中的內(nèi)容.

  • socket API可以都用struct sockaddr *類型表示, 在使用的時(shí)候需要強(qiáng)制轉(zhuǎn)化成sockaddr_in; 這樣的好處是程序的通用性, 可以接收IPv4, IPv6, 以及UNIX Domain Socket各種類型的sockaddr結(jié)構(gòu)體指針做為參數(shù);

    有sockaddr 結(jié)構(gòu),sockaddr_in 結(jié)構(gòu), sockaddr_un 結(jié)構(gòu)

sockaddr_in 結(jié)構(gòu) 用于網(wǎng)絡(luò)中通訊。

sockaddr_un 結(jié)構(gòu) 用于本地進(jìn)程間的通信。

我們應(yīng)該要知道的是,sockaddr 結(jié)構(gòu),sockaddr_in 結(jié)構(gòu), sockaddr_un 結(jié)構(gòu),構(gòu)成了多態(tài)

sockaddr 結(jié)構(gòu)是基類,sockaddr_in 結(jié)構(gòu), sockaddr_un 結(jié)構(gòu)是衍生類。

我們現(xiàn)在只需要了解sockaddr_in 結(jié)構(gòu)。

在這里插入圖片描述

構(gòu)建服務(wù)器

初始化服務(wù)器

1 .創(chuàng)建socket接口,打開網(wǎng)絡(luò)文件

2 .給服務(wù)器指明IP 和 PORT

3 .bind 將 sockaddr 套接字字段和 網(wǎng)絡(luò)文件描述符 進(jìn)行綁定

?

代碼:

void Init(){//1. 創(chuàng)建socket接口,打開網(wǎng)絡(luò)文件_sock=socket(AF_INET,SOCK_DGRAM,0);if(_sock==-1){std::cerr<<"socket error"<<strerror(errno)<<std::endl;exit(SOCKET_ERR);}std::cout<<"creat socket success:"<<_sock<<std::endl;//2.給服務(wù)器指明IP 和 PORTstruct sockaddr_in local;memset(&local,0,sizeof(local));local.sin_family=AF_INET;//端口號(hào)和IP地址都需要通過(guò)網(wǎng)絡(luò)發(fā)送給目標(biāo),所以需要轉(zhuǎn)換成網(wǎng)絡(luò)序列local.sin_port=htons(_port);//字符串風(fēng)格的IP需要轉(zhuǎn)換成4字節(jié)int   ,1.1.1.1 -》》intlocal.sin_addr.s_addr=INADDR_ANY;//char* 類型的 c_str();//此時(shí)的 local 還只是一個(gè)臨時(shí)變量// bind 將 addr套接字字段和 網(wǎng)絡(luò)文件描述符 進(jìn)行綁定if(bind(_sock,(sockaddr*)&local,sizeof(local))==-1){std::cerr<<"bind error"<<strerror(errno)<<std::endl;exit(BIND_ERR);}std::cout<<"bind success"<<std::endl;}

到這里,我們服務(wù)器的初始化就算成功了,接下來(lái)就是啟動(dòng)服務(wù)器了.

啟動(dòng)服務(wù)器

我們需要接收來(lái)自客戶端的消息,并將消息進(jìn)行處理后返回給客戶端。

這就意味著,服務(wù)器需要有最基礎(chǔ)的兩項(xiàng)功能,

接收網(wǎng)絡(luò)消息,并在網(wǎng)絡(luò)上發(fā)送消息

  • 定義一個(gè)緩沖區(qū),里面用來(lái)存儲(chǔ)接收的消息
  • 我們同時(shí)也需要知道是誰(shuí)給我發(fā)送的消息,定義一個(gè)sockaddr_in 結(jié)構(gòu),用于保存客戶的IP和PORT
  • 最后,將消息返回給客戶端

代碼:

 void Start(){char buffer[1024];while(true){struct sockaddr_in client;socklen_t len=sizeof(client);//client是一個(gè)接收型參數(shù),存儲(chǔ)了給服務(wù)器發(fā)送消息的客戶端的IP+端口號(hào)int n=recvfrom(_sock,buffer,sizeof(buffer)-1,0,(sockaddr*)&client,&len);if(n>0) buffer[n]='\0';else continue;std::string clientIp=inet_ntoa(client.sin_addr);int clientport=ntohs(client.sin_port);std::cout<<clientIp<<'-'<<clientport<<" "<<"client echo#"<<buffer<<std::endl;sendto(_sock,buffer,strlen(buffer),0,(sockaddr*)&client,len);}}

整個(gè)服務(wù)端的簡(jiǎn)易編寫就此完成:

代碼:

udp_server.hpp

#pragma once 
#include<sys/socket.h>
#include<sys/types.h>
#include<iostream>
#include<cstring>
#include<errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include"err.hpp"class UdpServer{
public:public:static const u_int16_t DEFAULT_PORT=8080;UdpServer(uint16_t port=DEFAULT_PORT):_port(port){std::cout<<"PORT:"<<_port<<std::endl;}void Init(){//1. 創(chuàng)建socket接口,打開網(wǎng)絡(luò)文件_sock=socket(AF_INET,SOCK_DGRAM,0);if(_sock==-1){std::cerr<<"socket error"<<strerror(errno)<<std::endl;exit(SOCKET_ERR);}std::cout<<"creat socket success:"<<_sock<<std::endl;//2.給服務(wù)器指明IP 和 PORTstruct sockaddr_in local;memset(&local,0,sizeof(local));local.sin_family=AF_INET;//端口號(hào)和IP地址都需要通過(guò)網(wǎng)絡(luò)發(fā)送給目標(biāo),所以需要轉(zhuǎn)換成網(wǎng)絡(luò)序列local.sin_port=htons(_port);//字符串風(fēng)格的IP需要轉(zhuǎn)換成4字節(jié)int   ,1.1.1.1 -》》intlocal.sin_addr.s_addr=INADDR_ANY;//char* 類型的 c_str();//此時(shí)的 local 還只是一個(gè)臨時(shí)變量// bind 將 addr套接字字段和 網(wǎng)絡(luò)文件描述符 進(jìn)行綁定if(bind(_sock,(sockaddr*)&local,sizeof(local))==-1){std::cerr<<"bind error"<<strerror(errno)<<std::endl;exit(BIND_ERR);}std::cout<<"bind success"<<std::endl;}void Start(){char buffer[1024];while(true){struct sockaddr_in client;socklen_t len=sizeof(client);//client是一個(gè)接收型參數(shù),存儲(chǔ)了給服務(wù)器發(fā)送消息的客戶端的IP+端口號(hào)int n=recvfrom(_sock,buffer,sizeof(buffer)-1,0,(sockaddr*)&client,&len);if(n>0) buffer[n]='\0';else continue;std::string clientIp=inet_ntoa(client.sin_addr);int clientport=ntohs(client.sin_port);std::cout<<clientIp<<'-'<<clientport<<" "<<"client echo#"<<buffer<<std::endl;sendto(_sock,buffer,strlen(buffer),0,(sockaddr*)&client,len);}}private:int _sock;uint16_t _port;//std::string _ip;//  云服務(wù)器,或者一款服務(wù)器,一般不要指明某一個(gè)確定的IP, 讓我們的udpserver在啟動(dòng)的時(shí)候,bind本主機(jī)上的任意IP
};

udp_server.cc

#include"udp_server.hpp"
#include<iostream>
#include<memory>using namespace std;static void usage(string prc)
{cout<<"Usage\n\t"<<prc<<"port\n"<<endl;
}int main(int argc,char* argv[])
{if(argc!=2){usage(argv[0]);exit(USAGE_ERR);}uint16_t port=atoi(argv[1]);unique_ptr<UdpServer> us(new UdpServer(port));us->Init();us->Start();return 0;
}
客戶端簡(jiǎn)易實(shí)現(xiàn)

接下來(lái)我們進(jìn)行客戶端的編寫,客戶端的編寫基本按照服務(wù)端的端口來(lái)進(jìn)行

現(xiàn)階段也是只需要向服務(wù)端發(fā)送消息,接收服務(wù)端的消息這兩種功能

流程:

  • 創(chuàng)建套接字
  • 我們要向客戶端發(fā)送消息,需要知道其IP和PORT,所以需要一個(gè)sockaddr_in結(jié)構(gòu),填充的是服務(wù)端的信息
  • 然后就是發(fā)送消息和接收消息

但是客戶端的編寫有一些需要注意的地方

1.服務(wù)端的端口號(hào)和IP地址都sh是需要我們自己綁定的,那么客戶端需要嗎?
答:客戶端也是需要綁定的,但是不需要我們自己手動(dòng)綁定,而是OS幫我們進(jìn)行操作。
原因:client的port要隨機(jī)讓OS分配防止client出現(xiàn)啟動(dòng)沖突(例如:我們的手機(jī)終端上有很多的軟件(客戶端),如果自己bind,會(huì)可能導(dǎo)致某些服務(wù)的端口號(hào)被占用,無(wú)法啟動(dòng))

2.服務(wù)端為什么需要自己綁定?
原因:
1. server的端口不能隨意改變,眾所周知且不能隨意改變的
2. 同一家公司的port號(hào)需要統(tǒng)一規(guī)范化

代碼:
udp_client.cc

#include<sys/socket.h>
#include<sys/types.h>
#include<iostream>
#include<cstring>
#include<errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include"err.hpp"
using namespace std;//127.0.0.1(本地回環(huán)) 表示的是當(dāng)前主機(jī),用于進(jìn)行本地通訊或者測(cè)試
static void Usage(string prc)
{cout<<"Usage\t\n"<<prc<<" serverip serverport\n"<<endl;
}int main(int argc,char* argv[])//./server 目標(biāo)IP 目標(biāo)PORT
{if(argc!=3){Usage(argv[0]);exit(USAGE_ERR);}string serverip=argv[1];uint16_t serverport=atoi(argv[2]);int sock=socket(AF_INET,SOCK_DGRAM,0);if(sock<0){cerr<<"socket fail"<<strerror(errno)<<endl;exit(SOCKET_ERR);}struct sockaddr_in server;//目標(biāo)服務(wù)器的IP+PORTmemset(&server,0,sizeof(server));server.sin_family=AF_INET;server.sin_port=htons(serverport);server.sin_addr.s_addr=inet_addr(serverip.c_str());while(true){char buffer[1024];cout<<"請(qǐng)輸入#";cin>>buffer;//1. client 這里要不要bind呢?要的!socket通信的本質(zhì)[clientip:clientport, serverip:serverport]// 2.client不需要自己bind,也不要自己bind,操作系統(tǒng)自動(dòng)給我們進(jìn)行bind -- 為什么?client的port要隨機(jī)讓OS分配防止client出現(xiàn)啟動(dòng)沖突(我們的手機(jī)終端上有很多的軟件(客戶端),如果自己bind,會(huì)可能導(dǎo)致某些服務(wù)的端口號(hào)被占用,無(wú)法啟動(dòng))// 3.server 為什么要自己bind?1. server的端口不能隨意改變,眾所周知且不能隨意改變的 2. 同一家公司的port號(hào)需要統(tǒng)一規(guī)范化sendto(sock,buffer,strlen(buffer),0,(sockaddr*)&server,sizeof(server));char Rebuffer[1024];sockaddr_in tmp;socklen_t len=sizeof(tmp);int n=recvfrom(sock,Rebuffer,sizeof(Rebuffer)-1,0,(sockaddr*)&tmp,&len);if(n>0) Rebuffer[n]='\0';std::cout<<serverip<<'-'<<serverport<<" "<<"server echo#"<<Rebuffer<<std::endl;}return 0;
}

好需要對(duì)錯(cuò)誤碼進(jìn)行處理:
err.hpp

#pragma onceenum ERR{SOCKET_ERR=1,BIND_ERR,USAGE_ERR
};

在服務(wù)器中,我們實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的echo型服務(wù)器,就不對(duì)消息進(jìn)行處理,只是進(jìn)行將消息簡(jiǎn)單的返回。

實(shí)現(xiàn)效果
在這里插入圖片描述

構(gòu)建一個(gè)群聊系統(tǒng)

在之前建設(shè)的服務(wù)器基礎(chǔ)上,進(jìn)行加工處理,便可以實(shí)現(xiàn)一個(gè)簡(jiǎn)易的群聊功能
我們可以這么理解群聊:

就是你把自己的消息發(fā)送給服務(wù)器,服務(wù)器在轉(zhuǎn)發(fā)給每一個(gè)在群聊中的人

所以我們需要做如下改動(dòng):

  1. 需要一個(gè)儲(chǔ)存客戶消息的容器,把連接過(guò)來(lái)的客戶都sockaddr_in結(jié)構(gòu)保存
  2. 還需要一個(gè)緩沖區(qū),保存發(fā)送過(guò)來(lái)的消息
  3. 同時(shí)需要注意的是,不同的客戶來(lái)對(duì)同一個(gè)緩沖區(qū)發(fā)送消息,會(huì)有多線程并發(fā)的問(wèn)題,需要加鎖
  4. 服務(wù)器也需要進(jìn)行多線程化,因?yàn)閱我贿M(jìn)程會(huì)阻塞在接收或者發(fā)送消息上

組件
RingQueue.hpp

#pragma once#include<pthread.h>
#include<semaphore.h>
#include<vector>const int DEFAULT_CAP=10; 
template<class T>
class RingQueue{
public:RingQueue(int num=DEFAULT_CAP):_cap(num),_rq(num),_consume_step(0),_produce_step(0){sem_init(&_consume_sem,0,0);sem_init(&_produce_sem,0,_cap);}~RingQueue(){sem_destroy(&_consume_sem);sem_destroy(&_produce_sem);}void lock(pthread_mutex_t& m){pthread_mutex_lock(&m);}void unlock(pthread_mutex_t& m){pthread_mutex_unlock(&m);}void P(sem_t& s){sem_wait(&s);}void V(sem_t& s){sem_post(&s);}void push(const T& in){P(_produce_sem);lock(_produce_mutex);_rq[_produce_step++]=in;_produce_step%=_cap;unlock(_produce_mutex);V(_consume_sem);}void pop(T* out){P(_consume_sem);lock(_consume_mutex);*out=_rq[_consume_step++];_consume_step%=_cap;unlock(_consume_mutex);V(_produce_sem);}private:std::vector<T> _rq;int _cap;//消費(fèi)者和生產(chǎn)者之間的同步關(guān)系通過(guò)信號(hào)量進(jìn)行調(diào)節(jié)sem_t _consume_sem;sem_t _produce_sem;//當(dāng)多線程運(yùn)行時(shí),生產(chǎn)者和生產(chǎn)者,消費(fèi)鎖者和消費(fèi)者之間的互斥關(guān)系需要通過(guò)鎖來(lái)維持pthread_mutex_t _consume_mutex;pthread_mutex_t _produce_mutex;int _consume_step;int _produce_step;
};

Thread.hpp

#pragma once#include <pthread.h>
#include <iostream>
#include <string>
#include <string.h>
#include <functional>using namespace std;string to_hex(uint64_t n)
{char arr[32];snprintf(arr, sizeof(arr), "0x%x", n);return arr;
}
class Thread
{
public:// typedef void(*func_t)();using func_t = std::function<void()>;typedef enum{NEW = 0,RUNNING,EXISTED} ThreadStatus;Thread(int num, func_t func) : _tid(0), _func(func){char name[108];snprintf(name, sizeof(name), "thread-%d", num);_name = name;_status = NEW;}static void *runHelper(void *args){Thread *ts = (Thread *)args;ts->_func();return nullptr;}void run(){int n = pthread_create(&_tid, nullptr, runHelper, this);if (n != 0){exit(1);}_status = RUNNING;}int get_status(){return _status;}void join(){int n = pthread_join(_tid, nullptr);if (n != 0)exit(2);cout << _name << " "<< "has joined" << endl;_status = EXISTED;}const string &get_name(){return _name;}string get_id(){return to_hex(_tid);}// pthread_t get_id()// {//     return _tid;// }~Thread(){}private:pthread_t _tid;string _name;ThreadStatus _status;func_t _func;// void* _args;//函數(shù)調(diào)用時(shí)需要傳的參數(shù)
};

err.hpp

#pragma onceenum ERR{SOCKET_ERR=1,BIND_ERR,USAGE_ERR
};

代碼:
udp_server.hpp

#pragma once
#include <sys/socket.h>
#include <sys/types.h>
#include <iostream>
#include <cstring>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <functional>
#include <unordered_map>
#include <pthread.h>
#include "RingQueue.hpp"
#include "LockGuard.hpp"
#include "Thread.hpp"
#include "err.hpp"using func_t = std::function<std::string(std::string)>;class UdpServer
{
public:
public:static const u_int16_t DEFAULT_PORT = 8080;UdpServer(uint16_t port = DEFAULT_PORT) : _port(port){std::cout << "PORT:" << _port << std::endl;pthread_mutex_init(&_lock, nullptr);// p=new Thread(1,);p = new Thread(1, std::bind(&UdpServer::Receive, this));c = new Thread(1, std::bind(&UdpServer::send, this));}void Start(){// 1. 創(chuàng)建socket接口,打開網(wǎng)絡(luò)文件_sock = socket(AF_INET, SOCK_DGRAM, 0);if (_sock == -1){std::cerr << "socket error" << strerror(errno) << std::endl;exit(SOCKET_ERR);}std::cout << "creat socket success:" << _sock << std::endl;// 2.給服務(wù)器指明IP 和 PORTstruct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;// 端口號(hào)和IP地址都需要通過(guò)網(wǎng)絡(luò)發(fā)送給目標(biāo),所以需要轉(zhuǎn)換成網(wǎng)絡(luò)序列local.sin_port = htons(_port);// 字符串風(fēng)格的IP需要轉(zhuǎn)換成4字節(jié)int   ,1.1.1.1 -》》intlocal.sin_addr.s_addr = INADDR_ANY; // char* 類型的 c_str();// 此時(shí)的 local 還只是一個(gè)臨時(shí)變量//  bind 將 addr套接字字段和 網(wǎng)絡(luò)文件描述符 進(jìn)行綁定if (bind(_sock, (sockaddr *)&local, sizeof(local)) == -1){std::cerr << "bind error" << strerror(errno) << std::endl;exit(BIND_ERR);}std::cout << "bind success" << std::endl;p->run();c->run();}void Add_user(std::string ip, int port, struct sockaddr_in user){std::string name = ip + " + " + to_string(port);LockGuard lockguard(&_lock);_online[name] = user;}void Receive(){char buffer[1024];while (true){struct sockaddr_in client;socklen_t len = sizeof(client); // client是一個(gè)接收型參數(shù),存儲(chǔ)了給服務(wù)器發(fā)送消息的客戶端的IP+端口號(hào)int n = recvfrom(_sock, buffer, sizeof(buffer) - 1, 0, (sockaddr *)&client, &len);if (n > 0)buffer[n] = '\0';elsecontinue;std::string clientIp = inet_ntoa(client.sin_addr);int clientport = ntohs(client.sin_port);Add_user(clientIp, clientport, client);_rq.push(buffer);std::cout << clientIp << '-' << clientport << " "<< "client echo#" << buffer << std::endl;}}void send(){while (true){std::string message;_rq.pop(&message);LockGuard lockguard(&_lock);//訪問(wèn)臨界資源,需要鎖for (auto &user : _online){sendto(_sock, message.c_str(), strlen(message.c_str()), 0, (sockaddr *)&user.second, sizeof(user.second));}}}~UdpServer(){pthread_mutex_destroy(&_lock);p->join();c->join();}private:int _sock;uint16_t _port;func_t _server;std::unordered_map<std::string, sockaddr_in> _online;RingQueue<std::string> _rq;pthread_mutex_t _lock;Thread *p;Thread *c;// std::string _ip;//  云服務(wù)器,或者一款服務(wù)器,一般不要指明某一個(gè)確定的IP, 讓我們的udpserver在啟動(dòng)的時(shí)候,bind本主機(jī)上的任意IP
};

udp_server.cc

#include "udp_server.hpp"
#include <iostream>
#include <memory>using namespace std;static void usage(string prc)
{cout << "Usage\n\t" << prc << "port\n"<< endl;
}bool isPass(const string &command)
{bool pass = true;auto pos = command.find("rm");if (pos != std::string::npos)pass = false;pos = command.find("mv");if (pos != std::string::npos)pass = false;pos = command.find("while");if (pos != std::string::npos)pass = false;pos = command.find("kill");if (pos != std::string::npos)pass = false;return pass;
}
string excuteCommand(const string &s)
{// 可能有些人會(huì)傳遞一些比較惡劣的代碼,如rm ,所以我們需要進(jìn)行安全檢查if (!isPass(s))return "you are a bad man!";FILE *fp = popen((s.c_str()), "r");if (fp == NULL)return "None";// 獲取結(jié)果string result;char line[2048];while (fgets(line, sizeof(line), fp) != NULL){result += line;}pclose(fp);return result;
}int main(int argc, char *argv[])
{if (argc != 2){usage(argv[0]);exit(USAGE_ERR);}uint16_t port = atoi(argv[1]);// unique_ptr<UdpServer> us(new UdpServer(excuteCommand,port));unique_ptr<UdpServer> us(new UdpServer(port));us->Start();return 0;
}

udp_client.cc

#include <iostream>
#include <bits/stdc++.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <string>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <functional>
#include "Thread.hpp"
#include "err.hpp"
#include <cstring>using namespace std;// 127.0.0.1(本地回環(huán)) 表示的是當(dāng)前主機(jī),用于進(jìn)行本地通訊或者測(cè)試
static void Usage(string prc)
{cout << "Usage\t\n"<< prc << " serverip serverport\n"<< endl;
}void *recver(void *args)
{int sock = *(static_cast<int *>(args));while (true){// 接受char buffer[2048];struct sockaddr_in temp;socklen_t len = sizeof(temp);int n = recvfrom(sock, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&temp, &len);if (n > 0){buffer[n] = 0;std::cout << buffer << std::endl; // 1}}
}int main(int argc, char *argv[]) //./server 目標(biāo)IP 目標(biāo)PORT
{if (argc != 3){Usage(argv[0]);exit(USAGE_ERR);}string serverip = argv[1];uint16_t serverport = atoi(argv[2]);int sock = socket(AF_INET, SOCK_DGRAM, 0);if (sock < 0){cerr << "socket fail" << strerror(errno) << endl;exit(SOCKET_ERR);}struct sockaddr_in server; // 目標(biāo)服務(wù)器的IP+PORTmemset(&server, 0, sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(serverport);server.sin_addr.s_addr = inet_addr(serverip.c_str());pthread_t tid;pthread_create(&tid, nullptr, recver, &sock);while (true){char buffer[1024];cout << "請(qǐng)輸入#";// cin>>buffer;cin.getline(buffer, 1024);// 1. client 這里要不要bind呢?要的!socket通信的本質(zhì)[clientip:clientport, serverip:serverport]// 2.client不需要自己bind,也不要自己bind,操作系統(tǒng)自動(dòng)給我們進(jìn)行bind -- 為什么?client的port要隨機(jī)讓OS分配防止client出現(xiàn)啟動(dòng)沖突(我們的手機(jī)終端上有很多的軟件(客戶端),如果自己bind,會(huì)可能導(dǎo)致某些服務(wù)的端口號(hào)被占用,無(wú)法啟動(dòng))// 3.server 為什么要自己bind?1. server的端口不能隨意改變,眾所周知且不能隨意改變的 2. 同一家公司的port號(hào)需要統(tǒng)一規(guī)范化sendto(sock, buffer, strlen(buffer), 0, (sockaddr *)&server, sizeof(server));}return 0;
}

展示效果:

在這里插入圖片描述

http://aloenet.com.cn/news/28022.html

相關(guān)文章:

  • 濟(jì)寧網(wǎng)站建設(shè)關(guān)鍵字是什么意思
  • 企業(yè)管理軟件是什么杭州seo博客
  • 微信網(wǎng)站制作價(jià)格競(jìng)價(jià)防惡意點(diǎn)擊
  • 做爰視頻無(wú)風(fēng)險(xiǎn)網(wǎng)站廣告有限公司
  • 服裝店網(wǎng)站模板網(wǎng)站快速建站
  • 深圳龍崗網(wǎng)站維護(hù)阿里域名購(gòu)買網(wǎng)站
  • 兩個(gè)人能用的一個(gè)公司做網(wǎng)站嗎怎樣注冊(cè)網(wǎng)站免費(fèi)注冊(cè)
  • 貴陽(yáng)網(wǎng)站建設(shè)制作成都最新消息今天
  • 做網(wǎng)站公司在深圳谷歌瀏覽器官方app下載
  • 安徽建設(shè)行業(yè)安全協(xié)會(huì)網(wǎng)站友情鏈接交易平臺(tái)
  • 社區(qū)電商網(wǎng)站設(shè)計(jì)磁力搜索引擎
  • 寧波公司網(wǎng)站開發(fā)推廣營(yíng)銷是什么
  • 為什么做視頻網(wǎng)站違法礦產(chǎn)網(wǎng)站建設(shè)價(jià)格
  • 阿里云wordpress很慢找seo外包公司需要注意什么
  • 心雨在線高端網(wǎng)站建設(shè)關(guān)鍵詞分析軟件
  • 成都官方網(wǎng)站建設(shè)seo怎么做關(guān)鍵詞排名
  • 建立淘寶客網(wǎng)站福州短視頻seo網(wǎng)紅
  • 做外貿(mào)的網(wǎng)站怎么建立武漢seo網(wǎng)站排名優(yōu)化
  • 做理財(cái)?shù)木W(wǎng)站有哪些搜索引擎推廣方案
  • 網(wǎng)站建設(shè)多久可以建成seo是搜索引擎優(yōu)化
  • 四字母net做網(wǎng)站怎么樣競(jìng)價(jià)推廣和信息流推廣
  • 比較大氣的網(wǎng)站深圳短視頻推廣
  • 邵陽(yáng)網(wǎng)站建設(shè)推廣域名權(quán)重
  • 視頻網(wǎng)站的服務(wù)器建設(shè)百度熱搜榜小說(shuō)排名
  • 委托網(wǎng)站建設(shè)注意什么個(gè)人如何推廣app
  • asp.net視頻網(wǎng)站模板下載站長(zhǎng)網(wǎng)站工具
  • 產(chǎn)品宣傳類網(wǎng)站設(shè)計(jì)互聯(lián)網(wǎng)推廣公司靠譜嗎
  • php網(wǎng)站開發(fā)套模板步驟打開百度首頁(yè)
  • 查詢企業(yè)聯(lián)系方式的軟件亞馬遜排名seo
  • 影城網(wǎng)站建設(shè)濟(jì)南優(yōu)化網(wǎng)站關(guān)鍵詞