Related
I'm implementing a server in C++ with non-blocking sockets.
Since I want to send messages between the client & server, I wrote 2 wrappers around send/recv syscalls. Mainly, I want to prepend 4Bytes (message length) to every message, so that the receiver knows how long to execute recv.
Moreover I have a client/server programs that each start a socket and listen on localhost.
Then the client sends a random message, which the server receives.
When I try,however, to send from the server to the client, both programs halt.
I have tested the wrappers many times and they read/deliver data, but whenever I try to receive on a previously sending connection, then comes the problem.
I know that there is a memory leak in the secure_recv but I need it to pass some custom tests, which are not very well written.
The issue lies in the select, which returns a positive number, but then I never go inside the if (FD_ISSET(fd, &readset)) statement.
What am I doing wrong and how can we fix it ? Thanks a lot !
EDIT
My problem was that the sockets were blocking(busy working) at the select function. I updated the code so that there is no select in the secure_* functions. It's a much better way to first check if the socket is available for send/recv on a client/server thread level via select and then calling the secure_* functions. Question is answered for now.
client.cpp
// Client side C/C++ program to demonstrate Socket programming
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include "util.h"
#define PORT 8080
int main(int argc, char const *argv[])
{
int sock = 0, valread;
struct sockaddr_in serv_addr;
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("\n Socket creation error \n");
return -1;
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
// Convert IPv4 and IPv6 addresses from text to binary form
if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0)
{
printf("\nInvalid address/ Address not supported \n");
return -1;
}
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
{
printf("\nConnection Failed \n");
return -1;
}
int numbytes;
size_t size = 0;
std::unique_ptr<char[]> buf = get_rand_data(size);
if ((numbytes = secure_send(sock, buf.get(), size, 0)) == -1)
{
std::cout << std::strerror(errno) << "\n";
exit(1);
}
std::cout << "Client sent : " << numbytes << "\n";
int64_t bytecount = -1;
while (1)
{
std::unique_ptr<char[]> buffer;
if ((bytecount = secure_recv(sock, buffer, 0)) <= 0)
{
if (bytecount == 0)
{
break;
}
}
std::cout << bytecount << "\n";
}
std::cout << "Client received : " << bytecount << "\n";
close(sock);
return 0;
}
server.cpp
// Server side C/C++ program to demonstrate Socket programming
#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <string.h>
#include "util.h"
#define PORT 8080
int main(int argc, char const *argv[])
{
int server_fd, new_socket, valread;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
// Creating socket file descriptor
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0)
{
perror("socket failed");
exit(EXIT_FAILURE);
}
// Forcefully attaching socket to the port 8080
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT,
&opt, sizeof(opt)))
{
perror("setsockopt");
exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons( PORT );
// Forcefully attaching socket to the port 8080
if (bind(server_fd, (struct sockaddr *)&address,
sizeof(address))<0)
{
perror("bind failed");
exit(EXIT_FAILURE);
}
if (listen(server_fd, 3) < 0)
{
perror("listen");
exit(EXIT_FAILURE);
}
if ((new_socket = accept(server_fd, (struct sockaddr *)&address,
(socklen_t*)&addrlen))<0)
{
perror("accept");
exit(EXIT_FAILURE);
}
// set the socket to non-blocking mode
fcntl(new_socket, F_SETFL, O_NONBLOCK);
int64_t bytecount = -1;
while (1) {
std::unique_ptr<char[]> buffer;
if ((bytecount = secure_recv(new_socket, buffer, 0)) <= 0) {
if (bytecount == 0) {
break;
}
}
std::cout << bytecount << "\n";
}
int numbytes;
size_t size = 0;
std::unique_ptr<char[]> buf = get_rand_data(size);
if ((numbytes = secure_send(new_socket, buf.get(), size, 0)) == -1)
{
std::cout << std::strerror(errno) << "\n";
exit(1);
}
std::cout << "Client sent : " << numbytes << "\n";
close(new_socket);
return 0;
}
util.h
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <signal.h>
#include <cstring>
#include <iostream>
#include <thread>
#include <vector>
#include <mutex>
#include <condition_variable>
#include <algorithm>
#include <memory>
#include <poll.h>
#include <iomanip>
/**
* It takes as arguments one char[] array of 4 or bigger size and an integer.
* It converts the integer into a byte array.
*/
void convertIntToByteArray(char *dst, int sz)
{
auto tmp = dst;
tmp[0] = (sz >> 24) & 0xFF;
tmp[1] = (sz >> 16) & 0xFF;
tmp[2] = (sz >> 8) & 0xFF;
tmp[3] = sz & 0xFF;
}
/**
* It takes as an argument a ptr to an array of size 4 or bigger and
* converts the char array into an integer.
*/
int convertByteArrayToInt(char *b)
{
return (b[0] << 24) + ((b[1] & 0xFF) << 16) + ((b[2] & 0xFF) << 8) + (b[3] & 0xFF);
}
/**
* It constructs the message to be sent.
* It takes as arguments a destination char ptr, the payload (data to be sent)
* and the payload size.
* It returns the expected message format at dst ptr;
*
* |<---msg size (4 bytes)--->|<---payload (msg size bytes)--->|
*
*
*/
void construct_message(char *dst, char *payload, size_t payload_size)
{
convertIntToByteArray(dst, payload_size);
memcpy(dst + 4, payload, payload_size);
}
/**
* It returns the actual size of msg.
* Not that msg might not contain all payload data.
* The function expects at least that the msg contains the first 4 bytes that
* indicate the actual size of the payload.
*/
int get_payload_size(char *msg, size_t bytes)
{
// TODO:
return convertByteArrayToInt(msg);
}
/**
* Sends to the connection defined by the fd, a message with a payload (data) of size len bytes.
* The fd should be non-blocking socket.
*/
/**
* Receives a message from the fd (non-blocking) and stores it in buf.
*/
int secure_recv(int fd, std::unique_ptr<char[]> &buf)
{
// TODO:
int valread = 0;
int len = 0;
int _len = 4;
bool once_received = false;
std::vector<char> ptr(4);
while (_len > 0)
{
int _valread = recv(fd, ptr.data() + valread, _len, MSG_DONTWAIT);
if (_valread == 0)
{
break;
}
if (_valread > 0)
{
_len -= _valread;
valread += _valread;
}
if (!once_received && valread == 4)
{
once_received = true;
len = convertByteArrayToInt(ptr.data());
_len = len;
ptr = std::vector<char>(len);
valread = 0;
}
}
buf = std::make_unique<char[]>(len);
memcpy(buf.get(), ptr.data(), len);
return len;
}
/**
* Sends to the connection defined by the fd, a message with a payload (data) of size len bytes.
* The fd should be non-blocking socket.
*/
int secure_send(int fd, char *data, size_t len)
{
// TODO:
char ptr[len + 4];
int valsent = 0;
int _len = 4;
bool once_sent = false;
construct_message(ptr, data, len);
while (_len > 0)
{
int _valsent = send(fd, ptr + valsent, _len, MSG_DONTWAIT);
if (_valsent == 0)
{
break;
}
if (_valsent > 0)
{
_len -= _valsent;
valsent += _valsent;
}
if (!once_sent && valsent == 4)
{
once_sent = true;
_len = len;
}
}
return len;
}
Compilation via
g++ -O3 -std=c++17 -Wall -g -I../ client.cpp -o client -lpthread
Let's start with the write loop:
while (1)
{
// std::cerr << "first iteration send\n";
FD_ZERO(&writeset);
FD_SET(fd, &writeset);
if (select(fd + 1, NULL, &writeset, NULL, NULL) > 0)
{
if (FD_ISSET(fd, &writeset))
{
valsent = send(fd, ptr + valsent, _len, 0);
Oops. This loses valsent, which tracks how many bytes you've sent so far. So on your third loop, ptr + valsent will only add the number of bytes received the second time. You need to track the total number of bytes sent so far somewhere.
if (valsent <= 0)
{
break;
}
_len -= valsent;
And what if _len becomes zero? You'll still call select and even send. You probably want that while (1) to be while (_len > 0).
}
}
}
return len;
Now, onto the read loop:
if (select(fd + 1, &readset, NULL, NULL, NULL) > 0)
{
if (FD_ISSET(fd, &readset))
{
if (first_iteration)
{
recv(fd, ptr, 4, 0);
You ignore the return value of recv here. What if it's not 4?
len = convertByteArrayToInt(ptr);
buf = std::make_unique<char[]>(len);
_len = len;
first_iteration = false;
}
valread = recv(fd, buf.get() + valread, _len, 0);
if (valread <= 0)
{
break;
}
_len -= valread;
You don't leave the loop if _len is zero. You'll call select again, waiting for data that may never come.
}
}
I am creating my own proxy server. Howerver, i don't know how to listen to browser. Moreover, i have some questions:
Do i need to listen to request from browser?
Do i have to display the received content on browser or just print all the html tags in console screen?
I think as every proxy server works, my program would get request from the client (browser), forward it to Web Server then receive the content from server, finally forward to the client.
#pragma comment(lib, "Ws2_32.lib")
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <string.h>
#include <sys/types.h>
#include <iostream>
#include <fcntl.h>
#include <WS2tcpip.h>
#include <sys/stat.h>
#include <io.h>
#include <process.h> /* for getpid() and the exec..() family */
#include <direct.h>
using namespace std;
bool checkSubString(char *str, char*substr)
{
if (strstr(str, substr) != NULL)
{
return true;
}
return false;
}
int main()
{
WSADATA wsData;
WORD ver = MAKEWORD(2, 2);
int wsOK = WSAStartup(ver, &wsData);
if (wsOK != 0)
{
cerr << "cant init winsock" << endl; return 0;
}
char message[1024];
int sockcheck = 0;
sockaddr_in server_input_addr;
memset(&server_input_addr, '0', sizeof(server_input_addr));
memset(&message, '0', sizeof(message));
sockcheck = socket(AF_INET, SOCK_STREAM, 0);
if (sockcheck < 0)
{
cerr << "Error while creating socket!!!\n";
return 0;
}
server_input_addr.sin_family = AF_INET;
server_input_addr.sin_addr.S_un.S_addr = INADDR_ANY;
server_input_addr.sin_port = htons(8888);
bind(sockcheck, (sockaddr*)&server_input_addr, sizeof(server_input_addr));
listen(sockcheck, 5);
int connFd = 0;
int n = 0;
int client_length = sizeof(server_input_addr);
while (1) {
connFd = accept(sockcheck, (sockaddr*)&server_input_addr, &client_length);
if (connFd < 0)
{
cerr << "\nError in accepting message from browser"; return 0;
}
n = _read(connFd, message, 1023);
if (n > 0)
{
cerr << "ERROR reading from socket\n";
}
_write(connFd, "Message received", 15);
_close(connFd);
}
cout << "\nSuccess!!!\n";
}
Right now I try to understand the forking/rebinding of stdin/out/err of child processes and to manage the resources (filehandles, sockets) rightly without leaking any resources.
There are some questions left:
After I create a socketpair and fork, I have in the parent 5 filedescriptors and in the child (stdin/out/err/socket1/socket2). In the child process, I need to close the "parent" side of the socketpair. I close() stdin/out/err after the fork and dup() the "client end" of the socket three times. After the dup(), do I need to close the "source" of the dup? I guess yes ... but am I right?
When I create in this way (see below) a second child, is the resource handling right? I tried to rely heavily on RAII to not leak any fds, but is it right? Do I miss a big thing?
Bye and thanks in advance!
Georg
EDIT: I fixed an error in rebind_and_exec_child.
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <memory>
#include <cassert>
// this handle takes a fd, behaves like an int and makes sure the fd is closed again
class fdhandle {
public:
explicit fdhandle(int fd) {
mp_fd = std::shared_ptr<int>(new int, [=](int* pfd) {
close(*pfd);
delete pfd;
});
assert(mp_fd);
*mp_fd = fd;
}
operator int() {
assert(mp_fd);
return *mp_fd;
}
private:
std::shared_ptr<int> mp_fd;
};
void rebind_and_exec_child(fdhandle fd, std::string exe) {
// now close the std fds and connect them to the given fd
close(0); close(1); close(2);
// dup the fd three times and recreate stdin/stdout/stderr with fd as the target
if (dup(fd) != 0 || dup(fd) != 1 || dup(fd) != 2) {
perror("error duplicating socket for stdin/stdout/stderr");
exit(EXIT_FAILURE);
}
// now we can exec the new sub process and talk to it through
// stdin/stdout/stderr
char *arguments[4] = { exe.c_str(), exe.c_str(), "/usr/bin", NULL };
execv(exe.c_str(), arguments);
// this could should never be reached
perror("error: executing the binary");
exit(EXIT_FAILURE);
}
fdhandle fork_connected_child(std::string exe) {
// create the socketpair
int fd[2];
if (-1 == socketpair(PF_LOCAL, SOCK_STREAM, 0, fd)) {
perror("error, could not create socket pair");
exit(EXIT_FAILURE);
}
fdhandle fdparent(fd[0]); fdhandle fdchild(fd[1]);
// now create the child
pid_t pid = fork();
switch (pid) {
case -1: // could not fork
perror("error forking the child");
exit(EXIT_FAILURE);
break;
case 0: // child
rebind_and_exec_child(fdchild);
break;
default: // parent
return fdparent;
break;
}
}
int main(int argc, const char** argv) {
// create 2 childs
fdhandle fdparent1 = fork_connected_child("/bin/ls");
fdhandle fdparent2 = fork_connected_child("/bin/ls");
}
I guess, I found the solution. For each created socket on the socketpair() call, I set FD_CLOEXEC. This way, I can be sure that the kernel closes all file descriptors. All other sockets which are handled by my code, will be closed by the fdhandle class call to close(). The rebinding of the stdin/stdout/stderr, I replaced the dup() for dup2() because it does close and dup atomicly.
The hint was this page:
http://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html
File descriptors open in the calling process image shall remain open
in the new process image, except for those whose close-on-exec flag
FD_CLOEXEC is set. For those file descriptors that remain open, all
attributes of the open file description remain unchanged. For any file
descriptor that is closed for this reason, file locks are removed as a
result of the close as described in close(). Locks that are not removed
by closing of file descriptors remain unchanged.
This is now my adjusted code:
EDIT: Adjusted structure
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <memory>
#include <cassert>
#include <iostream>
// this handle takes a fd, behaves like an int and makes sure the fd is closed again
class fdhandle {
public:
fdhandle() {}
explicit fdhandle(int fd) {
mp_fd = std::shared_ptr<int>(new int, [=](int* pfd) {
close(*pfd);
delete pfd;
});
assert(mp_fd);
*mp_fd = fd;
// set FD_CLOEXEC on fd
int flags;
flags = fcntl(fd, F_GETFD);
if (-1 == flags) {
perror("error, could not get flags from filedescriptor");
exit(EXIT_FAILURE);
}
flags |= FD_CLOEXEC;
if (fcntl(fd, F_SETFD, flags) == -1) {
perror("error, could not set FD_CLOEXEC");
exit(EXIT_FAILURE);
}
}
operator int() {
assert(mp_fd);
return *mp_fd;
}
void show_fd_status() {
if (!mp_fd)
return;
int fd = *mp_fd;
using namespace std;
char buf[256];
int fd_flags = fcntl(fd, F_GETFD);
if (fd_flags == -1)
return;
int fl_flags = fcntl(fd, F_GETFL);
if (fl_flags == -1)
return;
char path[256];
sprintf(path, "/proc/self/fd/%d", fd);
memset(&buf[0], 0, 256);
ssize_t s = readlink(path, &buf[0], 256);
if (s == -1) {
cerr << " (" << path << "): " << "not available";
return;
}
cerr << fd << " (" << buf << "): ";
// file status
if (fd_flags & FD_CLOEXEC) cerr << "cloexec ";
if (fl_flags & O_APPEND) cerr << "append ";
if (fl_flags & O_NONBLOCK) cerr << "nonblock ";
// acc mode
if (fl_flags & O_RDONLY) cerr << "read-only ";
if (fl_flags & O_RDWR) cerr << "read-write ";
if (fl_flags & O_WRONLY) cerr << "write-only ";
if (fl_flags & O_DSYNC) cerr << "dsync ";
if (fl_flags & O_RSYNC) cerr << "rsync ";
if (fl_flags & O_SYNC) cerr << "sync ";
struct flock fl;
fl.l_type = F_WRLCK;
fl.l_whence = 0;
fl.l_start = 0;
fl.l_len = 0;
fcntl(fd, F_GETLK, &fl);
if (fl.l_type != F_UNLCK)
{
if (fl.l_type == F_WRLCK)
cerr << "write-locked";
else
cerr << "read-locked";
cerr << "(pid:" << fl.l_pid << ") ";
}
}
private:
std::shared_ptr<int> mp_fd;
};
struct child
{
pid_t pid;
fdhandle fd;
};
void rebind_and_exec_child(fdhandle fd, std::string exe) {
// unset FD_CLOEXEC
int flags, oflags;
flags = oflags = fcntl(fd, F_GETFD);
if (-1 == flags) {
perror("error, could not get flags from filedescriptor");
exit(EXIT_FAILURE);
}
flags &= ~FD_CLOEXEC;
if (fcntl(fd, F_SETFD, flags) == -1) {
perror("error, could not unset FD_CLOEXEC");
exit(EXIT_FAILURE);
}
// close and rebind the stdin/stdout/stderr
// dup the fd three times and recreate stdin/stdout/stderr with fd as the target
if (dup2(fd, STDIN_FILENO) != 0 || dup2(fd, STDOUT_FILENO) != 1 || dup2(fd, STDERR_FILENO) != 2) {
perror("error duplicating socket for stdin/stdout/stderr");
exit(EXIT_FAILURE);
}
// restore the old flags
if (fcntl(fd, F_SETFD, oflags) == -1) {
perror("error, could not set FD_CLOEXEC");
exit(EXIT_FAILURE);
}
// now we can exec the new sub process and talk to it through
// stdin/stdout/stderr
char path[256];
char argv[256];
sprintf(path,"%s",exe.c_str());
sprintf(argv,"%d",30);
execlp(path, path, argv, 0);
// this should never be reached
perror("error: executing the binary");
exit(EXIT_FAILURE);
}
child fork_connected_child(std::string exe) {
// create the socketpair
int fd[2];
if (-1 == socketpair(PF_LOCAL, SOCK_STREAM, 0, fd)) {
perror("error, could not create socket pair");
exit(EXIT_FAILURE);
}
fdhandle fdparent(fd[0]); fdhandle fdchild(fd[1]);
// now create the child
pid_t pid = fork();
switch (pid) {
case -1: // could not fork
perror("error forking the child");
exit(EXIT_FAILURE);
break;
case 0: // child
rebind_and_exec_child(fdchild, exe);
break;
default: // parent
std::cout << "forked " << exe << std::endl;
return child { pid, fdparent };
break;
}
}
int main(int argc, const char** argv) {
// setup the signal handler prior to forking
sleep(20);
// create 2 childs
{
child child1 = fork_connected_child("/usr/bin/sleep");
child child2 = fork_connected_child("/usr/bin/sleep");
int status;
waitpid(child1.pid, &status, 0);
waitpid(child2.pid, &status, 0);
}
sleep(20);
}
I'm doing an implementation of FTP, now I'm implementing the RETR command, I take the errno code 88 when I try to do "get FILENAME".
I think that the error can be the conversion of unsigned to uint32_t and uint16_t in the port command.
#include <cstring>
#include <cstdarg>
#include <cstdio>
#include <cerrno>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <locale.h>
#include <langinfo.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <iostream>
#include <dirent.h>
#include "common.h"
#include "ClientConnection.h"
ClientConnection::ClientConnection(int s) {
int sock = (int)(s);
char buffer[MAX_BUFF];
control_socket = s;
// Consultar la documentación para conocer el funcionamiento de fdopen.
fd = fdopen(s, "a+");
if (fd == NULL){
std::cout << "Connection closed" << std::endl;
fclose(fd);
close(control_socket);
ok = false;
return ;
}
ok = true;
data_socket = -1;
};
ClientConnection::~ClientConnection() {
fclose(fd);
close(control_socket);
}
int connect_TCP(uint32_t address, uint16_t port) {
struct sockaddr_in sin;
struct hostent
*hent;
int s;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
sin.sin_addr.s_addr = address;
//if(hent = gethostbyname(address))
//memcpy(&sin.sin_addr,hent->h_addr,hent->h_length);
//else if ((sin.sin_addr.s_addr = inet_addr((char*)address)) == INADDR_NONE)
//errexit("No puedo resolver el nombre \"%s\"\n", address);
s = socket(AF_INET, SOCK_STREAM, 0);
if(s < 0){
printf("No se puede crear el socket\n");
return 0;
}
if(connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0){
printf("No se puede conectar con %u\n", address);
return 0;
}
return s;
}
void ClientConnection::stop() {
close(data_socket);
close(control_socket);
parar = true;
}
#define COMMAND(cmd) strcmp(command, cmd)==0
void ClientConnection::WaitForRequests() {
if (!ok) {
return;
}
fprintf(fd, "220 Service ready\n");
while(!parar) {
fscanf(fd, "%s", command);
if (COMMAND("USER")) {
fscanf(fd, "%s", arg);
fprintf(fd, "331 User name ok, need password\n");
}
else if (COMMAND("PWD")) {
}
else if (COMMAND("PASS")) {
char pass[30];
fscanf(fd,"%s",pass);
fprintf(fd,"230 User logged in\n");
}
else if (COMMAND("PORT")) {
unsigned ip[4];
unsigned port[2];
fscanf(fd,"%u,%u,%u,%u,%u,%u",&ip[0],&ip[1],&ip[2],&ip[3],&port[0],&port[1]);
uint32_t aux1;
uint16_t aux2;
aux1 = ip[3] << 24 | ip[2] << 16 | ip[1] << 8 | ip[0];
aux2 = port[1]*256 + port[0];
data_socket = connect_TCP(aux1,aux2);
fprintf(fd,"200 OK\n");
}
else if (COMMAND("PASV")) {
}
else if (COMMAND("CWD")) {
}
else if (COMMAND("STOR") ) { //put
FILE* fp = fopen("filename","w+");
int size_buffer = 512;
char buffer[size_buffer];
int recived_datas;
while(recived_datas == size_buffer){
datos_recibidos = recv(data_socket,buffer,size_buffer,0);
fwrite(buffer,1,recived_datas,fp);
}
close(data_socket);
fclose(fp);
}
else if (COMMAND("SYST")) {
fprintf(fd,"SYSTEM DETAILS\n");
}
else if (COMMAND("TYPE")) {
fprintf(fd,"type 1");
}
else if (COMMAND("RETR")) {
fscanf(fd,"%s",arg);
std::cout << "Argument: " << arg << std::endl;
FILE* fp = fopen(arg,"r+");
int sent_datas;
int size_buffer = 512;
char buffer[size_buffer];
std::cout << "Buffer size = " << size_buffer << std::endl;
do{
sent_datas = fread(buffer,size_buffer,1,fp);
printf("Code %d | %s\n",errno,strerror(errno));
send(data_socket,buffer,sent_datas,0);
printf("Code %d | %s\n",errno,strerror(errno));
}while(sent_datas == size_buffer);
close(data_socket);
fclose(fp);
fprintf(fd,"Transferencia completada");
}
else if (COMMAND("QUIT")) {
}
else if (COMMAND("LIST")) {
}
else {
fprintf(fd, "502 Command not implemented.\n"); fflush(fd);
printf("Comando : %s %s\n", command, arg);
printf("Error interno del servidor\n");
}
}
fclose(fd);
return;
};
Error code 88 is ENOTSOCK meaning that your tried to do a socket operation on "not a socket".
The offending line, I believe is:
send(data_socket,buffer,sent_datas,0);
It looks like in your RETR section you never set data_socket to a valid socket with your connect_TCP function, as you did in PORT. Are you certain that data_socket is a valid fd when you call your RETR function?
I'm having a problem with my server printing what is sent to it. I know that the data is being received because it broadcasts it back to all clients.
#include <iostream>
#include <string>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <ncurses.h>
#include <vector>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#define MAX_CONNECTIONS 10
#define MAX_SRSIZE 500
using namespace std;
struct bcpackage{
string * message;
};
struct clientval{
int fd;
};
vector<int> file_descriptors;
WINDOW * console, * input;
void * handleClient(void * arg);
void * broadcast(void * arg);
void wprintr(WINDOW * win, const char * message);
int main(int argc, char **argv)
{
int myfd, * status;
status = new int();
struct addrinfo myaddrinfo, *res;
/****************SETUP NCURSES UI******************/
initscr();
int y, x;
getmaxyx(stdscr, y, x);
console = subwin(stdscr,y - 1, x, 0, 0);
input = subwin(stdscr,1,x,y-1,0);
wrefresh(console);
wprintr(input,">");
/**************************************************/
string port = "25544";
memset(&myaddrinfo, 0, sizeof(myaddrinfo));
myaddrinfo.ai_family = AF_UNSPEC;
myaddrinfo.ai_socktype = SOCK_STREAM;
myaddrinfo.ai_flags = AI_PASSIVE;//Specifies that your socket will be a passive socket that waits for connections
wprintr(console,"Starting Server");
int aistat = getaddrinfo(NULL, "25544", &myaddrinfo, &res);
if( aistat == 0){
wprintr(console,"Host Information Retrieved");
}
else{
//string message = "Error: ";
//message+=gai_strerror(aistat);
wprintr(console, gai_strerror(aistat));
endwin();
exit(1);
}
//We now have our address now we create a socket
myfd = socket(res->ai_family, res->ai_socktype,res->ai_protocol);
if(myfd==-1){
wprintr(console, "Socket Creation Failed");
getch();
endwin();
exit(2);
}
//If all went well, we now have a socket for our server
//we will now use the bind() function to bind our socket
//to our program. I think that is what it does at least.
*status = bind(myfd, res->ai_addr, res->ai_addrlen);
//wprintw(console, "Status: %d\n", *status);
if((*status) < 0){
wprintr(console, "Bind failed");
getch();
endwin();
exit(3);
}
else{
wprintr(console, "Bind success");
}
//Now that we are bound, we need to listen on the socket
*status = listen(myfd, MAX_CONNECTIONS);
if(status>=0){
wprintr(console, "Listening on socket");
}
else{
wprintr(console, "Listen failed");
getch();
endwin();
exit(4);
}
//Everything is setup now we send the server into a loop that will pass
//each client to a new pthread.
while(true){
int *clientfd = new int();
pthread_t * cliPID = new pthread_t();
struct sockaddr_in * cliaddr = new struct sockaddr_in();
socklen_t *clilen = new socklen_t();
*clilen = sizeof(*cliaddr);
*clientfd = accept(myfd, (struct sockaddr *)cliaddr, clilen);
file_descriptors.push_back(*clientfd);
pthread_create(cliPID, NULL, handleClient, clientfd);
}
wprintr(console, "Now Exiting");
getch();
endwin();
return 0;
}
void * handleClient(void * arg){//Reads and writes to the functions
int filedesc = *((int *)arg);
char buffer[MAX_SRSIZE];
char * buf = buffer;
memset(&buffer, 0, sizeof(buffer));
while(!read(filedesc, buf, sizeof(buffer))<=0){
if(strcmp(buf, "")!=0){
wprintr(console, buffer);//<- I think this is the problem Idk why though.
broadcast(&buffer);
}
memset(&buffer, 0, sizeof(buffer));
}
wprintr(console, "Client Exited");
int fdremove = -1;
for(int i = 0; i < file_descriptors.size(); i++){
if(file_descriptors.at(i)==filedesc){
file_descriptors.erase(file_descriptors.begin()+i);
wprintr(console, "File Descriptor Removed");
}
}
pthread_exit(NULL);
}
void * broadcast(void * arg){
char * message = (char *)arg;
int num_fds = file_descriptors.size();
for(int i = 0; i < num_fds; i++ ){
write(file_descriptors.at(i), message, MAX_SRSIZE);
}
}
void wprintr(WINDOW * win, const char * message){
wprintw(win, message);
wprintw(win, "\n");
wrefresh(win);
}
This is written for Linux and needs -lpthread and -lncurses to compile. When you run the server you can telnet to it to test it. I know that data is being received because it is getting broadcasted back to all of the clients however the data received is not being printed on the server's screen. I think it may be an issue with ncurses but I don't know. I believe the problem is in my handleClient function.
Thanks in advance.
telnet sends "\r\n" at the end of every line. If you don't remove those characters every line you print is instantly overwritten.
I think it's bad idea to use ncurses in a server. Usually you want to save a log to a file, which would be very hard if you use ncurses. And, you will run into problems like this, because you cannot tell exactly what your program is outputting.