Data commit
This commit is contained in:
parent
7387c8f97b
commit
cb5bb5e222
199093 changed files with 3378972 additions and 0 deletions
199
Task/Chat-server/Wren/chat-server-1.wren
Normal file
199
Task/Chat-server/Wren/chat-server-1.wren
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
/* chat_server.wren */
|
||||
|
||||
class Clients {
|
||||
foreign static max
|
||||
foreign static count
|
||||
foreign static isActive(vmi)
|
||||
foreign static connfd(vmi)
|
||||
foreign static uid(vmi)
|
||||
foreign static name(vmi)
|
||||
foreign static setName(vmi, s)
|
||||
foreign static printAddr(vmi)
|
||||
foreign static delete(vmi)
|
||||
}
|
||||
|
||||
class Mutex {
|
||||
foreign static clientsLock()
|
||||
foreign static clientsUnlock()
|
||||
|
||||
foreign static topicLock()
|
||||
foreign static topicUnlock()
|
||||
}
|
||||
|
||||
class Chat {
|
||||
// send message to all clients but the sender
|
||||
static sendMessage(s, uid) {
|
||||
Mutex.clientsLock()
|
||||
for (i in 0...Clients.max) {
|
||||
if (Clients.isActive(i) && Clients.uid(i) != uid) {
|
||||
if (write(Clients.connfd(i), s, s.bytes.count) < 0) {
|
||||
System.print("Write to descriptor %(Clients.connfd(i)) failed.")
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
Mutex.clientsUnlock()
|
||||
}
|
||||
|
||||
// send message to all clients
|
||||
static sendMessageAll(s) {
|
||||
Mutex.clientsLock()
|
||||
for (i in 0...Clients.max) {
|
||||
if (Clients.isActive(i)) {
|
||||
if (write(Clients.connfd(i), s, s.bytes.count) < 0) {
|
||||
System.print("Write to descriptor %(Clients.connfd(i)) failed.")
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
Mutex.clientsUnlock()
|
||||
}
|
||||
|
||||
// send message to sender
|
||||
static sendMessageSelf(s, connfd) {
|
||||
if (write(connfd, s, s.bytes.count) < 0) {
|
||||
Fiber.abort("Write to descriptor %(connfd) failed.")
|
||||
}
|
||||
}
|
||||
|
||||
// send message to client
|
||||
static sendMessageClient(s, uid) {
|
||||
Mutex.clientsLock()
|
||||
for (i in 0...Clients.max) {
|
||||
if (Clients.isActive(i) && Clients.uid(i) == uid) {
|
||||
if (write(Clients.connfd(i), s, s.bytes.count) < 0) {
|
||||
System.print("Write to descriptor %(Clients.connfd(i)) failed.")
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
Mutex.clientsUnlock()
|
||||
}
|
||||
|
||||
// send list of active clients
|
||||
static sendActiveClients(connfd) {
|
||||
Mutex.clientsLock()
|
||||
for (i in 0...Clients.max) {
|
||||
if (Clients.isActive(i)) {
|
||||
var s = "<< [%(Clients.uid(i))] %(Clients.name(i))\r\n"
|
||||
sendMessageSelf(s, connfd)
|
||||
}
|
||||
}
|
||||
Mutex.clientsUnlock()
|
||||
}
|
||||
|
||||
// handle all communication with the client
|
||||
static handleClient(vmi) {
|
||||
if (!Clients.isActive(vmi)) {
|
||||
Fiber.abort("The client handled by VM[%(vmi)] is inactive.")
|
||||
}
|
||||
var connfd = Clients.connfd(vmi)
|
||||
var uid = Clients.uid(vmi)
|
||||
var name = Clients.name(vmi)
|
||||
System.write("<< accept ")
|
||||
Clients.printAddr(vmi)
|
||||
System.print(" referenced by %(uid)")
|
||||
var buffOut = "<< %(name) has joined\r\n"
|
||||
sendMessageAll(buffOut)
|
||||
Mutex.topicLock()
|
||||
if (topic != "") {
|
||||
buffOut = "<< topic: %(topic)\r\n"
|
||||
sendMessageSelf(buffOut, connfd)
|
||||
}
|
||||
Mutex.topicUnlock()
|
||||
sendMessageSelf("<< see /help for assistance\r\n", connfd)
|
||||
|
||||
/* receive input from client */
|
||||
var buffIn = ""
|
||||
while ((buffIn = read(connfd, bufferSize/2 - 1)) && buffIn.bytes.count > 0) {
|
||||
buffOut = ""
|
||||
buffIn = buffIn.trimEnd("\r\n")
|
||||
|
||||
/* ignore empty buffer */
|
||||
if (buffIn == "") continue
|
||||
|
||||
/* special options */
|
||||
if (buffIn[0] == "/") {
|
||||
var split = buffIn.split(" ")
|
||||
var command = split[0]
|
||||
if (command == "/quit") {
|
||||
break
|
||||
} else if (command == "/ping") {
|
||||
sendMessageSelf("<< pong\r\n", connfd)
|
||||
} else if (command == "/topic") {
|
||||
if (split.count > 0) {
|
||||
Mutex.topicLock()
|
||||
topic = split[1..-1].join(" ")
|
||||
Mutex.topicUnlock()
|
||||
buffOut = "<< topic changed to: %(topic)\r\n"
|
||||
sendMessageAll(buffOut)
|
||||
} else {
|
||||
sendMessageSelf("<< message cannot be null\r\n", connfd)
|
||||
}
|
||||
} else if (command == "/nick") {
|
||||
if (split.count > 0) {
|
||||
var newName = split[1..-1].join(" ")
|
||||
buffOut = "<< %(name) is now known as %(newName)\r\n"
|
||||
Clients.setName(vmi, newName)
|
||||
name = newName
|
||||
sendMessageAll(buffOut)
|
||||
} else {
|
||||
sendMessageSelf("<< name cannot be null\r\n", connfd)
|
||||
}
|
||||
} else if (command == "/msg") {
|
||||
if (split.count > 0) {
|
||||
var toUid = Num.fromString(split[1])
|
||||
if (split.count > 1) {
|
||||
buffOut = "[PM][%(name)] "
|
||||
buffOut = buffOut + split[2..-1].join(" ") + "\r\n"
|
||||
sendMessageClient(buffOut, toUid)
|
||||
} else {
|
||||
sendMessageSelf("<< message cannot be null\r\n", connfd)
|
||||
}
|
||||
} else {
|
||||
sendMessageSelf("<< reference cannot be null\r\n", connfd)
|
||||
}
|
||||
} else if (command == "/list") {
|
||||
buffOut = "<< clients %(Clients.count)\r\n"
|
||||
sendMessageSelf(buffOut, connfd)
|
||||
sendActiveClients(connfd)
|
||||
} else if (command == "/help") {
|
||||
buffOut = ""
|
||||
buffOut = buffOut + "<< /quit Quit chatroom\r\n"
|
||||
buffOut = buffOut + "<< /ping Server test\r\n"
|
||||
buffOut = buffOut + "<< /topic <message> Set chat topic\r\n"
|
||||
buffOut = buffOut + "<< /nick <name> Change nickname\r\n"
|
||||
buffOut = buffOut + "<< /msg <reference> <message> Send private message\r\n"
|
||||
buffOut = buffOut + "<< /list Show active clients\r\n"
|
||||
buffOut = buffOut + "<< /help Show help\r\n"
|
||||
sendMessageSelf(buffOut, connfd)
|
||||
} else {
|
||||
sendMessageSelf("<< unknown command\r\n", connfd)
|
||||
}
|
||||
} else {
|
||||
/* send message */
|
||||
buffOut = "[%(name)] %(buffIn)\r\n"
|
||||
sendMessage(buffOut, uid)
|
||||
}
|
||||
}
|
||||
|
||||
/* close connection */
|
||||
buffOut = "<< [%(name)] has left\r\n"
|
||||
sendMessageAll(buffOut)
|
||||
close(connfd)
|
||||
|
||||
/* delete client from queue and yield thread (from C side) */
|
||||
System.write("<< quit ")
|
||||
Clients.printAddr(vmi)
|
||||
System.print(" referenced by %(uid)")
|
||||
Clients.delete(vmi)
|
||||
}
|
||||
|
||||
foreign static topic
|
||||
foreign static topic=(s)
|
||||
|
||||
foreign static bufferSize
|
||||
foreign static write(connfd, buf, count)
|
||||
foreign static read(connfd, count)
|
||||
foreign static close(connfd)
|
||||
}
|
||||
353
Task/Chat-server/Wren/chat-server-2.wren
Normal file
353
Task/Chat-server/Wren/chat-server-2.wren
Normal file
|
|
@ -0,0 +1,353 @@
|
|||
/* gcc chat_server.c -o chat_server -lpthread -lwren -lm */
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/types.h>
|
||||
#include <signal.h>
|
||||
#include <wren.h>
|
||||
|
||||
#define MAX_CLIENTS 10
|
||||
#define BUFFER_SZ 2048
|
||||
|
||||
static _Atomic unsigned int cli_count = 0;
|
||||
static int listenfd = 0, uid = 10;
|
||||
char *script = NULL;
|
||||
|
||||
/* Client structure */
|
||||
typedef struct {
|
||||
struct sockaddr_in addr; /* Client remote address */
|
||||
int connfd; /* Connection file descriptor */
|
||||
int uid; /* Client unique identifier */
|
||||
int vmi; /* The index of the VM which handles this client */
|
||||
char name[32]; /* Client name */
|
||||
} client_t;
|
||||
|
||||
client_t *clients[MAX_CLIENTS];
|
||||
|
||||
pthread_mutex_t clients_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
static char topic[BUFFER_SZ/2];
|
||||
|
||||
pthread_mutex_t topic_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
WrenVM* vms[MAX_CLIENTS]; // array of VMs
|
||||
|
||||
/* add client to queue */
|
||||
void queue_add(client_t *cl){
|
||||
pthread_mutex_lock(&clients_mutex);
|
||||
for (int i = 0; i < MAX_CLIENTS; ++i) {
|
||||
if (!clients[i]) {
|
||||
cl->vmi = i;
|
||||
clients[i] = cl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&clients_mutex);
|
||||
}
|
||||
|
||||
/* Delete client from queue */
|
||||
void queue_delete(int uid){
|
||||
pthread_mutex_lock(&clients_mutex);
|
||||
for (int i = 0; i < MAX_CLIENTS; ++i) {
|
||||
if (clients[i]) {
|
||||
if (clients[i]->uid == uid) {
|
||||
clients[i] = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&clients_mutex);
|
||||
}
|
||||
|
||||
/* print ip address */
|
||||
void print_client_addr(struct sockaddr_in addr){
|
||||
printf("%d.%d.%d.%d",
|
||||
addr.sin_addr.s_addr & 0xff,
|
||||
(addr.sin_addr.s_addr & 0xff00) >> 8,
|
||||
(addr.sin_addr.s_addr & 0xff0000) >> 16,
|
||||
(addr.sin_addr.s_addr & 0xff000000) >> 24);
|
||||
}
|
||||
|
||||
/* enable Wren to handle all client communication */
|
||||
void *handle_client(void *arg) {
|
||||
client_t *cli = (client_t *)arg;
|
||||
cli_count++;
|
||||
int vmi = cli->vmi;
|
||||
WrenHandle *callHandle = wrenMakeCallHandle(vms[vmi], "handleClient(_)");
|
||||
wrenEnsureSlots(vms[vmi], 2);
|
||||
wrenGetVariable(vms[vmi], "main", "Chat", 0);
|
||||
wrenSetSlotDouble(vms[vmi], 1, (double)vmi);
|
||||
wrenCall(vms[vmi], callHandle);
|
||||
}
|
||||
|
||||
/* C <= Wren interface functions */
|
||||
|
||||
void C_max(WrenVM* vm) {
|
||||
wrenSetSlotDouble(vm, 0, (double)MAX_CLIENTS);
|
||||
}
|
||||
|
||||
void C_count(WrenVM* vm) {
|
||||
wrenSetSlotDouble(vm, 0, (double)cli_count);
|
||||
}
|
||||
|
||||
void C_isActive(WrenVM* vm) {
|
||||
int vmi = (int)wrenGetSlotDouble(vm, 1);
|
||||
bool res = clients[vmi] != NULL;
|
||||
wrenSetSlotBool(vm, 0, res);
|
||||
}
|
||||
|
||||
void C_connfd(WrenVM* vm) {
|
||||
int vmi = (int)wrenGetSlotDouble(vm, 1);
|
||||
wrenSetSlotDouble(vm, 0, (double)clients[vmi]->connfd);
|
||||
}
|
||||
|
||||
void C_uid(WrenVM* vm) {
|
||||
int vmi = (int)wrenGetSlotDouble(vm, 1);
|
||||
wrenSetSlotDouble(vm, 0, (double)clients[vmi]->uid);
|
||||
}
|
||||
|
||||
void C_name(WrenVM* vm) {
|
||||
int vmi = (int)wrenGetSlotDouble(vm, 1);
|
||||
wrenSetSlotString(vm, 0, (const char *)clients[vmi]->name);
|
||||
}
|
||||
|
||||
void C_setName(WrenVM* vm) {
|
||||
int vmi = (int)wrenGetSlotDouble(vm, 1);
|
||||
const char *name = wrenGetSlotString(vm, 2);
|
||||
size_t size = sizeof(clients[vmi]->name);
|
||||
strncpy(clients[vmi]->name, name, size);
|
||||
clients[vmi]->name[size-1] = '\0';
|
||||
}
|
||||
|
||||
void C_clientsLock(WrenVM* vm) {
|
||||
pthread_mutex_lock(&clients_mutex);
|
||||
}
|
||||
|
||||
void C_clientsUnlock(WrenVM* vm) {
|
||||
pthread_mutex_unlock(&clients_mutex);
|
||||
}
|
||||
|
||||
void C_topicLock(WrenVM* vm) {
|
||||
pthread_mutex_lock(&topic_mutex);
|
||||
}
|
||||
|
||||
void C_topicUnlock(WrenVM* vm) {
|
||||
pthread_mutex_unlock(&topic_mutex);
|
||||
}
|
||||
|
||||
void C_printAddr(WrenVM* vm) {
|
||||
int vmi = (int)wrenGetSlotDouble(vm, 1);
|
||||
print_client_addr(clients[vmi]->addr);
|
||||
}
|
||||
|
||||
void C_topic(WrenVM* vm) {
|
||||
wrenSetSlotString(vm, 0, (const char *)topic);
|
||||
}
|
||||
|
||||
void C_setTopic(WrenVM* vm) {
|
||||
const char *t = wrenGetSlotString(vm, 1);
|
||||
strncpy(topic, t, sizeof(topic));
|
||||
topic[sizeof(topic)-1] = '\0';
|
||||
}
|
||||
|
||||
void C_bufferSize(WrenVM* vm) {
|
||||
wrenSetSlotDouble(vm, 0, (double)BUFFER_SZ);
|
||||
}
|
||||
|
||||
void C_write(WrenVM* vm) {
|
||||
int fd = (int)wrenGetSlotDouble(vm, 1);
|
||||
const void *buf = (const void *)wrenGetSlotString(vm, 2);
|
||||
size_t count = (size_t)wrenGetSlotDouble(vm, 3);
|
||||
ssize_t res = write(fd, buf, count);
|
||||
wrenSetSlotDouble(vm, 0, (double)res);
|
||||
}
|
||||
|
||||
void C_read(WrenVM* vm) {
|
||||
char buf[BUFFER_SZ / 2];
|
||||
int fd = (int)wrenGetSlotDouble(vm, 1);
|
||||
size_t count = (size_t)wrenGetSlotDouble(vm, 2);
|
||||
ssize_t rlen = read(fd, buf, count);
|
||||
buf[rlen] = '\0';
|
||||
wrenSetSlotString(vm, 0, (const char *)buf);
|
||||
}
|
||||
|
||||
void C_close(WrenVM* vm) {
|
||||
int connfd = (int)wrenGetSlotDouble(vm, 1);
|
||||
close(connfd);
|
||||
}
|
||||
|
||||
void C_delete(WrenVM* vm) {
|
||||
int vmi = (int)wrenGetSlotDouble(vm, 1);
|
||||
client_t *cli = clients[vmi];
|
||||
queue_delete(cli->uid);
|
||||
free(cli);
|
||||
cli_count--;
|
||||
pthread_detach(pthread_self());
|
||||
}
|
||||
|
||||
WrenForeignMethodFn bindForeignMethod(
|
||||
WrenVM* vm,
|
||||
const char* module,
|
||||
const char* className,
|
||||
bool isStatic,
|
||||
const char* signature) {
|
||||
if (strcmp(module, "main") == 0) {
|
||||
if (strcmp(className, "Clients") == 0) {
|
||||
if (isStatic && strcmp(signature, "max") == 0) return C_max;
|
||||
if (isStatic && strcmp(signature, "count") == 0) return C_count;
|
||||
if (isStatic && strcmp(signature, "isActive(_)") == 0) return C_isActive;
|
||||
if (isStatic && strcmp(signature, "connfd(_)") == 0) return C_connfd;
|
||||
if (isStatic && strcmp(signature, "uid(_)") == 0) return C_uid;
|
||||
if (isStatic && strcmp(signature, "name(_)") == 0) return C_name;
|
||||
if (isStatic && strcmp(signature, "setName(_,_)") == 0) return C_setName;
|
||||
if (isStatic && strcmp(signature, "printAddr(_)") == 0) return C_printAddr;
|
||||
if (isStatic && strcmp(signature, "delete(_)") == 0) return C_delete;
|
||||
} else if (strcmp(className, "Mutex") == 0) {
|
||||
if (isStatic && strcmp(signature, "clientsLock()") == 0) return C_clientsLock;
|
||||
if (isStatic && strcmp(signature, "clientsUnlock()") == 0) return C_clientsUnlock;
|
||||
if (isStatic && strcmp(signature, "topicLock()") == 0) return C_topicLock;
|
||||
if (isStatic && strcmp(signature, "topicUnlock()") == 0) return C_topicUnlock;
|
||||
} else if (strcmp(className, "Chat") == 0) {
|
||||
if (isStatic && strcmp(signature, "topic") == 0) return C_topic;
|
||||
if (isStatic && strcmp(signature, "topic=(_)") == 0) return C_setTopic;
|
||||
if (isStatic && strcmp(signature, "bufferSize") == 0) return C_bufferSize;
|
||||
if (isStatic && strcmp(signature, "write(_,_,_)") == 0) return C_write;
|
||||
if (isStatic && strcmp(signature, "read(_,_)") == 0) return C_read;
|
||||
if (isStatic && strcmp(signature, "close(_)") == 0) return C_close;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void writeFn(WrenVM* vm, const char* text) {
|
||||
printf("%s", text);
|
||||
}
|
||||
|
||||
void errorFn(WrenVM* vm, WrenErrorType errorType, const char* module, const int line, const char* msg) {
|
||||
switch (errorType) {
|
||||
case WREN_ERROR_COMPILE:
|
||||
printf("[%s line %d] [Error] %s\n", module, line, msg);
|
||||
break;
|
||||
case WREN_ERROR_STACK_TRACE:
|
||||
printf("[%s line %d] in %s\n", module, line, msg);
|
||||
break;
|
||||
case WREN_ERROR_RUNTIME:
|
||||
printf("[Runtime Error] %s\n", msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
char *readFile(const char *fileName) {
|
||||
FILE *f = fopen(fileName, "r");
|
||||
fseek(f, 0, SEEK_END);
|
||||
long fsize = ftell(f);
|
||||
rewind(f);
|
||||
char *script = malloc(fsize + 1);
|
||||
fread(script, 1, fsize, f);
|
||||
fclose(f);
|
||||
script[fsize] = 0;
|
||||
return script;
|
||||
}
|
||||
|
||||
void catch_ctrl_c(int sig) {
|
||||
/* clean up and exit */
|
||||
for (int i = 0; i < MAX_CLIENTS; ++i) {
|
||||
wrenFreeVM(vms[i]);
|
||||
if (clients[i]) {
|
||||
close(clients[i]->connfd);
|
||||
free(clients[i]);
|
||||
}
|
||||
}
|
||||
close(listenfd);
|
||||
free(script);
|
||||
printf("\n<[ SERVER ENDED ]>\n");
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
WrenConfiguration config;
|
||||
wrenInitConfiguration(&config);
|
||||
config.writeFn = &writeFn;
|
||||
config.errorFn = &errorFn;
|
||||
config.bindForeignMethodFn = &bindForeignMethod;
|
||||
const char* module = "main";
|
||||
const char* fileName = "chat_server.wren";
|
||||
script = readFile(fileName);
|
||||
|
||||
/* config the VMs and interpret the script */
|
||||
for (int i = 0; i < MAX_CLIENTS; ++i) {
|
||||
vms[i] = wrenNewVM(&config);
|
||||
wrenInterpret(vms[i], module, script);
|
||||
}
|
||||
|
||||
/* prepare to start the server */
|
||||
int connfd = 0;
|
||||
struct sockaddr_in serv_addr;
|
||||
struct sockaddr_in cli_addr;
|
||||
pthread_t tid;
|
||||
|
||||
/* socket settings */
|
||||
listenfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
serv_addr.sin_family = AF_INET;
|
||||
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
serv_addr.sin_port = htons(5000);
|
||||
|
||||
/* ignore pipe signals */
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
/* catch ctrl-c being pressed */
|
||||
signal(SIGINT, catch_ctrl_c);
|
||||
|
||||
/* bind */
|
||||
if (bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
|
||||
perror("Socket binding failed");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* listen */
|
||||
if (listen(listenfd, 10) < 0) {
|
||||
perror("Socket listening failed");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
printf("<[ SERVER STARTED ]>\n");
|
||||
|
||||
/* accept clients */
|
||||
while (1) {
|
||||
socklen_t clilen = sizeof(cli_addr);
|
||||
connfd = accept(listenfd, (struct sockaddr*)&cli_addr, &clilen);
|
||||
|
||||
/* check if max clients is reached */
|
||||
if ((cli_count + 1) == MAX_CLIENTS) {
|
||||
printf("<< max clients reached\n");
|
||||
printf("<< reject ");
|
||||
print_client_addr(cli_addr);
|
||||
printf("\n");
|
||||
close(connfd);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* client settings */
|
||||
client_t *cli = (client_t *)malloc(sizeof(client_t));
|
||||
cli->addr = cli_addr;
|
||||
cli->connfd = connfd;
|
||||
cli->uid = uid++;
|
||||
sprintf(cli->name, "%d", cli->uid);
|
||||
|
||||
/* add client to the queue and fork thread */
|
||||
queue_add(cli);
|
||||
pthread_create(&tid, NULL, &handle_client, (void*)cli);
|
||||
|
||||
/* reduce CPU usage */
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue