Guest User

Untitled

a guest
Jul 19th, 2018
86
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 17.26 KB | None | 0 0
  1. // ftp_server.cpp : Defines the entry point for the console application.
  2.  
  3. #include "stdafx.h"
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <string.h>
  7. #include <WinSock2.h>
  8. #include <Windows.h>
  9.  
  10. #define MAX_FILE_BUFF_SIZE 4000 //максимальный размер буфера файлов
  11. #define MAX_COMMAND_BUFF_SIZE 300 //максимальный размер буфера команд
  12. #define MAX_FILE_THREADS 10 //максимальное количество одновременно передаваемых файлов
  13. #define QUEUE_SIZE 3 //размер очереди клиентов
  14. #define FILE_PORT 20 //номер порта для передачи файлов
  15. #define CMD_PORT 21 //номер порта для передачи сообщений
  16. #define CONTINUE "cont" //сигнал продолжать передачу (для ls)
  17. #define STOP "stop" //сигнал остановить передачу (для ls)
  18.  
  19. struct clientSocks //сокеты клиента
  20. {
  21. SOCKET clCmdSock; //сокет команд
  22. SOCKET clFileSock; //сокет файлов
  23. };
  24.  
  25. struct threadParam //структура параметра потоковой функции
  26. {
  27. clientSocks clSocks; //сокеты клиента
  28. char *str1;
  29. char *str2;
  30. };
  31.  
  32. clientSocks fact_param[ QUEUE_SIZE ]; //массив сокетов( нужен для параллельной обработки запросов клиентов)
  33. HANDLE hsemFreeConnects; //семафор свободных подключений для клиентов
  34. HANDLE hsemFileTh; //семафор потоков передачи файлов
  35. HANDLE hFileThreads[ MAX_FILE_THREADS ]; //поток передачи файла
  36. int clientInd = 0; //номер клиента
  37. int fileThInd = 0; //номер потока передачи файла
  38.  
  39. void InitSocketLib( void ); //инициализация библиотеки сокетов
  40. void CreateSocket( SOCKET* ); //создание сокета
  41. void BindSocket( SOCKET*, int ); //связывание сокета с локальным адресом
  42. void WaitForClientsRequests( void ); //ожидание запросов клиентов и их обработка
  43. void ReceiveCommand( SOCKET, char*, char* ); //получение команды от клиента
  44. void ChangeCurDirectory( SOCKET, char*, char* ); //смена текущей директории
  45. void SendDirList( SOCKET ); //отправка списка файлов и директорий
  46. void ListDirectory( SOCKET, char*, char* ); //просмотров файлов и директорий
  47. DWORD WINAPI SendFile( LPVOID ); //отправка файла
  48. DWORD WINAPI ServiceClient( LPVOID param ); //обслужить клиента в потоке
  49. //----------------------------------------------------------------------------------------
  50. int _tmain(int argc, _TCHAR* argv[])
  51. {
  52. printf( "FTP server has started\n" );
  53.  
  54. //создать семафоры
  55. hsemFreeConnects = CreateSemaphore( NULL, QUEUE_SIZE, QUEUE_SIZE, NULL );
  56. hsemFileTh = CreateSemaphore( NULL, MAX_FILE_THREADS, MAX_FILE_THREADS, NULL );
  57.  
  58. //инициализация винсоксов
  59. InitSocketLib();
  60.  
  61. //ожидание запросов клиентов и их обработка
  62. WaitForClientsRequests();
  63.  
  64. //деинициализация библиотеки сокетов
  65. WSACleanup();
  66. CloseHandle( hsemFreeConnects );
  67. printf( "FTP finished its work\n" );
  68. system( "pause" );
  69. return 0;
  70. }
  71. //----------------------------------------------------------------------------------------
  72. //инициализация библиотеки сокетов
  73. void InitSocketLib( void )
  74. {
  75. WSADATA wsData;
  76. //инициализация винсоксов
  77. if ( WSAStartup( MAKEWORD( 2, 2 ), &wsData ) )
  78. printf( "Initialization Error\n" );
  79. else printf( "Initialization Successful\n" );
  80. }
  81. //----------------------------------------------------------------------------------------
  82. //создание сокета
  83. void CreateSocket( SOCKET* p_sock )
  84. {
  85. //создание сокета
  86. *p_sock = socket( AF_INET, SOCK_STREAM, 0 );
  87. if ( *p_sock == INVALID_SOCKET )
  88. printf( "Socket Creation Failed\n" );
  89. else printf( "Socket Creation Successful\n" );
  90. }
  91. //----------------------------------------------------------------------------------------
  92. //связывание сокета с локальным адресом
  93. void BindSocket( SOCKET* p_sock, int portInd )
  94. {
  95. //задаем локальный адрес
  96. sockaddr_in servLocalAddr;
  97. servLocalAddr.sin_family = AF_INET;
  98. servLocalAddr.sin_port = htons( portInd );
  99. servLocalAddr.sin_addr.s_addr = INADDR_ANY;
  100.  
  101. //связваем сокет сервера с локальным адресом
  102. if ( bind( *p_sock, ( sockaddr* )&servLocalAddr, sizeof( servLocalAddr ) ) == SOCKET_ERROR )
  103. printf( "Binding Error Port %d\n", portInd );
  104. else printf( "Binding Successful Port %d\n", portInd );
  105. }
  106. //----------------------------------------------------------------------------------------
  107. //отправка файла
  108. DWORD WINAPI SendFile( LPVOID param )
  109. {
  110. threadParam thParam = *( ( threadParam* )param );
  111. clientSocks clSocks = thParam.clSocks;
  112. char* fileName = thParam.str1;
  113. char* curDir = thParam.str2;
  114.  
  115. FILE *fileToSend; //файл на отправку
  116. char bufCmd[ MAX_COMMAND_BUFF_SIZE ]; //буфер команд
  117. char bufFile[ MAX_FILE_BUFF_SIZE ]; //буфер файла
  118. __int64 fileSize = 0; //размер файла
  119.  
  120. //установить текущую директорию перед началом выполнения команды
  121. SetCurrentDirectoryA( ( LPCSTR )curDir );
  122.  
  123. //послать клиенту имя передаваемого файла
  124. send( clSocks.clCmdSock, fileName, MAX_COMMAND_BUFF_SIZE, 0 );
  125.  
  126. //если не удалось открыть файл - уведомить об этом клиента, иначе начать передачу
  127. if ( ( fileToSend = fopen( fileName, "rb" ) ) == NULL )
  128. {
  129. //уведомить клиента о том что файл невозможно отркыть
  130. strcpy( bufCmd, STOP );
  131. send( clSocks.clCmdSock, bufCmd, MAX_COMMAND_BUFF_SIZE, 0 );
  132. printf( "File %s Cann't Be Opened On The Server\n", fileName );
  133. }
  134. else
  135. {
  136. //уведомить клиента о том что файл отркыт
  137. strcpy( bufCmd, CONTINUE );
  138. send( clSocks.clCmdSock, bufCmd, MAX_COMMAND_BUFF_SIZE, 0 );
  139.  
  140. //получить и отправить клиенту размер файла
  141. fseek( fileToSend, 0, SEEK_END );
  142. fileSize = _ftelli64( fileToSend );
  143. fseek( fileToSend, 0, SEEK_SET );
  144. _i64toa( fileSize, bufCmd, 10 );
  145. send( clSocks.clCmdSock, bufCmd, MAX_COMMAND_BUFF_SIZE, 0 );
  146.  
  147. //пока не кончился файл его куски клиенту
  148. while ( !feof( fileToSend ) )
  149. {
  150. //считать из файла в буфер
  151. fread( bufFile, sizeof( char ), MAX_FILE_BUFF_SIZE, fileToSend );
  152. //отправить информацию
  153. send( clSocks.clFileSock, bufFile, MAX_FILE_BUFF_SIZE, 0 );
  154. }
  155. //освободить место для нового потока передачи файла
  156. ReleaseSemaphore( hsemFileTh, 1, NULL );
  157. fileThInd--;
  158. fclose( fileToSend );
  159. }
  160. return 0;
  161. }
  162. //----------------------------------------------------------------------------------------
  163. //получение команды от клиента
  164. void ReceiveCommand( SOCKET clCmdSock, char *cmdName, char *argument )
  165. {
  166. char *clCmdReceived = ( char* )calloc( MAX_COMMAND_BUFF_SIZE, sizeof( char ) ); //команда клиента
  167.  
  168. //принять команду от клиента
  169. recv( clCmdSock, clCmdReceived, MAX_COMMAND_BUFF_SIZE, 0 );
  170. //printf( "Command Recieve: %s\n", clCmdReceived );
  171. //получить командную часть
  172. *cmdName = clCmdReceived[ 0 ];
  173. //получить аргумент, если его нет - сделать строку аргумента пустой
  174. if ( strlen( clCmdReceived ) == 1 )
  175. strcpy( argument, "\0" );
  176. else if ( ( *cmdName ) != 'q' )
  177. {
  178. clCmdReceived += 2;
  179. strcpy( argument, clCmdReceived );
  180. }
  181. }
  182. //----------------------------------------------------------------------------------------
  183. //смена текущей директории
  184. void ChangeCurDirectory( SOCKET clCmdSocket, char *newDir, char* curDir )
  185. {
  186. //установить текущую директорию перед началом выполнения команды
  187. SetCurrentDirectoryA( ( LPCSTR )curDir );
  188.  
  189. //если смена удалась, послать ему директорию, иначе послать ошибку
  190. if ( SetCurrentDirectoryA( ( LPCSTR )newDir ) )
  191. {
  192. //получить текущую директорию
  193. GetCurrentDirectoryA( MAX_COMMAND_BUFF_SIZE, ( LPSTR )curDir );
  194. //послать её клиенту
  195. send( clCmdSocket, curDir, MAX_COMMAND_BUFF_SIZE, 0 );
  196. printf( "Current Directory Changed To: %s\n", curDir );
  197. }
  198. else
  199. {
  200. strcpy( newDir, "error" );
  201. send( clCmdSocket, newDir, MAX_COMMAND_BUFF_SIZE, 0 );
  202. printf( "Error changing directory\n" );
  203.  
  204. }
  205. }
  206. //----------------------------------------------------------------------------------------
  207. //просмотров файлов и директорий
  208. void ListDirectory( SOCKET clCmdSocket )
  209. {
  210. WIN32_FIND_DATAA findFileData; //информация о файле или директории
  211. HANDLE hFile; //хэндл файла
  212. char *mask = "\*"; //маска файлов и каталогов
  213. char bufName[ MAX_COMMAND_BUFF_SIZE ]; //буфер для передачи
  214. char bufSignal[ MAX_COMMAND_BUFF_SIZE ]; //буфер для передачи
  215.  
  216. //попытаться найти 1ый файл в текущей директории
  217. if ( ( hFile = FindFirstFileA( ( LPCSTR )mask, &findFileData ) ) == INVALID_HANDLE_VALUE )
  218. {
  219. //отправить клиенту сообщение об останове поиска
  220. strcpy( bufSignal , STOP );
  221. send( clCmdSocket, bufSignal, MAX_COMMAND_BUFF_SIZE, 0 );
  222. /*printf( "##Can't Find First File\n" );*/
  223. }
  224. else
  225. {
  226. //отправить клиенту сообщение о продолжении
  227. strcpy( bufSignal , CONTINUE );
  228. send( clCmdSocket, bufSignal, MAX_COMMAND_BUFF_SIZE, 0 );
  229.  
  230. //отправить клиенту имя файла/директории
  231. strcpy( bufName , findFileData.cFileName );
  232. send( clCmdSocket, bufName, MAX_COMMAND_BUFF_SIZE, 0 );
  233.  
  234. /*printf( "##\t%s\n", findFileData.cFileName );*/
  235. //пока не просмотрели весь каталог на наличие файлов и папок - просматриваем
  236. while ( FindNextFileA( hFile, &findFileData ) != 0 )
  237. {
  238. //отправить сообщение о продолжении передачи
  239. strcpy( bufSignal , CONTINUE );
  240. send( clCmdSocket, bufSignal, MAX_COMMAND_BUFF_SIZE, 0 );
  241.  
  242. //передать клиенту имя файла/каталога
  243. strcpy( bufName , findFileData.cFileName );
  244. send( clCmdSocket, bufName, MAX_COMMAND_BUFF_SIZE, 0 );
  245. /*printf( "##%s\n", findFileData.cFileName );*/
  246. }
  247. //уведомить клиента об окончании поиска в каталоге
  248. strcpy( bufSignal , STOP );
  249. send( clCmdSocket, bufSignal, MAX_COMMAND_BUFF_SIZE, 0 );
  250. }
  251.  
  252. //завершить поиск
  253. FindClose( hFile );
  254.  
  255. }
  256. //----------------------------------------------------------------------------------------
  257. //отправка списка файлов и директорий
  258. void SendDirList( SOCKET clCmdSocket, char* argument, char* curDir )
  259. {
  260. char bufSignal[ MAX_COMMAND_BUFF_SIZE ]; //буфер для передачи
  261.  
  262. //установить текущую директорию перед началом выполнения команды
  263. SetCurrentDirectoryA( ( LPCSTR )curDir );
  264.  
  265. //если аргумент отсутствует - отправить список файлов и папок текущего каталога
  266. if ( strcmp( argument, "\0" ) == 0 )
  267. ListDirectory( clCmdSocket );
  268. else
  269. {
  270. //установить текущей директорию аргумента
  271. //если установка удачна - то отправить клиенту список
  272. if ( SetCurrentDirectoryA( ( LPCSTR )argument ) )
  273. ListDirectory( clCmdSocket );
  274. else
  275. {
  276. //отправить клиенту сообщение об останове поиска
  277. strcpy( bufSignal , STOP );
  278. send( clCmdSocket, bufSignal, MAX_COMMAND_BUFF_SIZE, 0 );
  279. }
  280.  
  281. //восстановить текущую директорию
  282. SetCurrentDirectoryA( ( LPCSTR )curDir );
  283. }
  284. }
  285. //----------------------------------------------------------------------------------------
  286. //обслуживание клиента
  287. DWORD WINAPI ServiceClient( LPVOID param )
  288. {
  289. clientSocks curClSocks = *( ( clientSocks* )param ); //сокеты клиента
  290.  
  291. char curCmd = '\0'; //текущая команда
  292. char cmdArgument[ MAX_COMMAND_BUFF_SIZE ]; //аргумент команды
  293. char currentDir[ MAX_COMMAND_BUFF_SIZE ]; //текущая директория
  294.  
  295. threadParam clThreadParam; //параметр для потоковых функций
  296. clThreadParam.clSocks = curClSocks;
  297.  
  298. //установить начальную директорию в "c:\" и отправить клиенту
  299. strcpy( currentDir, "c:\\" );
  300. SetCurrentDirectoryA( ( LPCSTR )currentDir );
  301. send( curClSocks.clCmdSock, currentDir, MAX_COMMAND_BUFF_SIZE, 0 );
  302.  
  303. do
  304. {
  305. //получить от клиента команду и имя файла
  306. ReceiveCommand( curClSocks.clCmdSock, &curCmd, cmdArgument );
  307. //в зависимости от команды выполнить действие
  308. //отправить файл
  309. switch ( curCmd )
  310. {
  311. case 'g':
  312. {
  313. //дожидаемся возможности скачать файл
  314. WaitForSingleObject( hsemFileTh, INFINITE );
  315. //запуск передачи файла в потоке
  316. clThreadParam.str1 = cmdArgument;
  317. clThreadParam.str2 = currentDir;
  318. hFileThreads[ fileThInd ] = CreateThread( NULL, 0, SendFile, ( LPVOID )&clThreadParam, 0, NULL );
  319. fileThInd++;
  320. break;
  321. }
  322. case 'c': ChangeCurDirectory( curClSocks.clCmdSock, cmdArgument, currentDir );
  323. break;
  324. case 'l': SendDirList( curClSocks.clCmdSock, cmdArgument, currentDir );
  325. break;
  326. case 'q': printf( "Client Was Disconnected\n" );
  327. break;
  328. default: //printf( "Unindefinied Command\n" );
  329. break;
  330. }
  331. } while ( curCmd != 'q' );
  332.  
  333. //закрытие сокетов и хэндлов потоков
  334. closesocket( curClSocks.clCmdSock );
  335. closesocket( curClSocks.clFileSock );
  336. WaitForMultipleObjects( MAX_FILE_THREADS, hFileThreads, true, INFINITE );
  337. for ( int i = 0; i < MAX_FILE_THREADS; i++ )
  338. CloseHandle( hFileThreads[ i ] );
  339. //освободить подключение
  340. ReleaseSemaphore( hsemFreeConnects, 1, NULL );
  341. --clientInd;
  342.  
  343. return 0;
  344. }
  345. //----------------------------------------------------------------------------------------
  346. //ожидание запросов клиентов и их обработка
  347. void WaitForClientsRequests( void )
  348. {
  349. //подготовка сокетов команд и файлов к работе
  350. SOCKET srvCmdSock; //сокет сервера команд
  351. SOCKET srvFileSock; //сокет сервера файлов
  352. sockaddr_in clLocAddr; //локальный адрес подключившегося клиента
  353.  
  354. CreateSocket( &srvCmdSock );
  355. BindSocket( &srvCmdSock, CMD_PORT );
  356.  
  357. CreateSocket( &srvFileSock );
  358. BindSocket( &srvFileSock, FILE_PORT );
  359.  
  360. //перевести сервера команд и файлов в режим ожидания подключений
  361. listen( srvCmdSock, QUEUE_SIZE );
  362. listen( srvFileSock, QUEUE_SIZE );
  363.  
  364. clientSocks clSocks; //сокеты клиента
  365.  
  366. //извлечение запросов клиентов из очереди на подключение
  367. printf( "Waiting for connections...\n" );
  368. while ( ( clSocks.clCmdSock = accept( srvCmdSock, ( sockaddr* )&clLocAddr, NULL ) ) )
  369. {
  370. if ( clSocks.clCmdSock != INVALID_SOCKET )
  371. {
  372. //дождаться свободных подключений
  373. WaitForSingleObject( hsemFreeConnects, INFINITE );
  374.  
  375. //создание сокета файлов клиента
  376. clSocks.clFileSock = accept( srvFileSock, NULL, NULL );
  377.  
  378. //создать поток для обслуживания клиента и увеличить счетчик клиентов
  379. fact_param[ clientInd ] = clSocks;
  380. if ( fact_param[ clientInd ].clFileSock == INVALID_SOCKET )
  381. printf( "clFileSocket Is Invalid\n" );
  382. else
  383. {
  384. CreateThread( NULL, 0, ServiceClient, ( LPVOID )&fact_param[ clientInd ], 0, NULL );
  385. printf( "Client Connects. IP: %s\n", inet_ntoa( clLocAddr.sin_addr ) );
  386. ++clientInd;
  387. }
  388. }
  389. }
  390.  
  391. //закрытие командного сокета сервера
  392. closesocket( srvCmdSock );
  393. closesocket( srvFileSock );
  394. }
  395. //----------------------------------------------------------------------------------------
Add Comment
Please, Sign In to add comment