Last active
November 16, 2017 15:05
-
-
Save yongboy/37875ad2476b0a8cab90 to your computer and use it in GitHub Desktop.
可绑定指定IP地址和端口的示范HTTP服务器程序,仅仅作为示范。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* One demo http server, just for test! | |
* how to compile it: | |
* gcc server_v2.c -o server_v2 /usr/local/lib/libev.a -lm | |
* how to run it: | |
* ./server_v2 -p port_num -i the_bind_ip | |
* default port is 8000 | |
* default ip is all of the local ip | |
* eg: | |
* ./server_v2 -p 9000 -i 192.168.192.130 | |
* | |
* nieyong@2014-06-24 youku.com | |
* | |
* */ | |
#include <arpa/inet.h> | |
#include <stdlib.h> | |
#include <stdio.h> | |
#include <string.h> | |
#include <fcntl.h> | |
#include <errno.h> | |
#include <err.h> | |
#include <unistd.h> | |
#include "/usr/local/include/ev.h" | |
#define HTMLFILE_RESPONSE_HEADER \ | |
"HTTP/1.1 200 OK\r\n" \ | |
"Connection: keep-alive\r\n" \ | |
"Content-Type: text/html; charset=utf-8\r\n" \ | |
"Transfer-Encoding: chunked\r\n" \ | |
"\r\n" | |
#define HTMLFILE_RESPONSE_FIRST \ | |
"<html><head><title>htmlfile chunked example</title><script>var _ = function (msg) { document.getElementById('div').innerHTML = msg; };</script></head><body><div id=\"div\"></div> " | |
static int server_port = 8000; | |
static unsigned long int server_ip = 0; | |
struct ev_loop *loop; | |
typedef struct { | |
int fd; | |
ev_io ev_read; | |
} client_t; | |
ev_io ev_accept; | |
static int usr_num; | |
static void incr_usr_num() { | |
usr_num ++; | |
printf("online user %d\n", usr_num); | |
} | |
static void dec_usr_num() { | |
usr_num --; | |
printf("~online user %d\n", usr_num); | |
} | |
static void free_res(struct ev_loop *loop, ev_io *ws); | |
int setnonblock(int fd) { | |
int flags = fcntl(fd, F_GETFL); | |
if (flags < 0) | |
return flags; | |
flags |= O_NONBLOCK; | |
if (fcntl(fd, F_SETFL, flags) < 0) | |
return -1; | |
return 0; | |
} | |
static int format_message(const char *ori_message, char *target_message) { | |
return sprintf(target_message, "%X\r\n<script>_('%s');</script>\r\n", ((int)strlen(ori_message) + 23), ori_message); | |
} | |
static void write_ori(client_t *client, char *msg) { | |
if (client == NULL) { | |
fprintf(stderr, "the client is NULL !\n"); | |
return; | |
} | |
write(client->fd, msg, strlen(msg)); | |
} | |
static void write_body(client_t *client, char *msg) { | |
char body_msg[strlen(msg) + 100]; | |
format_message(msg, body_msg); | |
write_ori(client, body_msg); | |
} | |
static void read_cb(struct ev_loop *loop, ev_io *w, int revents) { | |
client_t *client = w->data; | |
int r = 0; | |
char rbuff[1024]; | |
if (revents & EV_READ) { | |
r = read(client->fd, &rbuff, 1024); | |
} | |
if (EV_ERROR & revents) { | |
fprintf(stderr, "error event in read\n"); | |
free_res(loop, w); | |
return ; | |
} | |
if (r < 0) { | |
fprintf(stderr, "read error\n"); | |
ev_io_stop(EV_A_ w); | |
free_res(loop, w); | |
return; | |
} | |
if (r == 0) { | |
fprintf(stderr, "client disconnected.\n"); | |
ev_io_stop(EV_A_ w); | |
free_res(loop, w); | |
return; | |
} | |
write_ori(client, HTMLFILE_RESPONSE_HEADER); | |
char target_message[strlen(HTMLFILE_RESPONSE_FIRST) + 20]; | |
sprintf(target_message, "%X\r\n%s\r\n", (int)strlen(HTMLFILE_RESPONSE_FIRST), HTMLFILE_RESPONSE_FIRST); | |
write_ori(client, target_message); | |
incr_usr_num(); | |
} | |
static void accept_cb(struct ev_loop *loop, ev_io *w, int revents) { | |
struct sockaddr_in client_addr; | |
socklen_t client_len = sizeof(client_addr); | |
int client_fd = accept(w->fd, (struct sockaddr *) &client_addr, &client_len); | |
if (client_fd == -1) { | |
fprintf(stderr, "the client_fd is NULL !\n"); | |
return; | |
} | |
client_t *client = malloc(sizeof(client_t)); | |
client->fd = client_fd; | |
if (setnonblock(client->fd) < 0) | |
err(1, "failed to set client socket to non-blocking"); | |
client->ev_read.data = client; | |
ev_io_init(&client->ev_read, read_cb, client->fd, EV_READ); | |
ev_io_start(loop, &client->ev_read); | |
} | |
void display_usage(void){ | |
printf("Usage: server_v2 -p your_port -i your_bind_ip\neg:server_v2 -p 9000 -i 192.168.192.130\n"); | |
exit(EXIT_FAILURE); | |
} | |
int main(int argc, char const *argv[]) { | |
int ch; | |
while ((ch = getopt(argc, argv, "p:i:")) != -1) { | |
switch (ch) { | |
case 'p': | |
server_port = atoi(optarg); | |
break; | |
case 'i': | |
server_ip = inet_addr(optarg); | |
break; | |
case '?': | |
display_usage(); | |
break; | |
} | |
} | |
loop = ev_default_loop(0); | |
struct sockaddr_in listen_addr; | |
int reuseaddr_on = 1; | |
int listen_fd = socket(AF_INET, SOCK_STREAM, 0); | |
if (listen_fd < 0) | |
err(1, "listen failed"); | |
// reuse the (IP:PORT) addr | |
if (setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr_on, sizeof(reuseaddr_on)) == -1) | |
err(1, "setsockopt failed"); | |
memset(&listen_addr, 0, sizeof(listen_addr)); | |
listen_addr.sin_family = AF_INET; | |
if(server_ip != 0){ | |
listen_addr.sin_addr.s_addr = server_ip; | |
}else{ | |
listen_addr.sin_addr.s_addr = INADDR_ANY; | |
} | |
listen_addr.sin_port = htons(server_port); | |
if (bind(listen_fd, (struct sockaddr *) &listen_addr, sizeof(listen_addr)) < 0) | |
err(1, "bind failed"); | |
if (listen(listen_fd, 5) < 0) | |
err(1, "listen failed"); | |
if (setnonblock(listen_fd) < 0) | |
err(1, "failed to set server socket to non-blocking"); | |
ev_io_init(&ev_accept, accept_cb, listen_fd, EV_READ); | |
ev_io_start(loop, &ev_accept); | |
ev_loop(loop, 0); | |
return 0; | |
} | |
static void free_res(struct ev_loop *loop, ev_io *w) { | |
dec_usr_num(); | |
client_t *client = w->data; | |
if (client == NULL) { | |
fprintf(stderr, "the client is NULL !!!!!!"); | |
return; | |
} | |
ev_io_stop(loop, &client->ev_read); | |
close(client->fd); | |
free(client); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment