Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // ftp_server.cpp : Defines the entry point for the console application.
- #include "stdafx.h"
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <WinSock2.h>
- #include <Windows.h>
- #define MAX_FILE_BUFF_SIZE 4000 //максимальный размер буфера файлов
- #define MAX_COMMAND_BUFF_SIZE 300 //максимальный размер буфера команд
- #define MAX_FILE_THREADS 10 //максимальное количество одновременно передаваемых файлов
- #define QUEUE_SIZE 3 //размер очереди клиентов
- #define FILE_PORT 20 //номер порта для передачи файлов
- #define CMD_PORT 21 //номер порта для передачи сообщений
- #define CONTINUE "cont" //сигнал продолжать передачу (для ls)
- #define STOP "stop" //сигнал остановить передачу (для ls)
- struct clientSocks //сокеты клиента
- {
- SOCKET clCmdSock; //сокет команд
- SOCKET clFileSock; //сокет файлов
- };
- struct threadParam //структура параметра потоковой функции
- {
- clientSocks clSocks; //сокеты клиента
- char *str1;
- char *str2;
- };
- clientSocks fact_param[ QUEUE_SIZE ]; //массив сокетов( нужен для параллельной обработки запросов клиентов)
- HANDLE hsemFreeConnects; //семафор свободных подключений для клиентов
- HANDLE hsemFileTh; //семафор потоков передачи файлов
- HANDLE hFileThreads[ MAX_FILE_THREADS ]; //поток передачи файла
- int clientInd = 0; //номер клиента
- int fileThInd = 0; //номер потока передачи файла
- void InitSocketLib( void ); //инициализация библиотеки сокетов
- void CreateSocket( SOCKET* ); //создание сокета
- void BindSocket( SOCKET*, int ); //связывание сокета с локальным адресом
- void WaitForClientsRequests( void ); //ожидание запросов клиентов и их обработка
- void ReceiveCommand( SOCKET, char*, char* ); //получение команды от клиента
- void ChangeCurDirectory( SOCKET, char*, char* ); //смена текущей директории
- void SendDirList( SOCKET ); //отправка списка файлов и директорий
- void ListDirectory( SOCKET, char*, char* ); //просмотров файлов и директорий
- DWORD WINAPI SendFile( LPVOID ); //отправка файла
- DWORD WINAPI ServiceClient( LPVOID param ); //обслужить клиента в потоке
- //----------------------------------------------------------------------------------------
- int _tmain(int argc, _TCHAR* argv[])
- {
- printf( "FTP server has started\n" );
- //создать семафоры
- hsemFreeConnects = CreateSemaphore( NULL, QUEUE_SIZE, QUEUE_SIZE, NULL );
- hsemFileTh = CreateSemaphore( NULL, MAX_FILE_THREADS, MAX_FILE_THREADS, NULL );
- //инициализация винсоксов
- InitSocketLib();
- //ожидание запросов клиентов и их обработка
- WaitForClientsRequests();
- //деинициализация библиотеки сокетов
- WSACleanup();
- CloseHandle( hsemFreeConnects );
- printf( "FTP finished its work\n" );
- system( "pause" );
- return 0;
- }
- //----------------------------------------------------------------------------------------
- //инициализация библиотеки сокетов
- void InitSocketLib( void )
- {
- WSADATA wsData;
- //инициализация винсоксов
- if ( WSAStartup( MAKEWORD( 2, 2 ), &wsData ) )
- printf( "Initialization Error\n" );
- else printf( "Initialization Successful\n" );
- }
- //----------------------------------------------------------------------------------------
- //создание сокета
- void CreateSocket( SOCKET* p_sock )
- {
- //создание сокета
- *p_sock = socket( AF_INET, SOCK_STREAM, 0 );
- if ( *p_sock == INVALID_SOCKET )
- printf( "Socket Creation Failed\n" );
- else printf( "Socket Creation Successful\n" );
- }
- //----------------------------------------------------------------------------------------
- //связывание сокета с локальным адресом
- void BindSocket( SOCKET* p_sock, int portInd )
- {
- //задаем локальный адрес
- sockaddr_in servLocalAddr;
- servLocalAddr.sin_family = AF_INET;
- servLocalAddr.sin_port = htons( portInd );
- servLocalAddr.sin_addr.s_addr = INADDR_ANY;
- //связваем сокет сервера с локальным адресом
- if ( bind( *p_sock, ( sockaddr* )&servLocalAddr, sizeof( servLocalAddr ) ) == SOCKET_ERROR )
- printf( "Binding Error Port %d\n", portInd );
- else printf( "Binding Successful Port %d\n", portInd );
- }
- //----------------------------------------------------------------------------------------
- //отправка файла
- DWORD WINAPI SendFile( LPVOID param )
- {
- threadParam thParam = *( ( threadParam* )param );
- clientSocks clSocks = thParam.clSocks;
- char* fileName = thParam.str1;
- char* curDir = thParam.str2;
- FILE *fileToSend; //файл на отправку
- char bufCmd[ MAX_COMMAND_BUFF_SIZE ]; //буфер команд
- char bufFile[ MAX_FILE_BUFF_SIZE ]; //буфер файла
- __int64 fileSize = 0; //размер файла
- //установить текущую директорию перед началом выполнения команды
- SetCurrentDirectoryA( ( LPCSTR )curDir );
- //послать клиенту имя передаваемого файла
- send( clSocks.clCmdSock, fileName, MAX_COMMAND_BUFF_SIZE, 0 );
- //если не удалось открыть файл - уведомить об этом клиента, иначе начать передачу
- if ( ( fileToSend = fopen( fileName, "rb" ) ) == NULL )
- {
- //уведомить клиента о том что файл невозможно отркыть
- strcpy( bufCmd, STOP );
- send( clSocks.clCmdSock, bufCmd, MAX_COMMAND_BUFF_SIZE, 0 );
- printf( "File %s Cann't Be Opened On The Server\n", fileName );
- }
- else
- {
- //уведомить клиента о том что файл отркыт
- strcpy( bufCmd, CONTINUE );
- send( clSocks.clCmdSock, bufCmd, MAX_COMMAND_BUFF_SIZE, 0 );
- //получить и отправить клиенту размер файла
- fseek( fileToSend, 0, SEEK_END );
- fileSize = _ftelli64( fileToSend );
- fseek( fileToSend, 0, SEEK_SET );
- _i64toa( fileSize, bufCmd, 10 );
- send( clSocks.clCmdSock, bufCmd, MAX_COMMAND_BUFF_SIZE, 0 );
- //пока не кончился файл его куски клиенту
- while ( !feof( fileToSend ) )
- {
- //считать из файла в буфер
- fread( bufFile, sizeof( char ), MAX_FILE_BUFF_SIZE, fileToSend );
- //отправить информацию
- send( clSocks.clFileSock, bufFile, MAX_FILE_BUFF_SIZE, 0 );
- }
- //освободить место для нового потока передачи файла
- ReleaseSemaphore( hsemFileTh, 1, NULL );
- fileThInd--;
- fclose( fileToSend );
- }
- return 0;
- }
- //----------------------------------------------------------------------------------------
- //получение команды от клиента
- void ReceiveCommand( SOCKET clCmdSock, char *cmdName, char *argument )
- {
- char *clCmdReceived = ( char* )calloc( MAX_COMMAND_BUFF_SIZE, sizeof( char ) ); //команда клиента
- //принять команду от клиента
- recv( clCmdSock, clCmdReceived, MAX_COMMAND_BUFF_SIZE, 0 );
- //printf( "Command Recieve: %s\n", clCmdReceived );
- //получить командную часть
- *cmdName = clCmdReceived[ 0 ];
- //получить аргумент, если его нет - сделать строку аргумента пустой
- if ( strlen( clCmdReceived ) == 1 )
- strcpy( argument, "\0" );
- else if ( ( *cmdName ) != 'q' )
- {
- clCmdReceived += 2;
- strcpy( argument, clCmdReceived );
- }
- }
- //----------------------------------------------------------------------------------------
- //смена текущей директории
- void ChangeCurDirectory( SOCKET clCmdSocket, char *newDir, char* curDir )
- {
- //установить текущую директорию перед началом выполнения команды
- SetCurrentDirectoryA( ( LPCSTR )curDir );
- //если смена удалась, послать ему директорию, иначе послать ошибку
- if ( SetCurrentDirectoryA( ( LPCSTR )newDir ) )
- {
- //получить текущую директорию
- GetCurrentDirectoryA( MAX_COMMAND_BUFF_SIZE, ( LPSTR )curDir );
- //послать её клиенту
- send( clCmdSocket, curDir, MAX_COMMAND_BUFF_SIZE, 0 );
- printf( "Current Directory Changed To: %s\n", curDir );
- }
- else
- {
- strcpy( newDir, "error" );
- send( clCmdSocket, newDir, MAX_COMMAND_BUFF_SIZE, 0 );
- printf( "Error changing directory\n" );
- }
- }
- //----------------------------------------------------------------------------------------
- //просмотров файлов и директорий
- void ListDirectory( SOCKET clCmdSocket )
- {
- WIN32_FIND_DATAA findFileData; //информация о файле или директории
- HANDLE hFile; //хэндл файла
- char *mask = "\*"; //маска файлов и каталогов
- char bufName[ MAX_COMMAND_BUFF_SIZE ]; //буфер для передачи
- char bufSignal[ MAX_COMMAND_BUFF_SIZE ]; //буфер для передачи
- //попытаться найти 1ый файл в текущей директории
- if ( ( hFile = FindFirstFileA( ( LPCSTR )mask, &findFileData ) ) == INVALID_HANDLE_VALUE )
- {
- //отправить клиенту сообщение об останове поиска
- strcpy( bufSignal , STOP );
- send( clCmdSocket, bufSignal, MAX_COMMAND_BUFF_SIZE, 0 );
- /*printf( "##Can't Find First File\n" );*/
- }
- else
- {
- //отправить клиенту сообщение о продолжении
- strcpy( bufSignal , CONTINUE );
- send( clCmdSocket, bufSignal, MAX_COMMAND_BUFF_SIZE, 0 );
- //отправить клиенту имя файла/директории
- strcpy( bufName , findFileData.cFileName );
- send( clCmdSocket, bufName, MAX_COMMAND_BUFF_SIZE, 0 );
- /*printf( "##\t%s\n", findFileData.cFileName );*/
- //пока не просмотрели весь каталог на наличие файлов и папок - просматриваем
- while ( FindNextFileA( hFile, &findFileData ) != 0 )
- {
- //отправить сообщение о продолжении передачи
- strcpy( bufSignal , CONTINUE );
- send( clCmdSocket, bufSignal, MAX_COMMAND_BUFF_SIZE, 0 );
- //передать клиенту имя файла/каталога
- strcpy( bufName , findFileData.cFileName );
- send( clCmdSocket, bufName, MAX_COMMAND_BUFF_SIZE, 0 );
- /*printf( "##%s\n", findFileData.cFileName );*/
- }
- //уведомить клиента об окончании поиска в каталоге
- strcpy( bufSignal , STOP );
- send( clCmdSocket, bufSignal, MAX_COMMAND_BUFF_SIZE, 0 );
- }
- //завершить поиск
- FindClose( hFile );
- }
- //----------------------------------------------------------------------------------------
- //отправка списка файлов и директорий
- void SendDirList( SOCKET clCmdSocket, char* argument, char* curDir )
- {
- char bufSignal[ MAX_COMMAND_BUFF_SIZE ]; //буфер для передачи
- //установить текущую директорию перед началом выполнения команды
- SetCurrentDirectoryA( ( LPCSTR )curDir );
- //если аргумент отсутствует - отправить список файлов и папок текущего каталога
- if ( strcmp( argument, "\0" ) == 0 )
- ListDirectory( clCmdSocket );
- else
- {
- //установить текущей директорию аргумента
- //если установка удачна - то отправить клиенту список
- if ( SetCurrentDirectoryA( ( LPCSTR )argument ) )
- ListDirectory( clCmdSocket );
- else
- {
- //отправить клиенту сообщение об останове поиска
- strcpy( bufSignal , STOP );
- send( clCmdSocket, bufSignal, MAX_COMMAND_BUFF_SIZE, 0 );
- }
- //восстановить текущую директорию
- SetCurrentDirectoryA( ( LPCSTR )curDir );
- }
- }
- //----------------------------------------------------------------------------------------
- //обслуживание клиента
- DWORD WINAPI ServiceClient( LPVOID param )
- {
- clientSocks curClSocks = *( ( clientSocks* )param ); //сокеты клиента
- char curCmd = '\0'; //текущая команда
- char cmdArgument[ MAX_COMMAND_BUFF_SIZE ]; //аргумент команды
- char currentDir[ MAX_COMMAND_BUFF_SIZE ]; //текущая директория
- threadParam clThreadParam; //параметр для потоковых функций
- clThreadParam.clSocks = curClSocks;
- //установить начальную директорию в "c:\" и отправить клиенту
- strcpy( currentDir, "c:\\" );
- SetCurrentDirectoryA( ( LPCSTR )currentDir );
- send( curClSocks.clCmdSock, currentDir, MAX_COMMAND_BUFF_SIZE, 0 );
- do
- {
- //получить от клиента команду и имя файла
- ReceiveCommand( curClSocks.clCmdSock, &curCmd, cmdArgument );
- //в зависимости от команды выполнить действие
- //отправить файл
- switch ( curCmd )
- {
- case 'g':
- {
- //дожидаемся возможности скачать файл
- WaitForSingleObject( hsemFileTh, INFINITE );
- //запуск передачи файла в потоке
- clThreadParam.str1 = cmdArgument;
- clThreadParam.str2 = currentDir;
- hFileThreads[ fileThInd ] = CreateThread( NULL, 0, SendFile, ( LPVOID )&clThreadParam, 0, NULL );
- fileThInd++;
- break;
- }
- case 'c': ChangeCurDirectory( curClSocks.clCmdSock, cmdArgument, currentDir );
- break;
- case 'l': SendDirList( curClSocks.clCmdSock, cmdArgument, currentDir );
- break;
- case 'q': printf( "Client Was Disconnected\n" );
- break;
- default: //printf( "Unindefinied Command\n" );
- break;
- }
- } while ( curCmd != 'q' );
- //закрытие сокетов и хэндлов потоков
- closesocket( curClSocks.clCmdSock );
- closesocket( curClSocks.clFileSock );
- WaitForMultipleObjects( MAX_FILE_THREADS, hFileThreads, true, INFINITE );
- for ( int i = 0; i < MAX_FILE_THREADS; i++ )
- CloseHandle( hFileThreads[ i ] );
- //освободить подключение
- ReleaseSemaphore( hsemFreeConnects, 1, NULL );
- --clientInd;
- return 0;
- }
- //----------------------------------------------------------------------------------------
- //ожидание запросов клиентов и их обработка
- void WaitForClientsRequests( void )
- {
- //подготовка сокетов команд и файлов к работе
- SOCKET srvCmdSock; //сокет сервера команд
- SOCKET srvFileSock; //сокет сервера файлов
- sockaddr_in clLocAddr; //локальный адрес подключившегося клиента
- CreateSocket( &srvCmdSock );
- BindSocket( &srvCmdSock, CMD_PORT );
- CreateSocket( &srvFileSock );
- BindSocket( &srvFileSock, FILE_PORT );
- //перевести сервера команд и файлов в режим ожидания подключений
- listen( srvCmdSock, QUEUE_SIZE );
- listen( srvFileSock, QUEUE_SIZE );
- clientSocks clSocks; //сокеты клиента
- //извлечение запросов клиентов из очереди на подключение
- printf( "Waiting for connections...\n" );
- while ( ( clSocks.clCmdSock = accept( srvCmdSock, ( sockaddr* )&clLocAddr, NULL ) ) )
- {
- if ( clSocks.clCmdSock != INVALID_SOCKET )
- {
- //дождаться свободных подключений
- WaitForSingleObject( hsemFreeConnects, INFINITE );
- //создание сокета файлов клиента
- clSocks.clFileSock = accept( srvFileSock, NULL, NULL );
- //создать поток для обслуживания клиента и увеличить счетчик клиентов
- fact_param[ clientInd ] = clSocks;
- if ( fact_param[ clientInd ].clFileSock == INVALID_SOCKET )
- printf( "clFileSocket Is Invalid\n" );
- else
- {
- CreateThread( NULL, 0, ServiceClient, ( LPVOID )&fact_param[ clientInd ], 0, NULL );
- printf( "Client Connects. IP: %s\n", inet_ntoa( clLocAddr.sin_addr ) );
- ++clientInd;
- }
- }
- }
- //закрытие командного сокета сервера
- closesocket( srvCmdSock );
- closesocket( srvFileSock );
- }
- //----------------------------------------------------------------------------------------
Add Comment
Please, Sign In to add comment