МИНИСТЕРСТВО ОБРАЗОВАНИЯ И НАУКИ РОССИЙСКОЙ ФЕДЕРАЦИИ Федеральное государственное автономное образовательное учреждение высшего профессионального образования НАЦИОНАЛЬНЫЙ ИССЛЕДОВАТЕЛЬСКИЙ ЯДЕРНЫЙ УНИВЕРСИТЕТ "МИФИ" Димитровградский инженерно-технологический институт - филиал НИЯУ МИФИ Отчет по практической работе №3 по дисциплине "Технологии программирования в сетях" Выполнил: студент группы ВТ-51 Улейкин Е.Ю. Проверил: ст. преподаватель кафедры ИТ Аленин А. А. Димитровград, 2012 Задание Разработать две клиент-серверные пары для передачи файла с сервера клиенту без использования системного вызова sendfile и с ним. Оценить временные характеристики их работы с использованием системных вызовов gettimeofday и times Программа клиент из параметров командной строки узнает адрес сервера и имя файла для чтения, передает имя файла серверу, а затем читает данные из сокета в память без записи на диск и завершает работу. Программа сервер выполняет следующие действия: * Ожидает подключение клиента. * После подключения ожидает пока клиент не передаст имя файла. * Передает запрошенный файл клиенту с помощью sendfile. * Завершает соединение, печатает временные характеристики сеанса обмена и переходит в ожидание следующего подключения. Программа "server" ожидает подключений на порту с номером из лабораторных работ 3-4. Размер файла для передачи желательно взять значительным. По завершении сеанса связи программа server остается работать, в результате вы опять сможете подключиться к ней и запросить другой файл. Если программе передать в качестве имени файла слово "quit", она должна завершит свою работу. Если в вашем распоряжении есть две машины, объединенные сетью, то можете попробовать выполнить передачу файлов через сеть. Теоретическая часть Приложения-серверы, такие как web-серверы, тратят огромное количество времени на передачу файлов, хранящихся на жестком диске, клиентам, работающим с сервером через web-браузер. Простой алгоритм передачи данных может выглядеть примерно так: открыть исходный файл (на диске) открыть файл назначения (сетевое соединение) пока файл не передан: прочитать блок данных из исходного файла в буфер записать данные из буфера в файл назначения закрыть оба файла Процедуры чтения и записи данных обычно используют системные вызовы read и write, соответственно, либо библиотечные функции, которые являются своего рода "обертками" для этих системных вызовов. Если следовать вышеприведенному алгоритму, то получается так, что данные копируются несколько раз, прежде чем они "уйдут" в сеть. Каждый раз, когда вызывается read, данные копируются с жесткого диска в буфер ядра (обычно посредством DMA). Затем буфер копируется в буфер приложения. Затем вызывается write и данные из буфера приложения опять копируются в буфер ядра и лишь потом этот буфер отправляется в сеть. Каждый раз, когда приложение обращается к системному вызову, происходит переключение контекста между пользовательским режимом и режимом ядра, а это весьма "дорогостоящая" операция. И чем больше в программе будет обращений к системным вызовам read и write, тем больше времени будет потрачено на выполнение переключений контекста исполнения. Операции копирования данных из области ядра в область приложения и обратно, в данном случае, излишни, поскольку сами данные в приложении не изменяются и не анализируются. Многие операционные системы, такие как Windows NT, FreeBSD и Solaris предоставляют в распоряжение программиста системный вызов, который выполняет передачу файла за одно обращение. Ранние версии Linux часто критиковали за отсутствие подобной возможности, в результате, начиная с версии 2.2.x, такой вызов появился. Теперь он широко используется такими серверными приложениями, как Apache и Samba для ускорения обслуживания большого количества клиентов. Реализация sendfile различна для разных операционных систем. Поэтому, в данной статье мы будем говорить о версии sendfile в Linux. Обратите внимание: утилита sendfile не то же самое, что системный вызов sendfile. Вызов sendfile Для его использования необходимо подключить заголовочный файл <sys/sendfile.h>, в котором находится описание прототипа функции-вызова: ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count); Функция принимает следующие входные параметры: out_fd - файловый дескриптор файла назначения, открытого на запись. В этот файл производится запись данных in_fd - файловый дескриптор исходного файла, открытого на чтение. Из этого файла читаются данные offset - смещение от начала исходного файла, с этой точки будет начата передача данных (т.е. значение 0 соответствует началу файла). Это значение изменяется в процессе работы функции и ваше приложение получит его в измененном виде после того, как функция вернет управление. count - количество байт, которое необходимо передать В случае успеха функция возвращает количество переданных байт, и -1 -- в случае ошибки. В Linux файловый дескриптор может соответствовать как обычному файлу так и устройству, например -- сокету. На сегодняшний день, реализация sendfile требует, чтобы исходный файловый дескриптор соответствовал обычному файлу или устройству, поддерживаемому mmap. Это означает, например, что исходный файл не может быть сокетом. Файл назначения может быть и сокетом, и это обстоятельство широко используется приложениями. Функция gettimeofday - получает текущее время. Описание array gettimeofday (void) Это интерфейс для gettimeofday(2). Она возвращает ассоциативный массив, содержащий данные, возвращённые системным вызовом. "sec" - секунды "usec" - микросекунды "minuteswest" - минуты к западу от Greenwich "dsttime" - тип dst-коррекции Программа "Клиент": #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/time.h> #define fatal(x) { perror(x); exit(1); } main(int argc, char *argv[]){ int s, sz, i; struct sockaddr_in ssa; struct sockaddr *sp; struct hostent *he; in_addr_t sip; int block_size=1024; char buf[1024]; struct timeval tv1,tv2; sz = sizeof(ssa); if (argc != 3) { fprintf(stderr,"Use: %s host file\n", argv[0]); exit(1); } if ((sip = inet_addr(argv[1])) == -1) { if ((he = gethostbyname(argv[1])) == NULL) { fprintf(stderr,"Unknown host: %s\n", argv[1]); exit(1); } sip = *(int *)he->h_addr_list[0]; } s = socket(AF_INET, SOCK_STREAM, 0); if (s == -1) fatal("Create socket fatal"); sp = (struct sockaddr *)&ssa; ssa.sin_family = AF_INET; ssa.sin_addr.s_addr = INADDR_ANY; if (bind(s, sp, sizeof(ssa)) == -1) fatal("Port already used"); ssa.sin_family = AF_INET; ssa.sin_port = htons(1234); ssa.sin_addr.s_addr = sip; if (connect(s, &ssa, sz) == -1) fatal("Connection fatal"); send(s,argv[2],strlen(argv[2]),0); gettimeofday(&tv1,NULL); while (recv(s, buf, block_size, 0) > 0); gettimeofday(&tv2,NULL); close(s); printf("%s\n",buf); } Программа "Сервер1": #include <sys/socket.h> #include <sys/sendfile.h> #include <netinet/in.h> #include <netdb.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/time.h> #define fatal(x) { perror(x); exit(1); } main() { int s, c, sz, size, i,src; struct sockaddr_in ssa, csa; struct sockaddr *sp, *cp; char buf[1024]; int f=1; int offset=0; struct stat stat_buf; int block_size=1024; struct timeval tv1,tv2; sp = (struct sockaddr *)&ssa; cp = (struct sockaddr *)&csa; s = socket(AF_INET, SOCK_STREAM, 0); if (s == -1) fatal("Create socket fatal"); ssa.sin_family = AF_INET; ssa.sin_port = htons(1234); ssa.sin_addr.s_addr = INADDR_ANY; if (bind(s, sp, sizeof(ssa)) == -1) fatal("Port already used"); if (listen(s, 0) == -1) fatal("Listen fatal"); while (1) { sz = sizeof(csa); if ((c = accept(s, cp, &sz)) == -1) fatal("Accept socket fatal"); size=recv(c, buf, 100, 0);buf[size]=0; if (strcmp(buf,"quit")==0){close(c);exit(0);} src=open(buf,O_RDONLY); if (src<0) fatal("Can't open file!!!") fstat(src,&stat_buf); f = stat_buf.st_size/block_size; if(stat_buf.st_size % block_size!=0) f++; gettimeofday(&tv1,NULL); while (f>0) { i = read(src,buf,block_size); size=send(c, buf, i, 0); if (size<0) fatal("Send file fatal"); f--; } gettimeofday(&tv2,NULL); close(c); close(src); printf("%d\n",(tv2.tv_usec-tv1.tv_usec)); } } Результат: Client vm:~# c 127.0.0.1 file1 Hello People!!!!! admsmda sd'as;dlasdl;as;dl as/d,./as,d.,mas,.dfm,.asmfksd asdsa d asd asd as Server1 vm:~# s1 66 276 41 48 40 41 59 64 80 Программа "Сервер2": #include <sys/socket.h> #include <sys/sendfile.h> #include <netinet/in.h> #include <netdb.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/time.h> #define fatal(x) { perror(x); exit(1); } main() { int s, c, sz, size, i,src; struct sockaddr_in ssa, csa; struct sockaddr *sp, *cp; char buf[1024]; int f=1; int offset=0; struct stat stat_buf; int block_size=1024; struct timeval tv1,tv2; sp = (struct sockaddr *)&ssa; cp = (struct sockaddr *)&csa; s = socket(AF_INET, SOCK_STREAM, 0); if (s == -1) fatal("Create socket fatal"); ssa.sin_family = AF_INET; ssa.sin_port = htons(1234); ssa.sin_addr.s_addr = INADDR_ANY; if (bind(s, sp, sizeof(ssa)) == -1) fatal("Port already used"); if (listen(s, 0) == -1) fatal("Listen fatal"); while (1) { sz = sizeof(csa); if ((c = accept(s, cp, &sz)) == -1) fatal("Accept socket fatal"); size=recv(c, buf, 100, 0);buf[size]=0; if (strcmp(buf,"quit")==0){close(c);exit(0);} src=open(buf,O_RDONLY); if (src<0) fatal("Can't open file!!!") fstat(src,&stat_buf); f = stat_buf.st_size/block_size; if(stat_buf.st_size % block_size!=0) f++; gettimeofday(&tv1,NULL); while (f>0) { i = read(src,buf,block_size); // size=send(c, buf, i, 0); off_t offset = 0; sendfile(c,src,&offset,stat_buf.st_size); if (size<0) fatal("Send file fatal"); f--; } gettimeofday(&tv2,NULL); close(c); close(src); printf("%d\n",tv2.tv_usec-tv1.tv_usec); } } Результат: Server2 vm:~# s2 57 59 42 62 53 53 55 49 46 50 81 В результате сравнения оказалось, что передача данных с помощью системного вызова sendfile происходит быстрее, чем с использованием функции send.
1/--страниц