目录
简单的UDP网络程序
1.1 UdpServer.hpp
cpp
复制代码
#pragma once
#include <iostream>
using namespace std;
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "log.hpp"
#include <strings.h>
#include <functional>
#include <cstring>
#include <unordered_map>
const static int NUM = 1024;
const static string DEFAULT_IP = "0.0.0.0";
const static uint16_t DEFAULT_PORT=8080;
using func_t=function<string(string)>;
Log log;
class UdpServer
{
public:
UdpServer(func_t func,uint16_t port=DEFAULT_PORT,string ip = DEFAULT_IP)
: _ip(ip), _port(port), _sockid(-1),_func(func)
{
}
~UdpServer()
{
if (_sockid > 0)
{
close(_sockid);
}
}
void Init()
{
// 创建套接字
_sockid = socket(AF_INET, SOCK_DGRAM, 0);
if (_sockid < 0)
{
log(Fatal, "socket failed");
exit(2);
}
log(Info,"create socket successful, sockid:%d",_sockid);
// 绑定
struct sockaddr_in local;
bzero(&local, sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(_port);
//绑定任意地址,可以接收任意发送给该主机的信息,而不是绑定一个具体的IP地址
local.sin_addr.s_addr = INADDR_ANY;
if (bind(_sockid, (struct sockaddr *)&local, sizeof(local)) < 0)
{
log(Fatal, "bind failed, errno:%d, error code:%s",errno,strerror(errno));
exit(3);
}
log(Info, "Server bind successful");
}
//通过哈希表检查用户是否已经连上,如果没有就添加到连接的列表中
void CheckUser(const struct sockaddr_in& client)
{
string clientIp=inet_ntoa(client.sin_addr);
auto it=_online_client.find(clientIp);
if(it==_online_client.end())
{
_online_client.insert({clientIp,client});
std::cout << "[" << clientIp << ":" << ntohs(client.sin_port) << "] add to online user." << std::endl;
}
}
//广播给所有人,即给所有连上该服务器的人都发送这条信息,类似于我们的微信群,
//自己发出的信息所有人都能看见
void BroadCast(const string& info,const string& clientip,const uint16_t& clientport)
{
for(const auto& it:_online_client)
{
string message="client";
message+='[';
message+="clientip:";
message+=clientip;
message+=' ';
message+="clientport";
message+=":";
message+=to_string(clientport);
message+="]# ";
message+=info;
sendto(_sockid,message.c_str(),message.size(),0,(struct sockaddr*)(&it.second),sizeof(it.second));
}
}
//启动服务器
void Run()
{
struct sockaddr_in client;
socklen_t len = sizeof(client);
bzero(&client, sizeof(client));
char buffer[NUM];
bzero(buffer, sizeof(buffer));
while (true)
{
ssize_t s = recvfrom(_sockid, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&client, &len);
if (s > 0)
{
buffer[s] = '\0';
//cout << "client# " << buffer << endl;
//printf("client[ip:%d,port:%d]# \n",client.sin_addr.s_addr,client.sin_port);
char* clientip=inet_ntoa(client.sin_addr);
//检查
CheckUser(client);
cout << "client[ip:"<<clientip<<" port:"<<ntohs(client.sin_port)<<"]# " << buffer << endl;
uint16_t clientport=ntohs(client.sin_port);
//广播给所有人
BroadCast(buffer,clientip,clientport);
}
else if(s==0)
{
log(Warning,"client quit...");
break;
}
else
{
log(Fatal,"recvfrom failed...");
break;
}
//string ret=_func(buffer);
//sendto(_sockid, ret.c_str(), ret.size(), 0, (struct sockaddr *)&client, len);
}
}
private:
string _ip;
uint16_t _port;
int _sockid;
//回调函数
func_t _func;
//通过IP地址映射标识一个已经连上服务器的客户端
unordered_map<string,struct sockaddr_in> _online_client;
};
cpp
复制代码
#include <iostream>
using namespace std;
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <strings.h>
#include <pthread.h>
#include <cstring>
// 重定向:2>/dev/pts/(1,2,3,4)
//UdpClient.cc
const static int NUM = 1024;
//客户端使用手册
void Usage(string argv)
{
cout << "\n\t"
<< "Usage:" << argv << " ServerIp ServerPort" << endl<<endl;
}
struct ThreadData
{
int sockid;
struct sockaddr_in server;
string ip;
};
//读取信息
void* recver_message(void* argv)
{
//线程分离
pthread_detach(pthread_self());
ThreadData* td=static_cast<ThreadData*>(argv);
char buffer[4096];
memset(buffer,0,sizeof(buffer));
while(true)
{
struct sockaddr_in t;
socklen_t len=sizeof(t);
ssize_t s=recvfrom(td->sockid,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&t,&len);
string tip=inet_ntoa(t.sin_addr);
if(s>0)
{
buffer[s]='\0';
// cout<<"server# "<<tmp<<endl;
printf("server[ip:%s,port:%d]# %s\n",tip.c_str(),ntohs(t.sin_port),buffer);
}
}
return nullptr;
}
//发送信息
void * sender_message(void* argv)
{
pthread_detach(pthread_self());
ThreadData* td=static_cast<ThreadData*>(argv);
std::string welcome = td->ip;
welcome += " comming...";
sendto(td->sockid, welcome.c_str(), welcome.size(), 0, (struct sockaddr *)&(td->server), sizeof(td->server));
string buffer;
while(true)
{
cerr<<"Please Enter# ";
getline(cin,buffer);
sendto(td->sockid,buffer.c_str(),buffer.size(),0,(struct sockaddr*)(&(td->server)),sizeof(td->server));
}
return nullptr;
}
int main(int argc, char *argv[])
{
if (argc != 3)
{
Usage(argv[0]);
exit(1);
}
string ServerIp=argv[1];
string str = argv[2];
uint16_t ServerPort = (uint16_t)stoi(str.c_str());
ThreadData td;
//创建套接字
int sockid=socket(AF_INET,SOCK_DGRAM,0);
td.server.sin_family = AF_INET;
td.server.sin_addr.s_addr=inet_addr(ServerIp.c_str());
td.server.sin_port=htons(ServerPort);
td.ip=ServerIp;
td.sockid=sockid;
socklen_t len=sizeof(td.server);
pthread_t recver,sender;
pthread_create(&recver,nullptr,recver_message,&td);
pthread_create(&sender,nullptr,sender_message,&td);
while(true)
{
sleep(1);
}
close(sockid);
}
cpp
复制代码
#include <iostream>
using namespace std;
#include <string>
#include "UdpServer.hpp"
#include <vector>
#include <memory>
//服务器的启动方式
void Usage(string argv)
{
cout << "\n\t"
<< "Usage:" << argv << " ServerPort" << endl
<< endl;
}
string func(string s)
{
return s + " already handled\n";
}
//安全检查
bool SafeCheck(const string &cmd)
{
//把客户端发过来的信息当作命令来解析,检查该信息是否合法
vector<string> key_word = {"rm", "mv", "cp", "kill", "sudo", "unlink", "uninstall",
"yum", "top", "while"};
for(const auto& s:key_word)
{
auto pos = cmd.find(s);
if(pos!=string::npos)
{
return false;
}
}
return true;
}
//执行指令
string ExcuteCommand(string cmd)
{
if (!SafeCheck(cmd))
{
return "bad man\n";
}
//popen函数会自己创建子进程,创建管道,让子进程执行cmd.c_str()命令,
//并通过管道把执行cmd命令的结果读取到FILE*的结构体对象中
FILE *p = popen(cmd.c_str(), "r");
if (nullptr == p)
{
perror("popen failed");
exit(5);
}
string ret="\n";
char buffer[4096];
while (true)
{
//把执行命令后的结果按行读取出来
char *s = fgets(buffer, sizeof(buffer) - 1, p);
if (nullptr == s)
{
break;
}
ret += buffer;
}
pclose(p);
return ret;
}
int main(int argc, char *argv[])
{
if (argc != 2)
{
Usage(argv[0]);
exit(1);
}
string str = argv[1];
uint16_t ServerPort = (uint16_t)stoi(str.c_str());
unique_ptr<UdpServer> svr(new UdpServer(func, ServerPort));
svr->Init();
svr->Run();
return 0;
}
1.4 makefile
cpp
复制代码
.PHONY:all
all:Client Server
Client:UdpClient.cc
g++ -o $@ $^ -std=c++11 -lpthread
Server:main.cc
g++ -o $@ $^ -std=c++11 -lpthread
.PHONY:clean
clean:
rm -f Client Server
1.5 log.hpp
cpp
复制代码
#pragma once
#include <iostream>
using namespace std;
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <string>
#include <time.h>
#include <stdarg.h>
// 日志等级
#define Info 0
#define Debug 1
#define Warning 2
#define Error 3
#define Fatal 4
#define Screen 1
#define OneFile 2
//向多个文件打印
#define Classfile 3
#define SIZE 1024
#define LogFile "log.txt"
class Log
{
public:
Log()
{
printMethod = Screen;
path = "./log/";
}
void Enable(int mothod)
{
printMethod = mothod;
}
string LevelToString(int level)
{
switch (level)
{
case Info:
{
return "Info";
}
case Debug:
{
return "Debug";
}
case Warning:
{
return "Warning";
}
case Error:
{
return "Error";
}
case Fatal:
{
return "Fatal";
}
default:
{
return "None";
}
}
}
void printlog(int level,const string& logtxt)
{
switch(printMethod)
{
case Screen:
{
cout<<logtxt<<endl;
break;
}
case OneFile:
{
PrintOneFile(LogFile,logtxt);
break;
}
case Classfile:
{
PrintClassfile(level,logtxt);
break;
}
default:
{
break;
}
}
}
void PrintOneFile(const string& logname,const string& logtxt)
{
string _logname=path+logname;
int fd=open(_logname.c_str(),O_WRONLY|O_CREAT|O_APPEND,0666);
if(fd<0)
{
perror("open fail");
return;
}
write(fd,logtxt.c_str(),logtxt.size());
close(fd);
}
void PrintClassfile(int level,const string& logtxt)
{
string filename=LogFile;
filename+='.';
filename+=LevelToString(level);
PrintOneFile(filename,logtxt);
}
void operator()(int level,const char* format,...)
{
time_t t=time(nullptr);
struct tm* ctime=localtime(&t);
char leftbuffer[SIZE];
snprintf(leftbuffer,SIZE,"[%s][%d-%d-%d %d:%d:%d]",LevelToString(level).c_str(),
ctime->tm_year+1900,ctime->tm_mon+1,ctime->tm_mday,
ctime->tm_hour,ctime->tm_min,ctime->tm_sec);
va_list s;
va_start(s,format);
char rightbuffer[SIZE]={0};
vsnprintf(rightbuffer,SIZE,format,s);
va_end(s);
char logtxt[SIZE*2];
snprintf(logtxt,sizeof(logtxt),"%s %s",leftbuffer,rightbuffer);
printlog(level,logtxt);
}
~Log()
{
}
private:
// 打印方法
int printMethod;
string path;
};