У меня есть следующий код.
Этот код предназначен для TFTP-сервера, который создает ответвление или поток для каждого полученного запроса. Моя проблема в методах потока.
Например, я запрашиваю 30 файлов с сервера, он должен создать 30 потоков и передать запрошенные файлы клиенту, каждый поток будет отправлять каждый файл. Код работает отлично, если я использую pthread_join
(который прокомментирован), но если у меня нет pthread_join
, он правильно обслуживает некоторые файлы, но некоторые из них повреждены или пусты.
Я считаю, что это проблема синхронизации, поэтому я попытался выделить malloc
кусок памяти для каждого файлового дескриптора клиента, чтобы поток мог изменять только свой собственный файловый дескриптор, но безуспешно. Следующий код, как есть, работает и служит некоторым
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <dirent.h>
#include <pthread.h>
#define BUFSIZE 8096
#define OperationMode 1
#if OperationMode
typedef struct {
int * fd;
int hit;
} THREAD_ARGS;
void *attendFTP(void *);
#endif
int ftp(int fd, int hit);
void getFunction(int fd, char * fileName);
void putFunction(int fd, char * fileName);
char * listFilesDir(char * dirName);
void lsFunction(int fd, char * dirName);
void mgetFunction(int fd, char *dirName);
/* just checks command line arguments, setup a listening socket and block on accept waiting for clients */
int main(int argc, char **argv) {
int i, port, pid, listenfd, socketfd, hit;
socklen_t length;
static struct sockaddr_in cli_addr; /* static = initialised to zeros */
static struct sockaddr_in serv_addr; /* static = initialised to zeros */
if (argc < 3 || argc > 3 || !strcmp(argv[1], "-?")) {
printf("\n\nhint: ./tftps Port-Number Top-Directory\n\n""\ttftps is a small and very safe mini ftp server\n""\tExample: ./tftps 8181 ./fileDir \n\n");
exit(0);
}
if (chdir(argv[2]) == -1) {
printf("ERROR: Can't Change to directory %s\n", argv[2]);
exit(4);
}
printf("LOG tftps starting %s - pid %d\n", argv[1], getpid());
/* setup the network socket */
if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
printf("ERROR system call - setup the socket\n");
port = atoi(argv[1]);
if (port < 0 || port > 60000)
printf("ERROR Invalid port number (try 1->60000)\n");
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(port);
if (bind(listenfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
printf("ERROR system call - bind error\n");
if (listen(listenfd, 64) < 0)
printf("ERROR system call - listen error\n");
// Main LOOP
for (hit = 1 ;; hit++) {
length = sizeof(cli_addr);
/* block waiting for clients */
socketfd = accept(listenfd, (struct sockaddr *) &cli_addr, &length);
if (socketfd < 0)
printf("ERROR system call - accept error\n");
else
{
#if OperationMode
pthread_t thread_id;
THREAD_ARGS *args = malloc(sizeof(THREAD_ARGS));
int * sockAUX = (int *) malloc(sizeof(int *));
*sockAUX = socketfd;
args->fd = sockAUX;
args->hit = hit;
if (args != NULL) {
if (pthread_create(&thread_id, NULL, &attendFTP, args)) {
perror("could not create thread");
return 1;
}
}
//pthread_join(thread_id,NULL);
#else
pid = fork();
if(pid==0) {
ftp(socketfd, hit);
} else {
//Temos de fechar o socketfd para que seja apenas a child a tratar dos pedidos, caso contrário iria ficar aqui pendurado
close(socketfd);
kill(pid, SIGCHLD);
}
#endif
}
}
}
#if OperationMode
void *attendFTP(void *argp) {
THREAD_ARGS *args = argp;
int sock = *args->fd;
printf("FD SOCK: %d\n\n", sock);
ftp(sock, args->hit);
free(args);
//printf("Thread executou\n\n");
pthread_exit(NULL);
return NULL;
}
#endif
/* this is the ftp server function */
int ftp(int fd, int hit) {
int j, file_fd, filedesc;
long i, ret, len;
char * fstr;
static char buffer[BUFSIZE + 1]; /* static so zero filled */
printf("FD: %d\n\n", fd);
ret = read(fd, buffer, BUFSIZE); // read FTP request
if (ret == 0 || ret == -1) { /* read failure stop now */
close(fd);
return 1;
}
if (ret > 0 && ret < BUFSIZE) /* return code is valid chars */
buffer[ret] = 0; /* terminate the buffer */
else
buffer[0] = 0;
for (i = 0; i < ret; i++) /* remove CF and LF characters */
if (buffer[i] == '\r' || buffer[i] == '\n')
buffer[i] = '*';
printf("LOG request %s - hit %d\n", buffer, hit);
/* null terminate after the second space to ignore extra stuff */
for (i = 4; i < BUFSIZE; i++) {
if (buffer[i] == ' ') { /* string is "GET URL " +lots of other stuff */
buffer[i] = 0;
break;
}
}
if (!strncmp(buffer, "get ", 4)) {
// GET
getFunction(fd, &buffer[5]);
} else if (!strncmp(buffer, "ls ", 3)) {
// LS
lsFunction(fd,&buffer[3]);
} else if (!strncmp(buffer, "mget ", 4)) {
// MGET
mgetFunction(fd, &buffer[5]);
}
sleep(1); /* allow socket to drain before signalling the socket is closed */
close(fd);
return 0;
}
void getFunction(int fd, char * fileName){
int file_fd;
long ret;
printf("FD GET: %d\n\n", fd);
static char buffer[BUFSIZE + 1]; /* static so zero filled */
if ((file_fd = open(fileName, O_RDONLY)) == -1) { /* open the file for reading */
printf("ERROR failed to open file %s\n", fileName);
printf("Err: %d\n\n",errno);
sprintf(buffer, "%s", "erro");
write(fd,buffer,BUFSIZE);
close(fd);
return;
}
printf("GET -> LOG SEND %s \n", fileName);
/* send file in 8KB block - last block may be smaller */
while ((ret = read(file_fd, buffer, BUFSIZE)) > 0) {
write(fd, buffer, ret);
}
}
void lsFunction(int fd, char * dirName){
printf("LS -> LOG Header %s \n", dirName);
static char buffer[BUFSIZE + 1];
sprintf(buffer, "%s", listFilesDir(dirName));
write(fd,buffer,BUFSIZE);
}
void mgetFunction(int fd, char *dirName)
{
FILE *fp;
char path[255];
static char buffer[BUFSIZE + 1];
printf("MGET COUNT -> LOG Header %s \n", dirName);
sprintf(buffer, "%s", listFilesDir(dirName));
write(fd,buffer,BUFSIZE);
}
char * listFilesDir(char * dirName)
{
DIR *midir;
struct dirent* info_archivo;
struct stat fileStat;
char fullpath[256];
char *files = malloc (sizeof (char) * BUFSIZE);
if ((midir=opendir(dirName)) == NULL)
{
return "\nO directorio pedido não existe.\n";
}
while ((info_archivo = readdir(midir)) != 0)
{
strcpy (fullpath, dirName);
strcat (fullpath, "/");
strcat (fullpath, info_archivo->d_name);
if (!stat(fullpath, &fileStat))
{
if(!S_ISDIR(fileStat.st_mode))
{
strcat (files, info_archivo->d_name);
strcat (files, "$$");
}
} else {
return "\nErro ao ler o directório.\n";
}
}
closedir(midir);
return files;
}
Это пример лога с сервера
LOG tftps начиная с 8181 - pid 15416 FD SOCK: 4
FD: 4
Запрос журнала ls . - нажмите 1 LS -> Заголовок журнала. ФД НОСОК: 5
FD: 5
LOG запрос mget . - нажмите 2 MGET COUNT -> Заголовок журнала. ФД НОСОК: 4
FD: 4
ФД НОСОК: 5
FD: 5
Запрос LOG получить /.gitconfig - нажмите 4 FD GET: 5
GET -> LOG SEND .gitconfig LOG request get /
ERROR failed to open file
FD SOCK: 4
FD: 4
Запрос LOG получить /ghostdriver.log - нажмите 6 FD GET: 4
ПОЛУЧИТЬ -> ОТПРАВИТЬ ЖУРНАЛ ghostdriver.log FD SOCK: 8
FD: 8
Запрос LOG получить /.bash_history - нажмите 7 FD GET: 8
ПОЛУЧИТЬ -> ОТПРАВИТЬ ЖУРНАЛ .bash_history FD SOCK: 6
FD: 6
Запрос LOG получить /.profile - нажмите 5 FD GET: 6
ПОЛУЧИТЬ -> ОТПРАВИТЬ ЖУРНАЛ .profile FD SOCK: 10
FD: 10
Запрос LOG get /.ICEauthority — нажмите 8 FD GET: 10
ПОЛУЧИТЬ -> ОТПРАВИТЬ ЖУРНАЛ .ICEauthority FD SOCK: 13
FD: 13
ФД НОСОК: 14
ФД НОСОК: 22
FD: 22
ФД НОСОК: 16
ФД НОСОК: 27
FD: 27
ФД НОСОК: 18
FD: 18
ФД НОСОК: 19
Запрос LOG get /.gtk-bookmarks — попадание 14 FD SOCK: 25
FD: 25
ФД НОСОК: 15
Запрос LOG получить /.bashrc — нажать 20 Запрос LOG получить /.bashrc — нажать 9 FD GET: 18
ФД ПОЛУЧИТЬ: 25
ФД ПОЛУЧИТЬ: 13
ПОЛУЧИТЬ -> ОТПРАВИТЬ ЖУРНАЛ .bashrc ПОЛУЧИТЬ -> ОТПРАВИТЬ ЖУРНАЛ .bashrc FD SOCK: 23
FD: 23
Запрос LOG get /.zshrc — попадание 18 FD SOCK: 26
FD: 26
ФД ПОЛУЧИТЬ: 23
ФД СОК: 29
FD: 29
ПОЛУЧИТЬ -> ОТПРАВИТЬ ЖУРНАЛ .bash_logoutks ПОЛУЧИТЬ -> ОТПРАВИТЬ ЖУРНАЛ .bash_logout FD SOCK: 17
FD: 17
Запрос LOG get /.zsh-update — попадание 13 Запрос LOG get /.zsh-update — попадание 22 FD SOCK: 28
ФД ПОЛУЧИТЬ: 27
FD: 14
Запрос LOG get /.nano_history — попадание 10 Запрос LOG get /.nano_history — попадание 24 Запрос LOG get /.nano_history — попадание 21 FD: 16
Запрос LOG get /.zsh_history — попадание 12 FD SOCK: 21
FD: 21
ФД ПОЛУЧИТЬ: 16
FD: 19
Запрос LOG получить /.selected_editor - нажмите 15 FD SOCK: 24
FD: 24
FD: 15
ФД ПОЛУЧИТЬ: 14
ФД ПОЛУЧИТЬ: 17
ФД ПОЛУЧИТЬ: 29
GET -> LOG SEND .zcompdump-MacPearl-5.0.2 Запрос LOG get /.zcompdump-MacPearl-5.0.2 - hit 17 FD: 28
Запрос LOG get /.Xauthority - нажмите 23 GET -> LOG SEND .Xauthority GET -> LOG SEND .Xauthority FD GET: 28
Запрос LOG get /.Xauthority — нажмите 11 GET -> LOG SEND .Xauthority FD GET: 15
ПОЛУЧИТЬ -> ОТПРАВИТЬ ЖУРНАЛ .Xauthority ПОЛУЧИТЬ -> ОТПРАВИТЬ ЖУРНАЛ .Xauthority FD ПОЛУЧИТЬ: 19
ФД ПОЛУЧИТЬ: 26
ПОЛУЧИТЬ -> ЖУРНАЛ ОТПРАВИТЬ .Xauthority Запрос ЖУРНАЛА получить /.Xauthority — нажать 16 FD GET: 21
ПОЛУЧИТЬ -> ОТПРАВИТЬ ЖУРНАЛ .Xauthority FD ПОЛУЧИТЬ: 22
Запрос LOG get /.Xauthority - нажмите 19 GET -> LOG SEND .Xauthority GET -> LOG SEND .Xauthority GET -> LOG SEND .Xauthority FD GET: 24
ПОЛУЧИТЬ -> ОТПРАВИТЬ ЖУРНАЛ .Xauthority FD SOCK: 30
FD: 30
ФД СОК: 31
FD: 31
Запрос LOG получить /.zcompdumpy — нажать 25 Запрос LOG получить /.zcompdump — нажать 26 FD GET: 30
ФД ПОЛУЧИТЬ: 31
ПОЛУЧИТЬ -> ОТПРАВИТЬ ЖУРНАЛ .zcompdump ПОЛУЧИТЬ -> ОТПРАВИТЬ ЖУРНАЛ .zcompdump
Я вижу, что иногда он использует один и тот же файловый дескриптор более чем в одном потоке. Я просто не могу понять, как я могу решить эту проблему с синхронизацией.