Directorio de artículos
función de traducción
El servidor UDP que escribimos no solo terminó de recibir datos, sino que también necesita realizar tareas de procesamiento .
Podemos configurar una función de devolución de llamada _callback en el servidor udpServer.hpp, y la lógica específica se pasa desde el exterior a través de udpServer.cc
El código se ve así:
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);
if(s > 0)
{
buffer[s] = 0;
string clientip = inet_ntoa(peer.sin_addr);
uint16_t clientport = ntohs(peer.sin_port);
string message = buffer;
cout << clientip <<"[" << clientport << "]# " << message << endl;//把收到的消息打印出来
_callback(_sockfd, clientip, clientport, message);
}
}
}
Función de traducción: el cliente ingresa una palabra, luego la envía al servidor y luego recibe el resultado traducido del servidor.
Primero proporcione un dictado de diccionario: cargue el archivo dicTxt correspondiente a inglés y chino en nuestro unordered_map
diccionario, y unordered_map en este momento guarda el contenido del diccionario :
dict.txt: los archivos externos pueden ser completados y complementados por usted mismo, aquí hay solo un código de prueba de muestra
apple:苹果
world:世界
hello:你好
goodman:你是一个好人
const std::string dictTxt="./dict.txt";//文件
unordered_map<string, string> dict;//字典
A continuación, inicialice nuestro diccionario: abra el archivo, divida cada línea del archivo en clave y valor, es decir, inglés y chino, y luego inserte el resultado en el dictado, es decir, coloque el resultado en el dictado, el código es como sigue:
static bool cutString(const string &target, string *s1, string *s2, const string &sep)
{
//apple:苹果
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;
}
Procese los datos a través de la función handlerMessage en udpServer.cc y luego envíe el resultado del procesamiento al cliente:
void handlerMessage(int sockfd, string clientip, uint16_t clientport, string message)
{
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));
}
El cliente udpClient.hpp ingresa el mensaje cin y envía sendto al servidor, y luego el servidor udpServer.hpp llama a la función de devolución de llamada para traducir el mensaje. Una vez completada la traducción, el resultado final se envía al cliente en sendto, y el cliente udpClient.hpp recibe el resultado después de recibir la traducción y finalmente imprime el resultado de la traducción:
void run()
{
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)
{
cout<<"Please Enter#";
cin>>message;
sendto(_sockfd,message.c_str(),message.size(),0,(struct sockaddr*)&server,sizeof(server));
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;
}
}
A continuación, los resultados de la prueba:
//udpServer.cc
int main(int argc, char *argv[])
{
if (argc != 2)
{
Usage(argv[0]);
exit(USAGE_ERR);
}
uint16_t port = atoi(argv[1]);
initDict();
std::unique_ptr<udpServer> usvr(new udpServer(handlerMessage, port));
}
//udpClient.cc
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;
}
Inicie el servidor primero:
Ingrese información en el cliente de inicio, envíe un mensaje al servidor, el servidor recibe el mensaje y lo imprime, y devuelve el resultado traducido al cliente, y el cliente imprime el resultado traducido, que es todo el proceso mencionado anteriormente:
análisis de línea de comando
Prestar interfaz popen: (función equivalente a pipe+fork, exec*)
#include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
command
: La cadena pasada, como ls -a -l
; type
: Cómo abrir el archivo (r/w/a), podemos llamarlo a través de una función execCommand, solo necesitamos modificar la función entrante en el archivo udpServer.cc para lograr esta función:
void execCommand(int sockfd, string clientip, uint16_t clientport, string cmd)
{
//1. cmd解析,ls -a -l
//先禁止一下非法操作,防止有人搞破坏
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));
}
Prueba de funcionamiento:
//udpServer.cc
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));
}
sala de chat en internet
Necesitamos administrar usuarios, y para cada usuario usamos IP y puerto para identificar la unicidad, por lo que podemos usar una tabla hash para una administración unificada:
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;
};
En la función de devolución de llamada, si el mensaje recibido está en línea, agregue el usuario a la tabla hash. Si está fuera de línea, elimínelo de la tabla hash
if (message == "online") onlineuser.addUser(clientip, clientport);
if (message == "offline") onlineuser.delUser(clientip, clientport);
OnlineUser onlineuser;
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));
}
}
Subprocesos múltiples: El cliente udpClient.hpp no puede recibir el mensaje de inmediato e imprimirlo.Para resolver este problema, podemos usar subprocesos múltiples, un subproceso se dedica a recibir mensajes y un subproceso se dedica a enviar mensajes: deje que el subproceso principal sea responsable de enviar mensajes, y los subprocesos sean responsables de recibir mensajes:
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)
{
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));
}
}
Windows y Linux de UDP
La implementación de UDP puede interactuar en diferentes plataformas, aquí usamos Linux como servidor y Windows como cliente para conectarnos.
Código del lado de Windows:
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)
#include <iostream>
#include <string>
#include <cstring>
#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
uint16_t serverport = 8080;
string serverip = "8.134.152.121";
int main()
{
WSAData wsd;
if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
{
cout << "WSAStartup Error = " << WSAGetLastError() << endl;
return 0;
}
else
{
cout << "WSAStartup Success" << endl;
}
SOCKET csock = socket(AF_INET, SOCK_DGRAM, 0);
if (csock == SOCKET_ERROR)
{
cout << "socket Error = " << WSAGetLastError() << endl;
return 1;
}
else
{
cout << "socket success" << endl;
}
struct sockaddr_in server;
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(serverport);
server.sin_addr.s_addr = inet_addr(serverip.c_str());
#define NUM 1024
char inbuffer[NUM];
string line;
while (true)
{
cout << "Please Enter#";
getline(cin, line);
int n = sendto(csock, line.c_str(), line.size(), 0, (struct sockaddr*)&server, sizeof(server));
if (n < 0)
{
cerr << "sendto error!" << endl;
break;
}
struct sockaddr_in peer;
int peerlen = sizeof(peer);
inbuffer[0] = 0;
n = recvfrom(csock, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr*)&peer, &peerlen);
if (n > 0)
{
inbuffer[n] = 0;
cout << "server 返回的消息是#" << inbuffer << endl;
}
else break;
}
closesocket(csock);
WSACleanup();
return 0;
}
código Linux:
pragma once
#include <iostream>
#include <string>
#include <strings.h>
#include <cerrno>
#include <cstring>
#include <functional>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
namespace Server
{
using namespace std;
static const string defaultIp = "0.0.0.0";
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()
{
_sockfd = socket(AF_INET,SOCK_DGRAM,0);
if(_sockfd == -1)
{
cerr<<"sdocket error: "<<errno<<" : "<<strerror(errno)<<endl;
exit(SOCKET_ERR);
}
cout<<"socket success: "<<" : "<<_sockfd<<endl;
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());
int n = bind(_sockfd,(struct sockaddr*)&local,sizeof(local));
if(n == -1)
{
cerr<<"bind errpr: "<<errno<<" : "<<strerror(errno)<<endl;
exit(BIND_ERR);
}
}
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);
if(s>0)
{
buffer[s] = 0;
string clientip = inet_ntoa(peer.sin_addr);
uint16_t clientport = ntohs(peer.sin_port);
string message = buffer;
cout<<clientip<<"["<<clientport<<"]#"<< message<<endl;
_callback(_sockfd,clientip,clientport,message);
}
}
}
~udpServer()
{
}
private:
int _sockfd;
uint16_t _port;
string _ip;
func_t _callback;
};
}
//udpServer.cc
#include <iostream>
#include <memory>
#include "udpServer.hpp"
using namespace std;
using namespace Server;
static void Usage(string proc)
{
cout<<"\nUsage:\n\t"<<proc<<" locla_port\n\n";
}
void handlerMessage(int sockfd,string clientip,uint16_t clientport,string message)
{
string response_message = message;
response_message+=" [server echo]";
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));
}
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(handlerMessage,port));
usvr->initServer();
usvr->start();
return 0;
}