summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoursoir <chat@joursoir.net>2020-11-25 19:04:49 +0300
committerJoursoir <chat@joursoir.net>2020-11-25 19:04:49 +0300
commit6139b74876a5c8417d75c2759ce0baec4ae0e171 (patch)
tree9924c31f8136c3834fd452e3846a35bcac6602d7
parent8dfca4755430209504dac5ed2e03a482342265b3 (diff)
downloadwant-chat-6139b74876a5c8417d75c2759ce0baec4ae0e171.tar.gz
want-chat-6139b74876a5c8417d75c2759ce0baec4ae0e171.tar.bz2
want-chat-6139b74876a5c8417d75c2759ce0baec4ae0e171.zip
add second (work with chat) level of abstraction
-rw-r--r--.gitignore3
-rw-r--r--Makefile14
-rw-r--r--src/const_vars.hpp16
-rw-r--r--src/server/Makefile18
-rw-r--r--src/server/chat.cpp196
-rw-r--r--src/server/chat.hpp60
-rw-r--r--src/server/rooms.cpp192
-rw-r--r--src/server/rooms.hpp33
-rw-r--r--src/server/server.cpp6
-rw-r--r--src/server/sockets.cpp3
10 files changed, 524 insertions, 17 deletions
diff --git a/.gitignore b/.gitignore
index aea0610..4d946e2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,5 @@
architecture/
+src/client/client
+src/server/server
+src/server/hash*
*.o \ No newline at end of file
diff --git a/Makefile b/Makefile
deleted file mode 100644
index 26ef7b4..0000000
--- a/Makefile
+++ /dev/null
@@ -1,14 +0,0 @@
-CPP = g++
-CPPFLAGS = -Wall -g
-SOURCES = src/client/client.cpp
-OBJECTS = client.o
-
-.PHONY: all clean client
-
-all: client
-
-client:
- $(CPP) $(CPPFLAGS) $(SOURCES) -o client
-
-clean:
- rm -rf src/client/client.o client \ No newline at end of file
diff --git a/src/const_vars.hpp b/src/const_vars.hpp
new file mode 100644
index 0000000..9c7b1c5
--- /dev/null
+++ b/src/const_vars.hpp
@@ -0,0 +1,16 @@
+#ifndef CONSTVARS_H
+#define CONSTVARS_H
+
+/*
+ * In chat messages look like that:
+ * Knight385: Hello, guys!
+ *
+*/
+
+const int max_name_len = 18;
+const int oneline_len = 57;
+const int max_usermsg_len = oneline_len * 3 - max_name_len - 2; // ": " = 2
+ // 57 * 3 - 18 - 2 = 151
+const int max_msg_len = oneline_len * 3; // 57*3 = 171
+
+#endif \ No newline at end of file
diff --git a/src/server/Makefile b/src/server/Makefile
new file mode 100644
index 0000000..be312d7
--- /dev/null
+++ b/src/server/Makefile
@@ -0,0 +1,18 @@
+CPP = g++
+CPPFLAGS = -Wall -g
+SOURCES = server.cpp sockets.cpp chat.cpp rooms.cpp
+OBJECTS = $(SOURCES:.cpp=.o)
+EXECUTABLE = server
+
+.PHONY: all clean
+
+all: $(EXECUTABLE)
+
+clean:
+ rm -rf $(OBJECTS) $(EXECUTABLE)
+
+$(EXECUTABLE): $(OBJECTS)
+ $(CPP) $(CPPFLAGS) -o $(EXECUTABLE) $(OBJECTS)
+
+$(OBJECTS):
+ $(CPP) -c $(CPPFLAGS) $(SOURCES) \ No newline at end of file
diff --git a/src/server/chat.cpp b/src/server/chat.cpp
new file mode 100644
index 0000000..a9ecda1
--- /dev/null
+++ b/src/server/chat.cpp
@@ -0,0 +1,196 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include "chat.hpp"
+#include "rooms.hpp"
+
+static const int qlen_for_listen = 6;
+
+void ChatSession::Send(const char *msg)
+{
+ CONSOLE_LOG("%s", msg);
+ write(GetFd(), msg, strlen(msg));
+}
+
+void ChatSession::Handle(bool r, bool w)
+{
+ if(!r)
+ return;
+
+ // this functions create for support any client, not one WantChat client:
+ if(buf_used >= (int)sizeof(buffer)) {
+ buf_used = 0;
+ ignoring = true;
+ }
+ if(ignoring) {
+ CONSOLE_LOG("Ignore the message, it's so big\n");
+ ReadAndIgnore();
+ }
+ else
+ ReadAndCheck();
+}
+
+void ChatSession::ReadAndIgnore()
+{
+ int rc = read(GetFd(), buffer, sizeof(buffer));
+ CONSOLE_LOG("readI return %d bytes\n", rc);
+ if(rc < 1) {
+ the_master->CloseSession(this);
+ return;
+ }
+
+ for(int i = 0; i < rc; i++)
+ if(buffer[i] == '\n')
+ { // stop ignoring!
+ CONSOLE_LOG("ReadAndIgnore: find \\n\n");
+ int rest = rc - i - 1;
+ if(rest > 0)
+ memmove(buffer, buffer + i + 1, rest);
+ buf_used = rest;
+ ignoring = 0;
+ CheckLines();
+ }
+}
+
+void ChatSession::ReadAndCheck()
+{
+ int rc = read(GetFd(), buffer+buf_used, sizeof(buffer)-buf_used);
+ CONSOLE_LOG("readC return %d bytes\n", rc);
+ if(rc < 1) {
+ the_master->LeaveMessage(this);
+ the_master->CloseSession(this);
+ return;
+ }
+ buf_used += rc;
+ CheckLines();
+}
+
+void ChatSession::CheckLines()
+{
+ if(buf_used <= 0)
+ return;
+
+ for(int i = 0; i < buf_used; i++) {
+ if(buffer[i] == '\n') {
+ CONSOLE_LOG("[CheckLines] buffer[i] == \\n i = %d\n", i);
+ buffer[i] = 0;
+ if(i > 0 && buffer[i-1] == '\r')
+ buffer[i-1] = 0;
+
+ CONSOLE_LOG("printed: %s\n", buffer);
+ the_master->HandleMessage(this, buffer);
+
+ int rest = buf_used - i - 1;
+ memmove(buffer, buffer + i + 1, rest);
+ buf_used = rest;
+ CONSOLE_LOG("[CheckLines] new buf_used = %d\n", buf_used);
+ CheckLines();
+ return;
+ }
+ }
+}
+
+void ChatSession::ChangeName(const char *n_name)
+{
+ for(int i = 0; i < max_name_len; i++)
+ name[i] = 0;
+
+ strcpy(name, n_name);
+ char *msg = new char[max_msg_len];
+ sprintf(msg, "Now your name is %s\n", name);
+
+ this->Send(msg);
+
+ delete[] msg;
+}
+
+//////////////////////////////////////////////////////////////
+
+Server::Server(EventSelector *sel, int fd)
+ : FdHandler(fd), the_selector(sel)
+{
+ the_selector->Add(this);
+ lobby = new ChatRoom(this, true);
+}
+
+Server::~Server()
+{
+ if(room)
+ delete[] room;
+
+ the_selector->Remove(this);
+}
+
+Server *Server::Start(EventSelector *sel, int port)
+{
+ int in_d = socket(AF_INET, SOCK_STREAM, 0);
+ if(in_d == -1)
+ return 0;
+
+ int opt = 1;
+ setsockopt(in_d, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
+
+ struct sockaddr_in addr;
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ addr.sin_port = htons(port);
+
+ int res = bind(in_d, (struct sockaddr*) &addr, sizeof(addr));
+ if(res == -1)
+ return 0;
+
+ res = listen(in_d, qlen_for_listen);
+ if(res == -1)
+ return 0;
+
+ return new Server(sel, in_d);
+}
+
+void Server::Handle(bool r, bool w)
+{
+ if(!r)
+ return;
+
+ struct sockaddr_in addr;
+ socklen_t len = sizeof(addr);
+ int sd = accept(GetFd(), (struct sockaddr*) &addr, &len);
+ if(sd == -1)
+ return;
+
+ ChatSession *s = new ChatSession(lobby, sd);
+ lobby->AddSession(s);
+ the_selector->Add(s);
+}
+
+int Server::AddRoom()
+{
+ if(!room) {
+ room_len = 16;
+ room = new ChatRoom*[room_len];
+ for(int i = 0; i < room_len; i++)
+ room[i] = 0;
+ }
+
+ int id = -1;
+ for(int i = 0; i < room_len; i++) {
+ if(room[i] == 0) {
+ id = 0;
+ room[i] = new ChatRoom(this, false);
+ }
+ }
+
+ if(id == -1) {
+ ChatRoom **tmp = new ChatRoom*[room_len+1];
+ for(int i = 0; i < room_len; i++) {
+ tmp[i] = room[i];
+ }
+ tmp[room_len] = new ChatRoom(this, false);
+ room_len += 1;
+ }
+
+ return id;
+} \ No newline at end of file
diff --git a/src/server/chat.hpp b/src/server/chat.hpp
new file mode 100644
index 0000000..ca22519
--- /dev/null
+++ b/src/server/chat.hpp
@@ -0,0 +1,60 @@
+#ifndef CHATREALIZATION_H
+#define CHATREALIZATION_H
+
+#include "sockets.hpp"
+#include "../const_vars.hpp"
+
+#define CONSOLE_LOG(f_, ...) printf((f_), ##__VA_ARGS__)
+
+class ChatRoom;
+class Server;
+
+class ChatSession : FdHandler {
+ friend class Server;
+
+ char name[max_name_len];
+ char buffer[max_msg_len];
+ int buf_used;
+ bool ignoring;
+
+ ChatRoom *the_master;
+
+ ChatSession(ChatRoom *i_master, int i_fd) : FdHandler(i_fd),
+ buf_used(0), ignoring(false), the_master(i_master) {}
+ ~ChatSession() {}
+
+ virtual void Handle(bool r, bool w);
+ void ReadAndIgnore();
+ void ReadAndCheck();
+ void CheckLines();
+public:
+ const char *GetName() const { return name; }
+
+ void ChangeName(const char *n_name);
+ void Send(const char *msg);
+};
+
+class Server : public FdHandler {
+ EventSelector *the_selector;
+ ChatRoom **room;
+ ChatRoom *lobby;
+
+ int room_len;
+
+ Server(EventSelector *sel, int fd);
+public:
+ ~Server();
+
+ static Server *Start(EventSelector *sel, int port);
+
+ int AddRoom();
+ // RemoveRoom();
+ // void AddSessionToRoom(ChatSession *s, int id);
+ void CloseConnection(ChatSession *s)
+ { the_selector->Remove(s); delete s; }
+private:
+ virtual void Handle(bool r, bool w);
+};
+
+
+#endif \ No newline at end of file
diff --git a/src/server/rooms.cpp b/src/server/rooms.cpp
new file mode 100644
index 0000000..624eb88
--- /dev/null
+++ b/src/server/rooms.cpp
@@ -0,0 +1,192 @@
+#include <stdio.h>
+#include <string.h>
+
+#include "rooms.hpp"
+#include "chat.hpp"
+
+// hash of commands:
+#define CMD_NAME 210647350421
+#define CMD_CREATE 229394553991880
+
+const int cmd_num = 2;
+const char *commands[cmd_num] = {
+ "/name",
+ "/create"
+ // /rooms - print all public rooms
+};
+
+char **ParseCommand(const char *input, int &arrc);
+unsigned long hash(const char *str);
+
+ChatRoom::~ChatRoom()
+{
+ while(first) {
+ item *tmp = first;
+ first = first->next;
+ the_server->CloseConnection(tmp->s);
+ delete tmp;
+ }
+}
+
+void ChatRoom::SendAll(const char *msg, ChatSession *except)
+{
+ CONSOLE_LOG("Send message all: %s\n", msg);
+ item *p;
+ for(p = first; p; p = p->next)
+ if(p->s != except)
+ p->s->Send(msg);
+}
+
+void ChatRoom::LeaveMessage(ChatSession *except)
+{
+ if(it_lobby)
+ return;
+
+ const char left_msg[] = " has left the chat";
+ const char *name = except->GetName();
+
+ int len = strlen(name);
+ char *lmsg = new char[len + sizeof(left_msg) + 2];
+ sprintf(lmsg, "%s%s\n", name, left_msg);
+ this->SendAll(lmsg, except);
+ delete[] lmsg;
+}
+
+void ChatRoom::HandleMessage(ChatSession *ses, const char *str)
+{
+ if(str[0] == '/') { // if user sent a command
+ int argc = 0;
+ char **argv = 0;
+ argv = ParseCommand(str, argc);
+
+ this->HandleCommand(ses, argc, argv);
+ }
+ else if(!it_lobby) {
+ char *msg = new char[max_msg_len];
+ sprintf(msg, "%s: %s\n", ses->GetName(), str);
+
+ this->SendAll(msg);
+
+ delete[] msg;
+ }
+}
+
+void ChatRoom::HandleCommand(ChatSession *s, int cmd_counter,
+ char **commands)
+{
+ unsigned long cmd = -1;
+ cmd = hash(commands[0]);
+
+ switch(cmd)
+ {
+ case CMD_NAME: {
+ if(!it_lobby) {
+ s->Send("You can use this command only in lobby\n");
+ break;
+ }
+
+ if(cmd_counter == 1) {
+ s->Send("Usage: /name your_good_name\n");
+ break;
+ }
+
+ int len = strlen(commands[1]);
+ if(len > max_name_len || len < 3) {
+ s->Send("Incorrect name. Name length from 3 to 18 chars\n");
+ break;
+ }
+
+ s->ChangeName(commands[1]);
+ break;
+ }
+ case CMD_CREATE: {
+ if(!it_lobby) {
+ s->Send("You can use this command only in lobby\n");
+ break;
+ }
+ if(!s->GetName()) {
+ s->Send("Before you can use this command, use /name\n");
+ break;
+ }
+
+ //int id = the_s->AddRoom();
+ //the_s->AddSessionToRoom(s, id);
+
+ break;
+ }
+
+ default: {
+ s->Send("Unknown command. Use: /help\n");
+ break;
+ }
+ }
+}
+
+void ChatRoom::AddSession(ChatSession *ses)
+{
+ item *p = new item;
+ p->next = first;
+ p->s = ses;
+ first = p;
+}
+
+void ChatRoom::RemoveSession(ChatSession *ses)
+{
+ item **p;
+ for(p = &first; *p; p = &((*p)->next)) {
+ if( ((*p)->s) == ses ) {
+ item *tmp = *p;
+ *p = tmp->next;
+ // not delete ChatSession!
+ delete tmp;
+ return;
+ }
+ }
+}
+
+void ChatRoom::CloseSession(ChatSession *ses)
+{
+ this->RemoveSession(ses);
+ the_server->CloseConnection(ses);
+}
+
+//////////////////////////////////////////////////////////////
+
+char **ParseCommand(const char *input, int &arrc)
+{
+ int max_argv = 5;
+ arrc = 0;
+ char **arr = new char*[max_argv];
+
+ int start = 0;
+ for(int i = 0; i < (int) strlen(input)+1; i++) {
+ if(input[i] == ' ' || input[i] == '\0' || input[i] == '\n') { // end
+ if(start == -1)
+ continue;
+
+ int size = i - start;
+ arr[arrc] = new char[size];
+ memcpy(arr[arrc], input + start, sizeof(char) * size);
+
+ start = -1;
+ arrc++;
+ if(arrc == max_argv)
+ break;
+ }
+ else if(start == -1)
+ start = i;
+ }
+
+ return arr;
+}
+
+unsigned long hash(const char *str)
+{
+ unsigned long hash = 5381;
+ char c;
+
+ while( (c = *str++) )
+ hash = ((hash << 5) + hash) + c;
+
+ return hash;
+} \ No newline at end of file
diff --git a/src/server/rooms.hpp b/src/server/rooms.hpp
new file mode 100644
index 0000000..08fffff
--- /dev/null
+++ b/src/server/rooms.hpp
@@ -0,0 +1,33 @@
+#ifndef ROOMREALIZATION_H
+#define ROOMREALIZATION_H
+
+class Server;
+class ChatSession;
+
+class ChatRoom {
+ Server *the_server;
+ const bool it_lobby;
+
+ struct item {
+ ChatSession *s;
+ item *next;
+ };
+ item *first;
+public:
+ ChatRoom(Server *i_server, bool i_lobby)
+ : the_server(i_server), it_lobby(i_lobby), first(0) {}
+ ~ChatRoom();
+
+ void SendAll(const char *msg, ChatSession *except = 0);
+ void LeaveMessage(ChatSession *except);
+
+ void HandleMessage(ChatSession *ses, const char *str);
+ void HandleCommand(ChatSession *s, int cmd_counter,
+ char **commands);
+
+ void AddSession(ChatSession *ses);
+ void RemoveSession(ChatSession *ses);
+ void CloseSession(ChatSession *ses);
+};
+
+#endif \ No newline at end of file
diff --git a/src/server/server.cpp b/src/server/server.cpp
index 6834ceb..ab7c420 100644
--- a/src/server/server.cpp
+++ b/src/server/server.cpp
@@ -2,6 +2,8 @@
#include "chat.hpp"
+const int port = 3030;
+
int main(int argc, char *argv[])
{
/* Event-driven programming */
@@ -10,11 +12,11 @@ int main(int argc, char *argv[])
2) event handling
*/
EventSelector *selector = new EventSelector;
- /*ChatServer *serv = ChatServer::Start(selector, port);
+ Server *serv = Server::Start(selector, port);
if(!serv) {
perror("server");
return 1;
- }*/
+ }
selector->Run();
return 0;
} \ No newline at end of file
diff --git a/src/server/sockets.cpp b/src/server/sockets.cpp
index 1a52786..7c995f0 100644
--- a/src/server/sockets.cpp
+++ b/src/server/sockets.cpp
@@ -1,4 +1,5 @@
#include <errno.h>
+#include <stdio.h>
#include "sockets.hpp"
@@ -78,7 +79,7 @@ void EventSelector::Run()
{
exit_flag = false;
do {
- int res = poll(fds, max_fd, -1);
+ int res = poll(fds, max_fd+1, -1);
if(res < 0) {
if(errno == EINTR)