Last active
June 4, 2017 04:50
-
-
Save yanxurui/50c3d38a9f92391d143df5f6a417677c to your computer and use it in GitHub Desktop.
chat room example based on libev
This file contains 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
#include <stdio.h> | |
#include <stdlib.h> | |
#include <unistd.h> | |
#include <string.h> | |
#include <netdb.h> | |
#include <netinet/in.h> | |
#include <ev.h> | |
#define BUFFER_SIZE 1024 | |
char buffer[BUFFER_SIZE] = ""; | |
int sd; | |
void stdin_cb(struct ev_loop *loop, ev_io *w, int revents); | |
void recv_cb(EV_P_ ev_io *w, int revents); | |
int main(int argc, char *argv[]) | |
{ | |
struct ev_loop *loop = EV_DEFAULT; | |
struct sockaddr_in addr; | |
ev_io stdin_watcher, recv_watcher; | |
if (argc < 3) { | |
fprintf(stderr,"usage %s hostname port\n", argv[0]); | |
exit(0); | |
} | |
// Create client socket | |
if( (sd = socket(PF_INET, SOCK_STREAM, 0)) < 0 ) | |
{ | |
perror("socket error"); | |
return -1; | |
} | |
bzero(&addr, sizeof(addr)); | |
struct hostent *server = gethostbyname(argv[1]); | |
if (server == NULL) { | |
perror("ERROR, no such host\n"); | |
return -1; | |
} | |
addr.sin_family = AF_INET; | |
bcopy((char *)server->h_addr, (char *)&addr.sin_addr.s_addr, server->h_length); | |
addr.sin_port = htons(atoi(argv[2])); | |
// Connect to server socket | |
if(connect(sd, (struct sockaddr *)&addr, sizeof addr) < 0) | |
{ | |
perror("Connect error"); | |
return -1; | |
} | |
printf("connected successfully!\n"); | |
ev_io_init(&stdin_watcher, stdin_cb, /*STDIN_FILENO*/ 0, EV_READ); | |
ev_io_start(loop, &stdin_watcher); | |
ev_io_init(&recv_watcher, recv_cb, sd, EV_READ); | |
ev_io_start(loop, &recv_watcher); | |
// It will ask the operating system for any new events, | |
// call the watcher callbacks, and then repeat the whole process indefinitely | |
ev_run(loop, 0); | |
return 0; | |
} | |
// Read input from user and send message to the server | |
void stdin_cb(struct ev_loop *loop, ev_io *w, int revents) | |
{ | |
fgets(buffer, sizeof(buffer), stdin); | |
if(strcmp(buffer, "quit\n") == 0) { | |
// this causes all nested ev_run's to stop iterating | |
ev_break (EV_A_ EVBREAK_ALL); | |
close(sd); | |
} | |
else { | |
send(sd, buffer, strlen(buffer), 0); | |
} | |
} | |
// Receive message from the server | |
void recv_cb(EV_P_ ev_io *w, int revents) | |
{ | |
ssize_t read; | |
if(EV_ERROR & revents) | |
{ | |
perror("got invalid event"); | |
return; | |
} | |
read = recv(w->fd, buffer, BUFFER_SIZE, 0); | |
if(read < 0) | |
{ | |
perror("read error"); | |
} | |
else if(read == 0) | |
{ | |
// it will result in dead loop if we don't stop ev loop | |
ev_break (EV_A_ EVBREAK_ALL); | |
close(sd); | |
perror("server might close"); | |
} | |
else { | |
printf(buffer); | |
} | |
} |
This file contains 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
#include <stdio.h> | |
#include <stdlib.h> | |
#include <unistd.h> | |
#include <string.h> | |
#include <netinet/in.h> | |
#include <arpa/inet.h> | |
#include <ev.h> | |
#include <assert.h> | |
#define BUFFER_SIZE 1024 | |
#define MAX_CLIENT 1000 | |
int online_clients = 0; // Total number of connected clients | |
char* clients[MAX_CLIENT] = {NULL}; | |
void accept_cb(struct ev_loop *loop, struct ev_io *watcher, int revents); | |
void read_cb(struct ev_loop *loop, struct ev_io *watcher, int revents); | |
int main(int argc, char *argv[]) | |
{ | |
struct ev_loop *loop = ev_default_loop(0); | |
int sd; | |
struct sockaddr_in addr; | |
int addr_len = sizeof(addr); | |
struct ev_io w_accept; | |
if (argc < 2) { | |
fprintf(stderr,"usage %s port\n", argv[0]); | |
exit(0); | |
} | |
// Create server socket | |
if( (sd = socket(PF_INET, SOCK_STREAM, 0)) < 0 ) | |
{ | |
perror("socket error"); | |
return -1; | |
} | |
/* | |
* setsockopt: Handy debugging trick that lets | |
* us rerun the server immediately after we kill it; | |
* otherwise we have to wait about 20 secs. | |
* Eliminates "ERROR on binding: Address already in use" error. | |
* The SO_REUSEADDR is for when the socket bound to an address has already been closed, | |
* the same address (ip-address/port pair) can be used again directly. | |
*/ | |
int optval = 1; | |
setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, | |
(const void *)&optval , sizeof(int)); | |
bzero(&addr, sizeof(addr)); | |
addr.sin_family = AF_INET; | |
addr.sin_port = htons(atoi(argv[1])); | |
addr.sin_addr.s_addr = INADDR_ANY; | |
// Bind socket to address | |
if (bind(sd, (struct sockaddr*) &addr, sizeof(addr)) != 0) | |
{ | |
perror("bind error"); | |
return -1; | |
} | |
// Start listing on the socket | |
if (listen(sd, 2) < 0) | |
{ | |
perror("listen error"); | |
return -1; | |
} | |
// Initialize and start a watcher to accepts client requests | |
ev_io_init(&w_accept, accept_cb, sd, EV_READ); | |
ev_io_start(loop, &w_accept); | |
// Start loop | |
ev_run(loop, 0); | |
return 0; | |
} | |
/* Accept client requests */ | |
void accept_cb(struct ev_loop *loop, struct ev_io *watcher, int revents) | |
{ | |
struct sockaddr_in client_addr; | |
socklen_t client_len = sizeof(client_addr); | |
int client_sd; | |
struct ev_io *w_client = (struct ev_io*) malloc (sizeof(struct ev_io)); | |
if(EV_ERROR & revents) | |
{ | |
perror("got invalid event"); | |
return; | |
} | |
// Accept client request | |
client_sd = accept(watcher->fd, (struct sockaddr *)&client_addr, &client_len); | |
if (client_sd < 0) | |
{ | |
perror("accept error"); | |
return; | |
} | |
if (client_sd >= MAX_CLIENT) | |
{ | |
perror("too many clients"); | |
close(client_sd); | |
return; | |
} | |
// accept would return unused file descriptor monotonically | |
// fd is guaranteed to be unique | |
clients[client_sd] = (char *)malloc(sizeof(char)*30); | |
sprintf(clients[client_sd], "%s:%d", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); | |
printf("Connected with %s\n", clients[client_sd]); | |
online_clients ++; // Increment online_clients count | |
printf("%d client(s) online.\n", online_clients); | |
// Initialize and start watcher to read client requests | |
ev_io_init(w_client, read_cb, client_sd, EV_READ); | |
ev_io_start(loop, w_client); | |
} | |
/* Read client message */ | |
void read_cb(struct ev_loop *loop, struct ev_io *watcher, int revents){ | |
char buffer[BUFFER_SIZE]; | |
int client_sd = watcher->fd; | |
ssize_t read; | |
if(EV_ERROR & revents) | |
{ | |
perror("got invalid event"); | |
return; | |
} | |
// Receive message from client socket | |
read = recv(client_sd, buffer, BUFFER_SIZE, 0); | |
if(read < 0) | |
{ | |
perror("read error"); | |
return; | |
} | |
if(read == 0) | |
{ | |
// Stop and free watcher if client socket is closing | |
ev_io_stop(loop, watcher); | |
close(watcher->fd); | |
free(watcher); | |
perror("peer might close"); | |
clients[client_sd] = NULL; | |
online_clients --; // Decrement online_clients count | |
printf("%d client(s) online.\n", online_clients); | |
return; | |
} | |
// override newline and append client info | |
sprintf(buffer+read-1, "(%s)\n", clients[client_sd]); | |
// Send message to all other clients | |
for(int i=0,j=0; i<online_clients;i++) { | |
// j is a client socket except me | |
while(clients[j] == NULL) j++; | |
if(j != client_sd) { | |
send(j, buffer, strlen(buffer), 0); | |
} | |
j++; | |
} | |
printf("message: %s", buffer); | |
bzero(buffer, BUFFER_SIZE); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment