Цитата
А мы уверены, что все клиенты параллельно качают файл размером гигабайт с сервера? А некоторые при этом ждут чего-то иногда?
Ну так сервак на JS показывает количество коннектов в секунду - это количество скачанных файлов в секунду. Правда не гиговых, а нормальных. Можете сами проверить на гиговом, если есть желание.
И однопоточный JS клиент может скачать 10000 файлов в секунду, при этом система нормально работает, памяти полно свободной, торохтит только диск и нельзя создать сокет - закончились лимиты ядра. А многопоточный на С смог скачать только 600-700 файлов на том же компе - то есть в 16 раз меньше. При этом убил всю систему, остановить его можно только кнопкой reset, мышь и клава да и вся система зависает намертво.
Делаю аналогичный многопоточный сервер, посмотрим что сможет выдержать он.
Цитата
Не думаю, что стоит изобретать велосипед на Си. Достаточно испробовать что-то серийное и прверенное.
Тут даже сравнивать нечего. Сервак на NodeJS убивает любой другой многопоточный, а тормознутого Апача десять раз убивает. Он может только конкурировать с серваком на Go, при одних задачах лучше NodeJS, при других - Go. Я работал и на том и на другом.
И так, осилил сервак, отловил баги, даже отладчик пришлось запускать

Как просто на С наделать ошибок..
С серваком не все так плохо, как я думал. Хоть он потребляет и больше ресурсов(где-то на 100мб больше памяти, чем JS), чем такой сервак на JS, но и способен обрабатывать порядка 7000 коннектов(против 10-11 тыс JS) в секунду и примерно столько же сокетов, сколько и JS.
Думал почему, дебажил, и наконец понял - Коннекты принимаются в одном потоке - в самом низу функции main() в вечном цикле accept() блокирует и принимает коннекты, а их обработка происходит уже в порожденных потоках. Порожденный поток успевает быстро отдать файл и выходит, получается, одновременно создано потоков примерно штук 40.
У клиента же все по другому - коннекты происходят в потоках и там на каждый коннект создается 1 пток.
Но это простая ситуация - отдача файла, который ОС положила в кеш. Попробую на днях услижнить задачу, чтобы ос отдавала явные данные, например рандом, чтобы потоку нужно было потрудиться/подождать. Думаю тогда сервак на C станет таким же тормознутым, как и клиент и сожрет всю оперативку, в то время как JS будет запросто работать.
По количеству строк JS тоже ессно выиграл 67 строк в JS против 151 на C. Асинхронное неблокирующее программирование проще и эффективное, чем многопоточное блокирующее.
Интересно такой же многопоточный сервак реализовать на чем-то аналогичном динамическом, типа Java. Думаю он загнется при сотне клиентов. А то силы не равны получаются, быстрый С против медленного JS, но последний все равно выиграл из за своей асинхронной натуры.
CODE
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <netdb.h>
#include <netinet/in.h>
#include <unistd.h>
#include <pthread.h>
long n_connections = 0;
long total_connections = 0;
pthread_mutex_t g_mutex;
void* Thread_connection(void* fd1){
long sock = (long)fd1;
// work with shared vars
pthread_mutex_lock(&g_mutex);
n_connections++;
total_connections++;
pthread_mutex_unlock(&g_mutex);
// ------------------
FILE* fd = fopen("file.txt","r");
if(fd==0){
perror("fopen");
goto exit1;
}
// read and send file - blocking!
char buffer[4096];
while(1){
// read chunk
size_t r = fread(buffer, 1, sizeof(buffer), fd);
if(r<=0)break;
// buffer[r]=0;
//printf("%s\n", buffer);
// send
r = send(sock, buffer, r, 0);
if(r<=0){
perror("Thread_connection: socket error");
break;
}
};
fclose(fd);
exit1:
// fuck...
pthread_mutex_lock(&g_mutex);
n_connections--;
pthread_mutex_unlock(&g_mutex);
// ------
close(sock);
pthread_exit(NULL);
return 0;
}
void* Thread_watcher(void* thr){
double tput = 0;
const double a = 0.9;
long prev_connections = 0;
while(1){
// work with shared vars
pthread_mutex_lock(&g_mutex);
long tcon = total_connections;
total_connections = 0;
prev_connections = n_connections;
pthread_mutex_unlock(&g_mutex);
// ----------------
// low-pass filter
tput = tput*(1-a) + tcon*a;
if(tput>=1){
printf("now approx %ld clients are connected. Tput: %.1f conn/s\n",
prev_connections, tput);
}
sleep(1);
}
return 0;
}
void startWatcher(){
// create watcher thread
pthread_t thr;
int rc = pthread_create(&thr, 0, Thread_watcher, (void*)0);
if(rc){
printf("startWatcher ERROR; return code from pthread_create() is %d\n", rc);
exit(-1);
}
}
int main(){
int port = 5555;
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("ERROR opening socket");
exit(1);
}
// Initialize socket structure
struct sockaddr_in serv_addr;
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(port);
// Now bind the host address using bind() call.
if(bind(sockfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) < 0) {
perror("ERROR on binding");
exit(1);
}
// listen and accept connections in endless loop
listen(sockfd, 10000);
// create fucking mutexes
pthread_mutex_init(&g_mutex, NULL);
startWatcher();
// accept connections in endless loop
struct sockaddr_in cli_addr;
unsigned clilen = sizeof(cli_addr);
while(1){
// Accept actual connection from the client - blocking
long newsockfd = accept(sockfd, (struct sockaddr *)&cli_addr, &clilen);
if(newsockfd < 0) {
perror("ERROR on accept");
exit(1);
}
// create thread for each connection
pthread_t thr;
int rc = pthread_create(&thr, 0, Thread_connection, (void*)newsockfd);
if(rc){
printf("ERROR; return code from pthread_create() is %d\n", rc);
exit(-1);
}
pthread_detach(thr);
}
return 0;
}
Цитата
Жаль, что памяти таки JS потребовалось больше даже в сравнении с апачем ))))
У меня node сожрал 160 метров. А у них 1.5 гига. Что-то у них не то с тестами. Будет время я сам прогоню апач и node, сравним с результатами, доступными в интернете.
Но все равно Node у них показал производительность в 6 раз больше апача.
Ну пусть PHP будет, так Js тоже полностью динамический и тяжелый язык, такой же тяжелый, как и PHP. Только PHP синхронный блокирующий, а Node асинхронный неблокирующий - на этом принципе и построен SST.
А если я запущу асинхронный сервак помимо сокетов линукса (есть для этого готовые библиотеки), то он уделает любой другой раз в 100-200 и даже в 1000. Посмотрите сканер портов Mass-scan - он способен "расплавить" любую сетевую карту - работает помимо сокетов, всего 2 потока(ридер и райтер) и полностью асинхронный
https://github.com/robertdavidgraham/masscanТа даже NodeJS на моем ноуте обгоняет сам линукс - у node еще куча запасов, а linux уже не тянет..