LINUX.ORG.RU

Система обмена файлами.Сокеты,linux.

 , , , ,


0

1

Здравствуйте! У меня есть проект «Система обмена файлами».

Задание: разработать приложение-клиент и приложение сервер, обеспечивающие функции обмена файлами.

Клиент может:

  • Получить список файлов хранящихся на сервере.
  • Добавить файл.
  • Удалить файл.
  • Скачать файл.
  • Отключиться от сервера.
  • Отключить сервер и сам отключиться.

Сервер:

  • Обновляет и отправляет список файлов клиенту.
  • Получает файл в папку files сервера.
  • Получает номер файла для удаления из папки files.
  • Отправляет файл клиенту
  • Пишем сообщение,что клиент отключился.
  • Завершаем работу сервера

Листинг Сервер:

/*  HELP:
..Server:
.........path: /home/bladzher/projects/[CLion]/FileTransferSystem/trunk/server
.........gcc server.c -o server
..Client
.........path: /home/bladzher/projects/[CLion]/FileTransferSystem/trunk/client
.........gcc client.c -o client
*******************************************************************************
*
* PATH files:
* ...........PC name: /ksergey/
* ...........Ultrabook name: /bladzher/
*
*******************************************************************************
 */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <time.h>
#include <string.h>
#include "dirent.h"

#define BUF_SIZE 1024
#define TEXT "Вы успешно подключились к системе передачи файлов!\n\nМЕНЮ:\n1)Отобразить список файлов.\n2)Добавить файл.\n3)Удалить файл.\n4)Скачать файл.\n5)Выход.\n6)Выключить сервер.\n"

char message1[1];
char buf1[sizeof(message1)];

int main() {
    int sock, listener;      // дескрипторы сокетов
    struct sockaddr_in addr; // структура с адресом
    char buf;          // буфер для приема
    int bytes_read;              // кол-во принятых байт

    listener = socket(AF_INET, SOCK_STREAM, 0); // создаем сокет для входных подключений
    if (listener < 0) {
        perror("socket");
        exit(1);
    }

    // Указываем параметры сервера
    addr.sin_family = AF_INET;
    addr.sin_port = htons(3425);
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    if (bind(listener, (struct sockaddr *) &addr, sizeof(addr)) < 0) // связываемся с сетевым устройство. Сейчас это устройство lo - "петля", которое используется для отладки сетевых приложений
    {
        perror("bind");
        exit(2);
    }

    listen(listener, 1); // очередь входных подключений

    int number, *ptn;
    number = 0;
    ptn = &number;

    int number1, *ptn1;
    number1 = 0;
    ptn1 = &number1;

    printf("\nОжидание подключения:\n");
    while (1) {
        sock = accept(listener, NULL, NULL); // принимаем входные подключение и создаем отделный сокет для каждого нового подключившегося клиента
        if (sock < 0) {
            perror("accept");
            exit(3);
        }
        char text[] = TEXT;
        send(sock, text, sizeof(text), 0);

        printf("К серверу подключился клиент!Ожидаем команду:\n");
        recv(sock, (void *) &number, 4, 0);
        printf("Получена команда от клиента: %d\n", *ptn);
        send(sock, "Сервер получил сообщение!", sizeof("Сервер получил сообщение!"), 0);

/*        ///Информация о файле
//        char *date;
//        int ret;
//        struct stat buf1;
//        if((ret = stat("/home/bladzher/projects/[CLion]/FileTransferSystem/trunk/server/files/Дима_Пряхин-новая_религия.mp3", &buf1)) !=0)
//        {
//            fprintf(stderr, "stat failure error .%d", ret);
//            abort();
//        }
//        date = asctime(localtime((time_t const *) &buf1.st_ctim));
//        printf("\n %s\n", date);
//        printf("\n %d mode\n", buf1.st_mode);
//        printf("\n %ld size\n", buf1.st_size);
        //Конец_Информация о файле*/

        if (*ptn == 1) {
            DIR *dir;
            struct dirent *entry;

            dir = opendir("/home/bladzher/projects/[CLion]/FileTransferSystem/trunk/server/files");
            if (!dir) {
                perror("diropen");
                close(sock);
                exit(4);
            }

            FILE *fo;
            if (!(fo = fopen("/home/bladzher/projects/[CLion]/FileTransferSystem/trunk/server/list.xml", "wt"))) {
                perror("fopen");
                close(sock);
                exit(6);
            }
            int i = 0;
            while ((entry = readdir(dir)) != NULL) {

                fprintf(fo, "%d)%s\n", i, entry->d_name);
                printf("%d)%s\n", i, entry->d_name);
                i++;
            }

            send(sock, entry, sizeof(entry), 0);
            closedir(dir);
            fclose(fo);

            FILE *f;
            if (!(f = fopen("/home/bladzher/projects/[CLion]/FileTransferSystem/trunk/server/list.xml", "rb"))) {
                perror("fopen");
                close(sock);
                exit(7);
            }
            fseek(f, 0, SEEK_END);
            int fsize = (int) ftell(f);
            rewind(f);

            long sended = 0;
            long readed = 0;

            char buffer[BUF_SIZE];
            do {
                readed = fread(buffer, 1, BUF_SIZE, f);
                send(sock, buffer, (size_t) readed, 0);
                sended += readed;
            } while (sended != fsize);
            fclose(f);

        }
        else if (*ptn == 2) {
            //Получаем имя и расширение файла
            //Получаем файл и добавляем его в папку files сервера.
            //Пишем клиенту,что файл успешно добавлен или иное.
        }
        else if (*ptn == 3) {

            recv(sock, (void *) &number1, 4, 0);
            printf("Получен номер файла для удаления: %d\n", *ptn1);
            //определяем имя и расширение файла
            //удаляет данный файл
            send(sock, "Сервер удалил файл!", sizeof("Сервер удалил файл!"), 0);

        }
        else if (*ptn == 4) {                                     //Передача файла
            FILE *f;
            if (!(f = fopen("/home/bladzher/projects/[CLion]/FileTransferSystem/trunk/server/files/Дима_Пряхин-новая_религия.mp3", "rb"))) {
                perror("fopen");
                close(sock);
                exit(8);
            }
            fseek(f, 0, SEEK_END);
            int fsize1 = (int) ftell(f);
            rewind(f);

            long sended1 = 0;
            long readed1 = 0;

            char buffer[BUF_SIZE];
            do {
                readed1 = fread(buffer, 1, BUF_SIZE, f);
                send(sock, buffer, (size_t) readed1, 0);
                sended1 += readed1;
            } while (sended1 != fsize1);
            fclose(f);
        }                                                   ///Конец_Передача файла
        else if (*ptn == 5) {

            printf("Клиент отключился!\n");

        }
        else if (*ptn == 6) {
            break;
        }

        close(sock); // закрываем сокет
        printf("\nОжидание подключения:\n");
    }
    close(sock);
    return 0;
}

Листинг Клиент:


/*  HELP:
..Server:
.........path: /home/bladzher/projects/[CLion]/FileTransferSystem/trunk/server
.........gcc server.c -o server
..Client
.........path: /home/bladzher/projects/[CLion]/FileTransferSystem/trunk/client
.........gcc client.c -o client
*******************************************************************************
*
* PATH files:
* ...........PC name: /ksergey/
* ...........Ultrabook name: /bladzher/
*
*******************************************************************************
 */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
//#include <dialog.h>
#include <time.h>
#include <search.h>
#include <bits/string2.h>

#define BUF_SIZE 1024

char message[1024];
char buf[sizeof(message)];

int main() {
    int sock;                // дескриптор сокета
    struct sockaddr_in addr; // структура с адресом

    sock = socket(AF_INET, SOCK_STREAM, 0); // создание TCP-сокета
    if (sock < 0) {
        perror("socket");
        exit(1);
    }

    // Указываем параметры сервера
    addr.sin_family = AF_INET; // домены Internet
    addr.sin_port = htons(3425); // Порт
    addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
    if (connect(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) // установка соединения с сервером
    {
        perror("Подключение");
        exit(2);
    }

    int number, *ptn;
    number = 0;
    ptn = &number;

    int number1, *ptn1;
    number1 = 0;
    ptn1 = &number1;

    recv(sock, message, sizeof(message), 0);
    printf("Сообщение от сервера: %s\n", message);

    printf("Введите команду серверу:");
    scanf("%d", &number);

    printf("Отправляю сообщение клиенту.\n");
    send(sock, (void const *) &number, 4, 0); // отправка сообщения на сервер
    recv(sock, message, sizeof(message), 0);
    printf("%d\n", *ptn);

    long buffer[BUF_SIZE];
    long rcv_len = 0;


    if (*ptn == 1) {

        FILE *f;
        if (!(f = fopen("/home/bladzher/Загрузки/list.xml", "wb"))) {
            perror("fopen");
            close(sock);
            exit(3);
        }

        do {
            rcv_len = recv(sock, buffer, BUF_SIZE, 0);
            fwrite(buffer, 1, (size_t) rcv_len, f);
        } while (rcv_len != 0);

        fclose(f);

    } else if (*ptn == 2) {

        char str[128];
        char str1[128];
        char str2[257];
        printf("Введите полный путь до файла[/home/bladzher/projects/[CLion]/FileTransferSystem/trunk/client/files/]:");
        scanf("%s", (char *) &str);
        printf("Ввелите полное имя файла с расширением[Сплин - Выхода нет.mp3]:");
        scanf("%s", (char *) &str1);
        //  printf("%s\n", str1);
        strcat(str2, str);
        strcat(str2, str1);
        printf("%s", str2);
        printf("\n");

    } else if (*ptn == 3) {

        printf("Введите номер файла для удаления:");
        scanf("%d", &number1);
        printf("Отправляю сообщение клиенту.\n");
        send(sock, (void const *) &number1, 4, 0); // отправка сообщения на сервер
        recv(sock, message, sizeof(message), 0);
        printf("%s\n", message);

    }
    else if (*ptn == 4) {                                              //Прием файла

        FILE *f;
        if (!(f = fopen("/home/bladzher/Загрузки/Дима_Пряхин-новая_религия.mp3", "wb"))) {
            perror("fopen");
            close(sock);
            exit(4);
        }
        rcv_len = 0;
        do {
            rcv_len = recv(sock, buffer, BUF_SIZE, 0);
            fwrite(buffer, 1, (size_t) rcv_len, f);
        } while (rcv_len != 0);

        fclose(f);
    }
        //Конец_Прием файла
    else if (*ptn == 5) {

        close(sock);

    }
    else if (*ptn == 6) {

        close(sock);

    }


    close(sock); // закрываем сокет
    return 0;
}

Подскажите пожалуйста в чем проблема,у меня при выборе первого варианта файл list.xml 0 байт у клиента,а тот же код только отправляю песню (4 вариант) все отправляется.

+ подскажите пожалуйста по коду,как лучше сделать данный проект,какие-нибудь советы.

+ проблема в том,что когда я выключаю сервер (Ctrl + C) или когда клиент посылает команду под номером 6(Откл.сервера),то при повторном запуске сервера пишется "./server bind: Address already in use" - как это исправить?

P.S. Извините,если плохо,что описал,спешу на работу,а спросить необходимо.Если будут вопросы пишите!

+ Теги почему-то не работают,чтобы красиво оформить :(



Последнее исправление: beastie (всего исправлений: 3)

Оформи пост нормально, читать невозможно.

sambist ★★
()

Пока я правил твою нечитабельную простыню, я понял, что делаю что-то не то.

Во первых — с листингами пожалуйста на http://pastebin.com/ или ещё куда.

И во вторых — делать за тебя твои домашние задания — это в Job.

PS: за код жЫрный неуд. Его даже читать не хочется. Про структурированое програмирование и «функции», надеюсь, вам уже расказывали.

beastie ★★★★★
()
Последнее исправление: beastie (всего исправлений: 1)
Ответ на: комментарий от beastie

Почему же делать за меня? Я хочу хорошо написать код,поэтому и спрашиваю советы, интересуюсь как будет правильнее поступить поскольку в первую очередь я это делаю для себя, а чтобы просто сдать я могу кое как дальше дописать- сдать и забыть.

BladzheR
() автор топика
Ответ на: комментарий от BladzheR

как будет правильнее поступить

Для начала — разбей код на логические участки, эти участки выдели в отдельные функции.

Все пути («/home/bladzher/projects/[CLion]/FileTransferSystem/trunk/server/files» и подобное) хотя бы сделай константами.

Все магические константы замени на мнемоники.

if (*ptn == 1) {
if (RM_RF_COMMAND == command) {

Сделай хотя бы это. Потом, наверняка, будут советы более по-существу.

Deleted
()

Вызовы recv() и send() возвращают число байт, не большее, чем указано в аргументе, и не меньше одного байта. Надо рассчитывать на прием или передачу одного байта. Для записи send() это делается легко, вместо send() вызывайте самодельную функцию типа этой:

int write_all(int sd, const char *buf, int n)
{
    int i=0, k;
    while(i<n){
        k=write(sd, buf+i, n-i);
        if(k<=0)return(-1);
        i=i+k;
    }
    return(i);
}
Для чтения сложнее. Сообщение может придти не цельно, а по частям. Читать нужно в цикле. Но как определить, сколько читать? Протокол нужно доработать. Например, рассмотрим команду «скачать файл» с точки зрения клиента:
1. -подключиться.
2. -принять от сервера меню (конец меню не определен).
3. -послать на сервер команду "скачать файл".
4. -принять от сервера сообщение (конец сообщения не определен).
5. -принять файл (конец по read()=0).
На шаге 2 если оставить, как сейчас, и если меню придет частями, то первую часть распечатает, как меню, а вторую часть примет уже на шаге 4. Так же кусок шага 4 может перейти на шаг 5, и файл будет испорчен. Прием меню, сообщения, а на сервере и команд нужно делать в цикле, читать столько байт, сколько нужно, а не сколько придет. Даже если нужно прочитать двухбайтную команду, всё равно цикл, т. к. может вернуть дважды по одному байту. Для этого нужно доработать протокол, чтоб знать, где конец сообщения. Вот примеры.
1. Перед каждым сообщением давать два байта, содержащих длину сообщения.
2. Все сообщения имеют строго определенную длину, например 100 байт.
3. Всё сообщение состоит из непустых строк, а пустая строка - признак конца.
4. Сообщение не должно иметь нулевых байт, нулевой байт - признак конца.
Первые два проще сделать. Там у вас есть слабые места, но с протоколом надо определиться.

oleg_2
()

Обрабатывать код завершения send это выше вашего достоинства?

A-234 ★★★★★
()
Ответ на: комментарий от Deleted

Улучшил код...

Спасибо!

Сейчас не могу разобраться почему не удаляется файл по 3-й команде,
если прописать путь вручную,то файл удаляется...

Листинг server.c: Открыть
Листинг client.c: Открыть

P.S.
Код длинный - залил на Яндекс.

BladzheR
() автор топика
Ответ на: Улучшил код... от BladzheR

Для начала по оформлению:

 void functionCommandTwo() 
function убери, в скобки void, в случае Си () равносильно (…), поэтому лучше не рисковать, ибо компилятор схавает как «норму». И назови по человечески, чтобы понятно было, что оно делает.
char str[128];
Что есть str? Почему 128?

printf ("%s", str2);
printf ("\n");

printf(«%s\n», str2), комментарии излишни.

#define commandOne 1
#define commandTwo 2
#define commandTree 3
#define commandFour 4
#define commandFive 5
#define commandSix 6

Прочитай что такое enum.

Сейчас не могу разобраться почему не удаляется файл по 3-й команде,

Отладчик в руки, также поможет отладочный вывод в stdout. Все статусы завершения функций необходимо проверять, в них и ответ найдешь.

P.S.
Код длинный - залил на Яндекс.

Для таких целей стоит использовать pastebin, чтобы не заставлять людей скачивать файл ради подсветки синтаксиса. И да, он не длинный, можно было и сюда.

Deleted
()

Всем спасибо за помощь :) Проект готов!

BladzheR
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.