diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..e5207452 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.0) +project(messenger) +add_executable(client src/client.cpp) +add_executable(server src/server.cpp) +find_library(PH pthread) +target_link_libraries(server ${PH}) +target_link_libraries(client ${PH}) \ No newline at end of file diff --git a/src/client.cpp b/src/client.cpp new file mode 100644 index 00000000..e48cfac4 --- /dev/null +++ b/src/client.cpp @@ -0,0 +1,126 @@ +#include +#include +#include +#include +#include +#include +#include +#include + + +#define DEF_PORT 8888 +#define DEF_IP "127.0.0.1" +int readFix(int sock, char** buf, char** name, long* time, int flags); +int sendFix(int sock, char* buf, char* name, int flags); +void* reader(void* arg) { + for (;;) { + int sock = *(int *) arg; + char* buf = nullptr; + char* name = nullptr; + time_t time; + int res = readFix(sock, &buf, &name, &time, 0); + if (res <= 0) { + perror("Error while receiving:"); + exit(1); + } + time -= timezone; + printf("%s\t%10s:\t%s\n", ctime(&time),name,buf); + free(buf); + free(name); + } +} +int main( int argc, char** argv) { + tzset(); + char* addr; + int port; + char* readbuf; + if(argc <3) { + printf("Using default port %d\n",DEF_PORT); + port = DEF_PORT; + } else + port = atoi(argv[2]); + if(argc < 2) { + printf("Using default addr %s\n",DEF_IP); + addr = DEF_IP; + } else + addr = argv[1]; + // создаем сокет + struct sockaddr_in peer; + peer.sin_family = AF_INET; + peer.sin_port = htons( port ); + peer.sin_addr.s_addr = inet_addr( addr ); + int sock = socket( AF_INET, SOCK_STREAM, 0 ); + if ( sock < 0 ){ + perror( "Can't create socket\n" ); + exit( 1 ); + } + // присоединяемся к серверу + int res = connect( sock, ( struct sockaddr * )&peer, sizeof(peer ) ); + if (res) { + perror( "Can't connect to server:" ); + exit( 1 ); + } + // основной цикл программы + char buf[100]; + char name[20]; + printf("Enter your name\n"); + bzero(name, 20); + fgets(name, 20, stdin); + name[strlen(name)-1] = '\0'; + pthread_t thr; + res = pthread_create(&thr,NULL,reader,(void*)&sock); + if (res) { + printf("Error while creating new thread\n"); + } + for(;;) { + printf("Input request (empty to exit)\n"); + bzero(buf,100); + fgets(buf, 100, stdin); + buf[strlen(buf)-1] = '\0'; + if(strlen(buf) == 0) { + printf("Bye-bye\n"); + return 0; + } + res = sendFix(sock, buf, name, 0); + if ( res <= 0 ) { + perror( "Error while sending:" ); + exit( 1 ); + } + + } +} +int readFix(int sock, char** buf, char** name, long* time, int flags) { + // читаем "заголовок" - сколько байт составляет наше сообщение + unsigned msgLength = 0; + unsigned nameLength = 0; + int res=recv(sock,&msgLength,sizeof(unsigned),flags|MSG_WAITALL); + if (res <= 0)return res; + // читаем само сообщение + *buf = new char[msgLength+1]; + bzero(*buf, msgLength+1); + res = recv(sock, *buf, msgLength, flags | MSG_WAITALL); + if (res <= 0) return res; + res = recv(sock, &nameLength, sizeof(unsigned), flags | MSG_WAITALL); + if (res <= 0) return res; + *name = new char[nameLength+1]; + bzero(*name, nameLength+1); + res = recv(sock, *name, nameLength, flags | MSG_WAITALL); + if (res <= 0) return res; + res = recv(sock, time, sizeof(long), flags | MSG_WAITALL); + if (res <= 0) return res; + return 1; +} +int sendFix(int sock, char* buf, char* name, int flags) { + // число байт в сообщении + unsigned msgLength = strlen(buf); + unsigned nameLength = strlen(name); + int res = send(sock, &msgLength, sizeof(unsigned), flags ); + if (res <= 0) return res; + res = send(sock, buf, msgLength, flags); + if (res <= 0) return res; + res = send(sock, &nameLength, sizeof(unsigned), flags); + if (res <= 0) return res; + res = send(sock, name, nameLength, flags); + if (res <= 0) return res; + return 1; +} diff --git a/src/server.cpp b/src/server.cpp new file mode 100644 index 00000000..0746880e --- /dev/null +++ b/src/server.cpp @@ -0,0 +1,191 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEF_PORT 8888 +#define DEF_IP "127.0.0.1" +std::set clients; + +// обработка одного клиента +int readFix(int sock, char **msg, char **name, long *time, int flags); + +int sendFix(int sock, char *buf, char *name, long *time, int flags); + +void *main_cycle(void *args) { + while (true) { + int listener = *(int *) args; + fd_set readset; + FD_ZERO(&readset); + FD_SET(listener, &readset); + for (auto it = clients.begin(); it != clients.end(); it++) { + FD_SET(*it, &readset); + } + timeval timeout; + timeout.tv_sec = 1; + timeout.tv_usec = 0; + int mx = std::max(listener, *std::max_element(clients.begin(), clients.end())); + int cnt = select(mx + 1, &readset, nullptr, nullptr, &timeout); + if (cnt == -1) { + perror("Failed select\n"); + exit(5); + } else if (cnt == 0) continue; + if (FD_ISSET(listener, &readset)) { + int client = accept(listener, nullptr, nullptr); + if (client <= 0) { + perror("Failed accept\n"); + exit(5); + } + fcntl(client, F_SETFL, O_NONBLOCK); + clients.insert(client); + } + for (auto it = clients.begin(); it != clients.end(); it++) { + if (FD_ISSET(*it, &readset)) { + char *buf = nullptr; + char *name = nullptr; + int res = 0; + long time; + res = readFix(*it, &buf, &name, &time, 0); + int err = errno; // save off errno, because because the printf statement might reset it + if (res < 0){ + if (err == EAGAIN){ + printf("non-blocking operation returned EAGAIN or EWOULDBLOCK\n"); + continue; + }else{ + printf("recv returned unrecoverable error(errno=%d)\n", err); + break; + } + } + if (res < 0) { + perror("Can't recv data from client\n"); + close(listener); + clients.erase(*it); + exit(1); + } else if (res == 0) { + FD_CLR(*it, &readset); + close(*it); + printf("Disconnected client(%d)\n",*it); + continue; + } + printf("%s\t%10s:%s\n", ctime(&time), name, buf); + for (int client : clients) { + res = sendFix(client, buf, name, &time, 0); + if (res < 0) { + perror("send call failed"); + continue; + } + } + free(name); + free(buf); + + } + } + } +} + +int main(int argc, char **argv) { + int port = 0; + if (argc < 2) { + printf("Using default port %d\n", DEF_PORT); + port = DEF_PORT; + } else + port = atoi(argv[1]); + struct sockaddr_in listenerInfo; + listenerInfo.sin_family = AF_INET; + listenerInfo.sin_port = htons(port); + listenerInfo.sin_addr.s_addr = htonl(INADDR_ANY); + int listener = socket(AF_INET, SOCK_STREAM, 0); + fcntl(listener, F_SETFL, O_NONBLOCK); + if (listener < 0) { + perror("Can't create socket to listen: "); + exit(1); + } + int res = bind(listener, (struct sockaddr *) &listenerInfo, sizeof(listenerInfo)); + if (res < 0) { + perror("Can't bind socket"); + exit(1); + } + // слушаем входящие соединения + res = listen(listener, 5); + if (res) { + perror("Error while listening:"); + exit(1); + } + // основной цикл работы + pthread_t cycle; + res = pthread_create(&cycle, nullptr, main_cycle, (void *) (&listener)); + if (res) { + printf("Error while creating new thread\n"); + } + char input; + for (;;) { + input = (char) getchar(); + if (input == 'q') { + exit(0); + } + } +} +ssize_t rec(int sock, void* data, size_t n, int flags) { + int res = 0; + int err = 0; + do { + res = recv(sock, data, n, flags); + err = errno; // save off errno, because because the printf statement might reset it + if (res < 0){ + if (err == EAGAIN){ + }else{ + printf("recv returned unrecoverable error(errno=%d)\n", err); + break; + } + } + } while (err == EAGAIN && res < 0); + return res; +} + +int readFix(int sock, char **msg, char **name, long *time, int flags) { + // читаем "заголовок" - сколько байт составляет наше сообщение + unsigned msgLength = 0; + unsigned nameLength = 0; + struct tm *t; + int res = rec(sock, &msgLength, sizeof(unsigned), flags); + if (res <= 0)return res; + // читаем само сообщение + *msg = new char[msgLength + 1]; + bzero(*msg, msgLength + 1); + res = rec(sock, *msg, msgLength, flags); + if (res <= 0) return res; + res = rec(sock, &nameLength, sizeof(unsigned), flags); + if (res <= 0) return res; + *name = new char[nameLength + 1]; + bzero(*name, nameLength + 1); + res = rec(sock, *name, nameLength, flags); + if (res <= 0) return res; + *time = ::time(nullptr); + t = gmtime(time); + *time = mktime(t); + return 1; +} +int sendFix(int sock, char *buf, char *name, long *time, int flags) { + // шлем число байт в сообщении + unsigned msgLength = strlen(buf); + unsigned nameLength = strlen(name); + int res = send(sock, &msgLength, sizeof(unsigned), flags); + if (res <= 0) return res; + res = send(sock, buf, msgLength, flags); + if (res <= 0) return res; + res = send(sock, &nameLength, sizeof(unsigned), flags); + if (res <= 0) return res; + res = send(sock, name, nameLength, flags); + if (res <= 0) return res; + res = send(sock, time, sizeof(long), flags); + if (res <= 0) return res; + return 1; +}