端口号(port)是传输层协议的内容
uint16_t
先对TCP((Transmission Control Protocol)有一个直观的认识,它有以下性质
先对对UDP(User Datagram Protocol)有一个直观的认识,它有以下性质
内存中的多字节数据相对于内存地址有大端和小端之分, 磁盘文件中的多字节数据相对于文件中的偏 移地址也有大端小端之分, 网络数据流同样有大端小端之分. 那么如何定义网络数据流的地址呢?
首先先回顾一下大小端
为了使得网络程序具备可移植性,使得同样的代码在大端和小端计算机上编译后都能运行,可以调用以下库函数,使得网络字节序和主机字节序的相互转换。
#include
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
htonl(uint16_t hostshort)
意思是将16位的短整形主机字节序转换成网络字节序,例如端口号的发送函数原型
#include
#include
int socket(int domain, int type, int protocol);
domain指协议域,常见的协议有AF_INET(ipv4) AF_INET6(ipv6) AF_LOCAL(本地协议)。协议决定了socket的地址类型,在通信中必须采用相应的地址。例如使用的是ipv4的协议,那么参数需要传AF_INET
type为socket的类型,主要分为流格式套接字(SOCK_STREAM)即使用TCP协议和数据报格式,也因此称之为面向连接的套接字,是一种可靠的、双向的通信数据流.它的数据可以准确无误的到达另一台里算计,如果损坏或者丢失会重新发送套接字;(SOCK_DGRAM)即使用UDP协议,也因此称之为无连接的套接字,计算机只负责传输数据,不进行数据校验
protocol默认输入0
若创建成功,返回值是一个socket文件描述符,若创建失败,返回-1,错误信息保存在错误码
函数原型
#include
int bind(int socket, const struct sockaddr *address, socklen_t address_len);
很多网络编程函数诞生早于IPv4协议,那时候都使用的是sockaddr结构体,为了向前兼容,现在sockaddr退化成了(void *)的作用,传递一个地址给函数,至于这个函数是sockaddr_in还是其他的,由地址族确定,然后函数内部再强制类型转化为所需的地址类型。
sackaddr_in结构源码
struct sockaddr_in
{
__SOCKADDR_COMMON(sin_);
int_port_t sin_port; /*Port number. */
struct in_addr sin_addr; /*Internet address. */
/*Pad to size of 'struct sockaddr'. */
unsigned char sin_zero[sizeof(struct sockaddr)-
__SOCKADDR_COMMON_SIZE-
sizeof(in_port_t)-
sizeof(struct in_addr)];
}
__SOCKADDR_COMMON的宏定义
#define __SOCKADDR_COMMON_(sa_prefix)
sa_family_t sa_prefix##fimily
in_addr结构源码
/* Internet address. */
typedef uint32_t in_addr_t;
struct in_addr
{
in_addr_t s_addr;
};
需要注意的是:
根据上面的接口可以写出server端的部分代码
netstat -nupa
server.c
1 #include"server.hpp"
2 #include<memory>
3
4 using namespace std;
5 using namespace udpServer ;
6 static void Usage(string proc)
7 {
8 cerr<<"Usage: \n\t"<<proc<<" serverport"<<endl;
9 }
10 int main(int argc,char* argv[])
11 {
12 if(argc!=2)
13 {
14 Usage(argv[0]);
15 exit(USAGE_ERR);
16 }
17
18 uint16_t port=atoi(argv[1]);//atoi作用:把port的str类型转化成int类型。而uin16_t本质类型是int
19
20 unique_ptr<udpserver> uspr(new udpserver(port));
21 uspr->initudpserver();//初始化服务端
22 uspr->start();//启动服务端
23 return 0;
24 }
server.hpp
1 #pragma once
2 #include<iostream>
3 #include <sys/types.h>
4 #include <sys/socket.h>
5 #include <netinet/in.h>
6 #include <arpa/inet.h>
7 #include <errno.h>
8 #include <strings.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include<unistd.h>
12 using namespace std;
13
14 namespace udpServer
15 {
16 static const string defaultIP="0.0.0.0";//默认IP地址
17 static int SIZE=1024;
18 enum {USAGE_ERR=1,SOCKET_ERR,BIND_ERR,OPEN_ERR};//用枚举函数定义各自错误的返回值
19 class udpserver
20 {
21 public:
22
23 udpserver(const uint16_t& port,const string& ip=defaultIP)
24 :_port(port)
25 ,_ip(ip)
26 ,_sockfd(-1)
27 {}
28
29 void initudpserver()
30 {
31 //1.创建套接字
32 _sockfd=socket(AF_INET,SOCK_DGRAM,0);//创建套接字文件描述符
33 if(_sockfd==-1)
34 {
35 cerr<<"socket err"<<errno<<" : "<<strerror(errno)<<endl;
36 exit(SOCKET_ERR);
37 }
38 cout<<"socket success"<<": "<< _sockfd<<endl;
39 //2.绑定port端口号
40 //2.1将port和ip填入结构体中,该结构体可以理解成用户定义的数据或用户栈
41 struct sockaddr_in local;//创建结构
42 bzero(&local,sizeof(local));//将结构清零
43 local.sin_family=AF_INET;//填充协议家族
44 local.sin_port=htons(_port);//填充端口号。htons将port主机序列转化为网络序列
45 //local.sin_addr.s_addr=inet_addr(_ip.c_str());
46 //填充ip地址。inet_addr函数作用:将ip地址的string类型转化为uint32_t,其次ip地址的将主机序列转化为网络序> 列
47 local.sin_addr.s_addr=htons(INADDR_ANY);//不绑定指定ip,可以接收任何传达到指定端口号的ip主机发的数据
48 //2.2将sockaddr_in与套接字进行绑定
49 int n=bind(_sockfd,(struct sockaddr*)&local,sizeof(local));
50 if(n==-1)
51 {
52 cerr<<"bind err"<<errno<<" : "<<strerror(errno)<<endl;
53 exit(BIND_ERR);
54 }
55 }
56
57 void start()
58 {
59 for(;;)
60 {
61 char buffer[SIZE];//缓冲区
62 struct sockaddr_in out;
63 socklen_t len=sizeof(out);
64 ssize_t s=recvfrom(_sockfd,buffer,sizeof(buffer)-1,0,(sockaddr*)&out,&len);
65 if(s>0)
66 {
67 buffer[s]=0;
68 string clientip=inet_ntoa(out.sin_addr);//网络序列转换为主机序列;uin32t_t->string
69 uint16_t clientport=ntohs(out.sin_port);//网络序列转化为主机序列
70 string message=buffer;
71 cout<<clientip<<"["<<clientport<<"]# "<<message<<endl;
72 }
73 }
74 }
75
76 ~udpserver()
77 {}
78
79 private:
80 uint16_t _port;//端口号
81 string _ip;//ip地址
82 int _sockfd;//套接字文件描述符
83 };
84 }
根据server.hpp的47行可知:server端(服务端)不能绑定指定ip地址,绑定ip地址意味着只接收指定ip地址和端口号的主机发来的报文;而不绑定ip地址可以接收任意ip地址的主机只需绑定指定端口号即发送报文給服务端。因此在server.cc只需要传参端口号port
server.hpp中start启动函数内是个死循环,即服务器本质是一个死循环,这个进程被称为常驻内存进程sockaddr_in类型的结构的ip地址INADDR_ANY就是全0,即接收任意绑定了对应端口的进程发送来的数据。
server.hpp的42行的bzero函数用于将local结构体清零
bzero函数原型
#include
void bzero(void *s, size_t n);
将参数s 所指的内存区域前n 个字节全部设为零
recvfrom函数原型
#include
#include
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
函数原型
#include
char *inet_ntoa(struct in_addr in);
需要注意的是:
client.cc
#include
#include"client.hpp"
using namespace udpClient;
static void startrouine(string proc)
{
cout<<"\nstartroutine\n\t" <<proc<<"serverip serverport"<<endl;
}
int main(int argc,char* argv[])
{
if(argc!=3)
{
startrouine(argv[0]);
exit(1);
}
string serverip=argv[1];
uint16_t serverport=atoi(argv[2]);//atoi作用:把port的str类型转化成int类型。而uin16_t本质类型是int
unique_ptr<udpclient> uct(new udpclient(serverip,serverport));
uct->initclient();
uct->run();
return 0;
}
client.hpp
1 #pragma once
2 #include <iostream>
3 #include <string>
4 #include <strings.h>
5 #include <cerrno>
6 #include <cstring>
7 #include <cstdlib>
8 #include <unistd.h>
9 #include <sys/types.h>
10 #include <sys/socket.h>
11 #include <arpa/inet.h>
12 #include <netinet/in.h>
13 #include <pthread.h>
14 using namespace std;
15 namespace udpClient
16 {
17 class udpclient
18 {
19 public:
20 udpclient(const string& ip,const uint16_t port)
21 :_serverip(ip)
22 ,_serverport(port)
23 ,_sockfd(-1)
24 ,flag(false)
25 {}
26 void initclient()
27 {
28 //1.创建套接字
29 _sockfd=socket(AF_INET,SOCK_DGRAM,0);
30 if(_sockfd==-1)
31 {
32 cout<<"socket error"<<errno<<":"<<strerror(errno)<<endl;
33 exit(2);
34 }
35 cout<<"socket success: "<<_sockfd<<endl;
36 //2.绑定,但不用显示绑定,OS会自动绑定指定ip和端口
37 }
38 void run()
39 {
40 // pthread_create(&_pt,nullptr,readmessage,(void*)&_sockfd);
41
42 struct sockaddr_in server;
43 server.sin_family=AF_INET;
44 server.sin_addr.s_addr=inet_addr(_serverip.c_str());//主机序列转换为网络序列;string->uin32t_t
45 server.sin_port=htons(_serverport);//主机序列转换为网络序列
46
47 string message;
48 char cmdbuffer[1024];
49 while(!flag)
50 {
51 fprintf(stderr,"enter# ");
52 fflush(stderr);
53 fgets(cmdbuffer,sizeof(cmdbuffer),stdin);//键盘上的内容写入缓冲区cmdbuffer
54 cmdbuffer[strlen(cmdbuffer)-1]=0;
55 message=cmdbuffer;
56 sendto(_sockfd,message.c_str(),message.size(),0,(struct sockaddr*)&server,sizeof(server));
57 //将缓冲区的内容发送到套接字里通常用于UDP
58 }
59 }
60 ~udpclient(){}
61 private:
62 int _sockfd;
63 string _serverip;
64 uint16_t _serverport;
65 bool flag;
66 };
67 }
sendto函数原型
#include
#include
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
若绑定了IP地址,那么服务器只能接收改绑定的IP地址的数据,其他IP地址发送来的不能接收
INADDR_ANY:任意地址绑定,任何绑定了8080ip的地址都能发数据来并接收(全0)
socket函数原型
#include /* See NOTES */
#include
int socket(int domain, int type, int protocol);
domain:协议域,常见的协议组用AF_INET(ipv4) AF_INET6(ipv6) AF_LOCAL AF_ROUTE . 协议族决定了socket的地址类型,在通信中必须采用相应的地址
type为socket的类型,主要分为流格式套接字(SOCK_STREAM)即使用TCP协议和数据报格式套接字(SOCK_DGRAM)即使用UDP协议
默认输入0
bind
#include
int bind(int socket, const struct sockaddr *address, socklen_t address_len);
recvfrom
recvfrom函数是用于从一个已连接的套接字接收数据的函数。它的作用是从指定的套接字接收数据,并将接收到的数据保存到指定的缓冲区中。
#include
#include
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
inet_ntoa()将网络序列转化为主机序列,将整数转化为字符串类型
课里讲的主要是代码和接口
inet_addr
inet_addr是一个用于将点分十进制表示的IP地址转换成网络字节序的32位二进制IP地址的函数。该函数定义在C语言的头文件
中。一是将string类型转化为uint32_t类型,二是将主机序列转化为网络序列
#include
#include
#include
in_addr_t inet_addr(const char *cp)
sendto
用于通过指定的套接字向目标地址发送数据。该函数通常用于面向无连接的协议(如UDP)中发送数据报。
#include
#include
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
makefile
.PHONY:all
all:udpClient udpServer
udpClient:udpClient.cc
g++ -o $@ $^ -std=c++11 -pthread
udpServer:udpServer.cc
g++ -o $@ $^ -std=c++11 -pthread
.PHONY:clean
clean:
rm -rf udpClient udpServer
udpClient.cc
#include "udpClient.hpp"
#include
using namespace Client;
static void Usage(string proc)
{
cerr << "\nUsage:\n\t" << proc << " server_ip server_port\n\n";
}
// ./udpClient server_ip server_port
int main(int argc, char *argv[])
{
if(argc != 3)
{
Usage(argv[0]);
exit(1);
}
string serverip = argv[1];
uint16_t serverport = atoi(argv[2]);
unique_ptr<udpClient> ucli(new udpClient(serverip, serverport));
ucli->initClient();
ucli->run();
return 0;
}
udpClient.hpp
#pragma once
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
namespace Client
{
using namespace std;
class udpClient
{
public:
udpClient(const string &serverip, const uint16_t &serverport)
: _serverip(serverip), _serverport(serverport), _sockfd(-1), _quit(false)
{
}
void initClient()
{
// 创建socket
_sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (_sockfd == -1)
{
cerr << "socket error: " << errno << " : " << strerror(errno) << endl;
exit(2);
}
cout << "socket success: "
<< " : " << _sockfd << endl;
// 2. client要不要bind[必须要的],client要不要显示的bind,需不需程序员自己bind?不需要!!!
// 写服务器的是一家公司,写client是无数家公司 -- 由OS自动形成端口进行bind!-- OS在什么时候,如何bind
}
static void *readMessage(void *args)
{
int sockfd = *(static_cast<int *>(args));
pthread_detach(pthread_self());
while (true)
{
char buffer[1024];
struct sockaddr_in temp;
socklen_t temp_len = sizeof(temp);
size_t n = recvfrom(sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&temp, &temp_len);
if (n >= 0)
buffer[n] = 0;
cout << buffer << endl;
}
return nullptr;
}
void run()
{
pthread_create(&_reader, nullptr, readMessage, (void *)&_sockfd);
struct sockaddr_in server;
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_addr.s_addr = inet_addr(_serverip.c_str());
server.sin_port = htons(_serverport);
string message;
char cmdline[1024];
while (!_quit)
{
//cerr << "# "; // ls -a -l
// cin >> message;
fprintf(stderr, "Enter# ");
fflush(stderr);
fgets(cmdline, sizeof(cmdline), stdin);
cmdline[strlen(cmdline)-1] = 0;
message = cmdline;
sendto(_sockfd, message.c_str(), message.size(), 0, (struct sockaddr *)&server, sizeof(server));
}
}
~udpClient()
{
}
private:
int _sockfd;
string _serverip;
uint16_t _serverport;
bool _quit;
pthread_t _reader;
};
} // namespace Client
udpServer.hpp
#pragma once
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
namespace Server
{
using namespace std;
static const string defaultIp = "0.0.0.0"; //TODO
static const int gnum = 1024;
enum {USAGE_ERR = 1, SOCKET_ERR, BIND_ERR, OPEN_ERR};
typedef function<void (int,string,uint16_t,string)> func_t;
class udpServer
{
public:
udpServer(const func_t &cb, const uint16_t &port, const string &ip = defaultIp)
:_callback(cb), _port(port), _ip(ip), _sockfd(-1)
{}
void initServer()
{
// 1. 创建socket
_sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(_sockfd == -1)
{
cerr << "socket error: " << errno << " : " << strerror(errno) << endl;
exit(SOCKET_ERR);
}
cout << "socket success: " << " : " << _sockfd << endl;
// 2. 绑定port,ip(TODO)
// 未来服务器要明确的port,不能随意改变
struct 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. htonl(); -> inet_addr
//local.sin_addr.s_addr = htonl(INADDR_ANY); // 任意地址bind,服务器的真实写法
int n = bind(_sockfd, (struct sockaddr*)&local, sizeof(local));
if(n == -1)
{
cerr << "bind error: " << errno << " : " << strerror(errno) << endl;
exit(BIND_ERR);
}
// UDP Server 的预备工作完成
}
void start()
{
// 服务器的本质其实就是一个死循环
char buffer[gnum];
for(;;)
{
// 读取数据
struct sockaddr_in peer;
socklen_t len = sizeof(peer); //必填
ssize_t s = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&peer, &len);
// 1. 数据是什么 2. 谁发的?
if(s > 0)
{
buffer[s] = 0;
string clientip = inet_ntoa(peer.sin_addr); //1. 网络序列 2. int->点分十进制IP
uint16_t clientport = ntohs(peer.sin_port);
string message = buffer;
cout << clientip <<"[" << clientport << "]# " << message << endl;
// 对数据做处理
_callback(_sockfd, clientip, clientport, message);
}
}
}
~udpServer()
{
}
private:
uint16_t _port;
string _ip;
int _sockfd;
func_t _callback; //回调
};
}
udpServer.cc
#include "udpServer.hpp"
#include "onlineUser.hpp"
#include
#include
#include
#include
using namespace std;
using namespace Server;
const std::string dictTxt="./dict.txt";
unordered_map<string, string> dict;
static void Usage(string proc)
{
cout << "\nUsage:\n\t" << proc << " local_port\n\n";
}
static bool cutString(const string &target, string *s1, string *s2, const string &sep)
{
auto pos = target.find(sep);
if(pos == string::npos) return false;
*s1 = target.substr(0, pos);
*s2 = target.substr(pos + sep.size());
return true;
}
static void initDict()
{
ifstream in(dictTxt, std::ios::binary);
if(!in.is_open())
{
cerr << "open file " << dictTxt << " error" << endl;
exit(OPEN_ERR);
}
string line;
std::string key, value;
while(getline(in, line))
{
if(cutString(line, &key, &value, ":"))
{
dict.insert(make_pair(key, value));
}
}
in.close();
cout << "load dict success" << endl;
}
void reload(int signo)
{
(void)signo;
initDict();
}
// // demo1
void handlerMessage(int sockfd, string clientip, uint16_t clientport, string message)
{
// 就可以对message进行特定的业务处理,而不关心message怎么来的 ---- server通信和业务逻辑解耦!
// 婴儿版的业务逻辑
string response_message;
auto iter = dict.find(message);
if(iter == dict.end()) response_message = "unknown";
else response_message = iter->second;
// 开始返回
struct sockaddr_in client;
bzero(&client, sizeof(client));
client.sin_family = AF_INET;
client.sin_port = htons(clientport);
client.sin_addr.s_addr = inet_addr(clientip.c_str());
sendto(sockfd, response_message.c_str(), response_message.size(), 0, (struct sockaddr*)&client, sizeof(client));
}
// // demo2
void execCommand(int sockfd, string clientip, uint16_t clientport, string cmd)
{
//1. cmd解析,ls -a -l
//2. 如果必要,可能需要fork, exec*
if(cmd.find("rm") != string::npos || cmd.find("mv") != string::npos || cmd.find("rmdir") != string::npos)
{
cerr << clientip << ":" << clientport << " 正在做一个非法的操作: " << cmd << endl;
return;
}
string response;
FILE *fp = popen(cmd.c_str(), "r");
if(fp == nullptr) response = cmd + " exec failed";
char line[1024];
while(fgets(line, sizeof(line), fp))
{
response += line;
}
pclose(fp);
// 开始返回
struct sockaddr_in client;
bzero(&client, sizeof(client));
client.sin_family = AF_INET;
client.sin_port = htons(clientport);
client.sin_addr.s_addr = inet_addr(clientip.c_str());
sendto(sockfd, response.c_str(), response.size(), 0, (struct sockaddr*)&client, sizeof(client));
}
OnlineUser onlineuser;
// demo3
void routeMessage(int sockfd, string clientip, uint16_t clientport, string message)
{
if (message == "online") onlineuser.addUser(clientip, clientport);
if (message == "offline") onlineuser.delUser(clientip, clientport);
if (onlineuser.isOnline(clientip, clientport))
{
// 消息的路由
onlineuser.broadcastMessage(sockfd, clientip, clientport, message);
}
else
{
struct sockaddr_in client;
bzero(&client, sizeof(client));
client.sin_family = AF_INET;
client.sin_port = htons(clientport);
client.sin_addr.s_addr = inet_addr(clientip.c_str());
string response = "你还没有上线,请先上线,运行: online";
sendto(sockfd, response.c_str(), response.size(), 0, (struct sockaddr *)&client, sizeof(client));
}
}
// ./udpServer port
int main(int argc, char *argv[])
{
if (argc != 2)
{
Usage(argv[0]);
exit(USAGE_ERR);
}
uint16_t port = atoi(argv[1]);
// string ip = argv[1];
signal(2, reload);
// initDict();
// std::unique_ptr usvr(new udpServer(handlerMessage, port));
// std::unique_ptr usvr(new udpServer(execCommand, port));
std::unique_ptr<udpServer> usvr(new udpServer(routeMessage, port));
usvr->initServer();
usvr->start();
return 0;
}
demo1:实现一个英译中服务
static bool cutString(const string &target, string *s1, string *s2, const string &sep)
{
auto pos = target.find(sep);
if(pos == string::npos) return false;
*s1 = target.substr(0, pos);
*s2 = target.substr(pos + sep.size());
return true;
}
static void initDict()
{
ifstream in(dictTxt, std::ios::binary);
if(!in.is_open())
{
cerr << "open file " << dictTxt << " error" << endl;
exit(OPEN_ERR);
}
string line;
std::string key, value;
while(getline(in, line))
{
if(cutString(line, &key, &value, ":"))
{
dict.insert(make_pair(key, value));
}
}
in.close();
cout << "load dict success" << endl;
}
void reload(int signo)
{
(void)signo;
initDict();
}
// // demo1
void handlerMessage(int sockfd, string clientip, uint16_t clientport, string message)
{
// 就可以对message进行特定的业务处理,而不关心message怎么来的 ---- server通信和业务逻辑解耦!
// 婴儿版的业务逻辑
string response_message;
auto iter = dict.find(message);
if(iter == dict.end()) response_message = "unknown";
else response_message = iter->second;
// 开始返回
struct sockaddr_in client;
bzero(&client, sizeof(client));
client.sin_family = AF_INET;
client.sin_port = htons(clientport);
client.sin_addr.s_addr = inet_addr(clientip.c_str());
sendto(sockfd, response_message.c_str(), response_message.size(), 0, (struct sockaddr*)&client, sizeof(client));
}
// ./udpServer port
int main(int argc, char *argv[])
{
if (argc != 2)
{
Usage(argv[0]);
exit(USAGE_ERR);
}
uint16_t port = atoi(argv[1]);
// string ip = argv[1];
signal(2, reload);
// initDict();
std::unique_ptr<udpServer> usvr(new udpServer(handlerMessage, port));
usvr->initServer();
usvr->start();
return 0;
}
dict.txt文件中数据对应关系
apple | 苹果 |
---|---|
banana | 香蕉 |
hello | 你好 |
goodman | 好人 |
demo2:将客户端发送来的代码当作命令行在服务器上做解析,即客户端输入命令对服务器进行命令行操作,操作后的服务器命令行解析器的结果返回給客户端
void execCommand(int sockfd, string clientip, uint16_t clientport, string cmd)
{
//1. cmd解析,ls -a -l
//2. 如果必要,可能需要fork, exec*
if(cmd.find("rm") != string::npos || cmd.find("mv") != string::npos || cmd.find("rmdir") != string::npos)
{
cerr << clientip << ":" << clientport << " 正在做一个非法的操作: " << cmd << endl;
return;
}
string response;
FILE *fp = popen(cmd.c_str(), "r");
if(fp == nullptr) response = cmd + " exec failed";
char line[1024];
while(fgets(line, sizeof(line), fp))
{
response += line;
}
pclose(fp);
// 开始返回
struct sockaddr_in client;
bzero(&client, sizeof(client));
client.sin_family = AF_INET;
client.sin_port = htons(clientport);
client.sin_addr.s_addr = inet_addr(clientip.c_str());
sendto(sockfd, response.c_str(), response.size(), 0, (struct sockaddr*)&client, sizeof(client));
}
// ./udpServer port
int main(int argc, char *argv[])
{
if (argc != 2)
{
Usage(argv[0]);
exit(USAGE_ERR);
}
uint16_t port = atoi(argv[1]);
std::unique_ptr<udpServer> usvr(new udpServer(execCommand, port));
usvr->initServer();
usvr->start();
return 0;
}
popen函数用于在一个子进程中执行一个 shell 命令,并建立一个与该子进程之间的管道,以便可以通过管道进行输入输出操作。
#include
FILE *popen(const char *command, const char *type);
"r"
(读取模式)或 "w"
(写入模式)popen
函数将返回一个文件流指针(FILE *
),您可以使用该指针进行读取或写入操作,具体取决于您指定的管道类型。当不再需要时,应使用 pclose
函数来关闭子进程并释放资源demo3:在客户端输入online上线,在服务器接收客户端发送来消息,当客户端发送online給服务器时,客户端才算上线成功,那么服务器才会将客户端发送来的信息返回給客户端
OnlineUser.hpp
#pragma once
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
class User
{
public:
User(const string &ip, const uint16_t &port) : _ip(ip), _port(port)
{
}
~User()
{
}
string ip(){ return _ip; }
uint16_t port(){ return _port; }
private:
string _ip;
uint16_t _port;
};
class OnlineUser
{
public:
OnlineUser() {}
~OnlineUser() {}
void addUser(const string &ip, const uint16_t &port)
{
string id = ip + "-" + to_string(port);
users.insert(make_pair(id, User(ip, port)));
}
void delUser(const string &ip, const uint16_t &port)
{
string id = ip + "-" + to_string(port);
users.erase(id);
}
bool isOnline(const string &ip, const uint16_t &port)
{
string id = ip + "-" + to_string(port);
return users.find(id) == users.end() ? false : true;
}
void broadcastMessage(int sockfd, const string &ip, const uint16_t &port, const string &message)
{
for (auto &user : users)
{
struct sockaddr_in client;
bzero(&client, sizeof(client));
client.sin_family = AF_INET;
client.sin_port = htons(user.second.port());
client.sin_addr.s_addr = inet_addr(user.second.ip().c_str());
string s = ip + "-" + to_string(port) + "# ";
s += message;
sendto(sockfd, s.c_str(), s.size(), 0, (struct sockaddr *)&client, sizeof(client));
}
}
private:
unordered_map<string, User> users;
};
OnlineUser onlineuser;
// demo3
void routeMessage(int sockfd, string clientip, uint16_t clientport, string message)
{
if (message == "online") onlineuser.addUser(clientip, clientport);
if (message == "offline") onlineuser.delUser(clientip, clientport);
if (onlineuser.isOnline(clientip, clientport))
{
// 消息的路由
onlineuser.broadcastMessage(sockfd, clientip, clientport, message);
}
else
{
struct sockaddr_in client;
bzero(&client, sizeof(client));
client.sin_family = AF_INET;
client.sin_port = htons(clientport);
client.sin_addr.s_addr = inet_addr(clientip.c_str());
string response = "你还没有上线,请先上线,运行: online";
sendto(sockfd, response.c_str(), response.size(), 0, (struct sockaddr *)&client, sizeof(client));
}
}
// ./udpServer port
int main(int argc, char *argv[])
{
if (argc != 2)
{
Usage(argv[0]);
exit(USAGE_ERR);
}
uint16_t port = atoi(argv[1]);
std::unique_ptr<udpServer> usvr(new udpServer(routeMessage, port));
usvr->initServer();
usvr->start();
return 0;
}