Biblioteca de rede para linux c ++
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <string.h>
#include <unistd.h>
#include <string>
class Website
{
int status, sock;
struct addrinfo hints;
struct addrinfo *servinfo;
public:
int init(std::string url){
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if((status = getaddrinfo(url.c_str(), "80", &hints, &servinfo)) != 0){
return status;
}
if((sock = socket(servinfo->ai_family, servinfo->ai_socktype, servinfo->ai_protocol)) == -1){
return -1;
}
return 0;
}
int connectToSite(){
return connect(sock, servinfo->ai_addr, servinfo->ai_addrlen);
}
int sendToSite(std::string request){
return send(sock, request.c_str(), strlen(request.c_str()), 0);
}
int recvFromSite(char buf[], int maxsize){
return recv(sock, buf, maxsize, 0);
}
void closeSocket(){
close(sock);
freeaddrinfo(servinfo);
}
};
Eu tenho uma classe de rede que implementa funções c de baixo nível em uma classe simples. Estou procurando tornar meu código mais eficiente
Respostas
Observação:
Não gosto do design.
Você está usando uma inicialização de duas fases. Você constrói o objeto (portanto, o construtor) e, a seguir, chama uma init()função. A inicialização em duas fases é uma má ideia, pois você não pode dizer (ou tem que validar) o estado do objeto antes que ele possa ser usado.
Uma técnica melhor é simplesmente usar o construtor para inicializar o objeto. Se o construtor falhar, o lançamento para que o objeto nunca exista e não possa ser usado incorretamente.
Você também tem um close ( closeSocket()) explícito, o que é bom até certo ponto, mas você está forçando as pessoas a se lembrarem de chamar esta função quando terminar de usar o objeto. É para isso que serve um destruidor.
Você precisa pesquisar o conceito de RAII.
{
Website site("Stuff"); // All resources allocated
}
// All resources for `site` are now released.
Projeto
Qual é a utilidade de connectToSite(), não é o init? Você pode ligar para leitura / gravação antes de ligar para isso? Caso contrário, a leitura / gravação está no objeto errado e a conexão deve retornar o objeto apropriado que possui a interface de leitura / gravação.
É assim que eu gostaria de usar um objeto chamado "WebSite"
{
// An abstract object that represents the site.
// No actual underlying connection as connections are
// usually to resources on the site not the actual site.
Website site("thorsanvil.com", 80);
// Create a connection to a specific resource on the site.
Connection c = site.open("Path");
// Object was created successfully so we have
// a valid connection to a server we can not communicate with.
c.writeData("This is a message");
std::string resp = c.read();
}
De sua interface. Gostaria de renomear sua classe para "Conexão", então as expectativas de como usar o objeto podem ser mais semelhantes.
{
// Create the socket and connect to the site.
Connection c("https://thorsanvil.com/MyResource");
// Object was created successfully so we have
// a valid connection to a server we can not communicate with.
c.writeData("This is a message");
std::string response = c.readData();
}
Revisão de código:
Você não quer usar strings C ++?
#include <string.h>
Um URL?
int init(std::string url)
Claro, mas o nome da classe é "Site". Um url é muito mais do que apenas um website. Há um esquema / host (nome do host / porta / usuário / senha) / caminho / consulta / fragmento. Você não passaria apenas um host e para a conexão talvez uma porta.
Ou você pode passar o url inteiro, mas cada nova conexão precisa de um novo URL, não apenas do site.
Você está retornando o código de status aqui (em caso de erro):
if((status = getaddrinfo(url.c_str(), "80", &hints, &servinfo)) != 0){
return status;
}
Mas aqui está você reajustando -1
if((sock = socket(servinfo->ai_family, servinfo->ai_socktype, servinfo->ai_protocol)) == -1){
return -1;
}
Tem certeza de que esses intervalos de erro não se sobrepõem?
Acho que uma interface melhor seria lançar uma exceção se algo der errado.
Isso não vai funcionar para o URL genérico.
status = getaddrinfo(url.c_str(), "80" ....
- A porta nem sempre é 80.
- A porta padrão é definida pelo esquema "http / https ..."
- O padrão pode ser substituído pelo próprio url.
Exemplo:
https://thorsanvil.com:1222/Work
Schema: https
Site: thorsanvil.com
Port: 1222 overrides default 443 for https
Path: /Work
Query: Empty
Fragment: Empty
Na verdade, o código acima deve ser:
status = getaddrinfo(getHost(url), getPort(url), ....
O que acontece com connect()falha? Você espera que o usuário verifique o código de erro?
int connectToSite(){
return connect(sock, servinfo->ai_addr, servinfo->ai_addrlen);
}
É uma interface muito semelhante a C e muito sujeita a erros. Sabemos que os usuários de API nem sempre verificam os códigos de erro. Não vaze esta ingormação. Torne-o explícito com uma exceção.
Não é assim que você usa send()e recv()o valor de retorno indica quanto da mensagem foi enviada / recebida. Você precisa chamar essas funções repetidamente até que a mensagem seja enviada ou até que ocorra um erro.
int sendToSite(std::string request){
return send(sock, request.c_str(), strlen(request.c_str()), 0);
}
int recvFromSite(char buf[], int maxsize){
return recv(sock, buf, maxsize, 0);
}
Pesquisa
Dê uma olhada nesta aula:
https://github.com/Loki-Astari/ThorsSocket/blob/master/src/ThorsSocket/Socket.h