Answer the question
In order to leave comments, you need to log in
Why is TCP chat not working?
I'm redoing the TCP client-server template for a chat server with n-nodes.
Added buffer and registered send(), recv()
Changes 0, how was the echo server, and remained, how to fix and make a chat?
Server:
/*
* Шаблон параллельного эхо-сервера TCP, работающего по модели
* "один клиент - один поток".
*
* Компиляция:
* cc -Wall -O2 -pthread -o server3 server3.c
*/
#include <errno.h>
#include <netinet/in.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
/*
* Конфигурация сервера.
*/
#define PORT 1027
#define BACKLOG 5
#define MAXLINE 256
#define SA struct sockaddr
// Обработчик фатальных ошибок.
void error(const char *s) {
perror(s);
exit(-1);
}
/*
* Функции-обертки.
*/
int Socket(int domain, int type, int protocol) {
int rc;
rc = socket(domain, type, protocol);
if(rc == -1) error("socket()");
return rc;
}
int Bind(int socket, struct sockaddr *addr, socklen_t addrlen) {
int rc;
rc = bind(socket, addr, addrlen);
if(rc == -1) error("bind()");
return rc;
}
int Listen(int socket, int backlog) {
int rc;
rc = listen(socket, backlog);
if(rc == -1) error("listen()");
return rc;
}
int Accept(int socket, struct sockaddr *addr, socklen_t *addrlen) {
int rc;
for(;;) {
rc = accept(socket, addr, addrlen);
if(rc != -1) break;
if(errno == EINTR || errno == ECONNABORTED) continue;
error("accept()");
}
return rc;
}
void Close(int fd) {
int rc;
for(;;) {
rc = close(fd);
if(!rc) break;
if(errno == EINTR) continue;
error("close()");
}
}
size_t Read(int fd, void *buf, size_t count) {
ssize_t rc;
for(;;) {
rc = read(fd, buf, count);
if(rc != -1) break;
if(errno == EINTR) continue;
error("read()");
}
return rc;
}
size_t Write(int fd, const void *buf, size_t count) {
ssize_t rc;
for(;;) {
rc = write(fd, buf, count);
if(rc != -1) break;
if(errno == EINTR) continue;
error("write()");
}
return rc;
}
void *Malloc(size_t size) {
void *rc;
rc = malloc(size);
if(rc == NULL) error("malloc()");
return rc;
}
void Pthread_create(pthread_t *thread, pthread_attr_t *attr,
void *(*start_routine)(void *), void *arg) {
int rc;
rc = pthread_create(thread, attr, start_routine, arg);
if(rc) {
errno = rc;
error("pthread_create()");
}
}
/*
* Чтение строки из сокета.
*/
size_t reads(int socket, char *s, size_t size) {
char *p;
size_t n, rc;
/* Проверить корректность переданных аргументов. */
if(s == NULL) {
errno = EFAULT;
error("reads()");
}
if(!size) return 0;
p = s;
size--;
n = 0;
while(n < size) {
rc = Read(socket, p, 1);
if(rc == 0) break;
if(*p == '\n') {
p++;
n++;
break;
}
p++;
n++;
}
*p = 0;
return n;
}
/*
* Запись count байтов в сокет.
*/
size_t writen(int socket, const char *buf, size_t count) {
const char *p;
size_t n, rc;
/* Проверить корректность переданных аргументов. */
if(buf == NULL) {
errno = EFAULT;
error("writen()");
}
p = buf;
n = count;
while(n) {
rc = Write(socket, p, n);
n -= rc;
p += rc;
}
return count;
}
void *serve_client(void *arg) {
int socket;
char s[MAXLINE];
ssize_t rc;
/* Перевести поток в отсоединенное (detached) состояние. */
pthread_detach(pthread_self());
socket = *((int *) arg);
free(arg);
while((rc = reads(socket,s,MAXLINE)) > 0) {if(writen(socket,s,rc) == -1) break;}
Close(socket);
return NULL;
}
int main(void) {
int lsocket; /* Дескриптор прослушиваемого сокета. */
int csocket; /* Дескриптор присоединенного сокета. */
struct sockaddr_in servaddr;
int *arg;
pthread_t thread;
/* Создать сокет. */
lsocket = Socket(PF_INET, SOCK_STREAM, 0);
/* Инициализировать структуру адреса сокета сервера. */
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(PORT);
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
/* Связать сокет с локальным адресом протокола. */
Bind(lsocket, (SA *) &servaddr, sizeof(servaddr));
/* Преобразовать неприсоединенный сокет в пассивный. */
Listen(lsocket, BACKLOG);
char buffer[BUFSIZ];
for(;;) {
csocket = Accept(lsocket, NULL, 0);
send(csocket, buffer, BUFSIZ, 0);//
arg = Malloc(sizeof(int));
*arg = csocket;
Pthread_create(&thread, NULL, serve_client, arg);
printf("Client"); //
recv(csocket, buffer, BUFSIZ,0);//
}
return 0;
}
/*
* Шаблон TCP клиента.
*
* Компиляция:
* cc -Wall -O2 -o client client.c
*
* Завершение работы клиента: Ctrl+D.
*/
#include <arpa/inet.h>
#include <errno.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <unistd.h>
#include <limits.h>
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define PORT 1027
#define MAXLINE 256
#define SA struct sockaddr
/*
* Обработчик фатальных ошибок.
*/
void error(const char *s) {
perror(s);
exit(-1);
}
/*
* Функции-обертки.
*/
int Socket(int domain, int type, int protocol) {
int rc;
rc = socket(domain, type, protocol);
if(rc == -1) error("socket()");
return rc;
}
void Connect(int socket, const struct sockaddr *addr, socklen_t addrlen) {
int rc;
rc = connect(socket, addr, addrlen);
if(rc == -1) error("connect()");
}
void Close(int fd) {
int rc;
for(;;) {
rc = close(fd);
if(!rc) break;
if(errno == EINTR) continue;
error("close()");
}
}
void Inet_aton(const char *str, struct in_addr *addr) {
int rc;
rc = inet_aton(str, addr);
if(!rc) {
/* Функция inet_aton() не меняет errno в случае ошибки. Чтобы
сообщение, выводимое error(), было более осмысленным,
присваиваем errno значение EINVAL. */
errno = EINVAL;
error("inet_aton()");
}
}
int Select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
struct timeval *timeout) {
int rc;
for(;;) {
rc = select(n, readfds, writefds, exceptfds, timeout);
if(rc != -1) break;
if(rc == EINTR) continue;
error("select()");
}
return rc;
}
size_t Read(int fd, void *buf, size_t count) {
ssize_t rc;
for(;;) {
rc = read(fd, buf, count);
if(rc != -1) break;
if(errno == EINTR) continue;
error("read()");
}
return rc;
}
size_t Write(int fd, const void *buf, size_t count) {
ssize_t rc;
for(;;) {
rc = write(fd, buf, count);
if(rc != -1) break;
if(errno == EINTR) continue;
error("write()");
}
return rc;
}
/*
* Запись count байтов в сокет.
*/
size_t writen(int socket, const char *buf, size_t count) {
const char *p;
size_t n, rc;
/* Проверить корректность переданных аргументов. */
if(buf == NULL) {
errno = EFAULT;
error("writen()");
}
p = buf;
n = count;
while(n) {
rc = Write(socket, p, n);
n -= rc;
p += rc;
}
return count;
}
void show_usage() {
puts("Usage: client ip_address");
exit(-1);
}
void do_work(int socket) {
int n;
fd_set readfds;
char s[MAXLINE];
ssize_t rc;
n = MAX(STDIN_FILENO, socket) + 1;
for(;;) {
/* Инициализировать набор дескрипторов. */
FD_ZERO(&readfds);
FD_SET(STDIN_FILENO, &readfds);
FD_SET(socket, &readfds);
Select(n, &readfds, NULL, NULL, NULL);
if(FD_ISSET(STDIN_FILENO, &readfds)) {
rc = Read(STDIN_FILENO, s, MAXLINE);
if(!rc) break;
writen(socket, s, rc);
}
if(FD_ISSET(socket, &readfds)) {
rc = Read(socket, s, MAXLINE);
if(!rc) break;
Write(STDOUT_FILENO, s, rc);
}
}
}
int main(int argc, char **argv) {
int socket;
struct sockaddr_in servaddr;
if(argc != 2) show_usage();
socket = Socket(PF_INET, SOCK_STREAM, 0);
/* Инициализировать структуру адреса сокета. */
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(PORT);
Inet_aton(argv[1], &servaddr.sin_addr);
// char buffer[BUFSIZ];
// recv(socket, buffer, BUFSIZ);
// while(true) {
// send();
// recv();
// }
Connect(socket, (SA *) &servaddr, sizeof(servaddr));
do_work(socket);
Close(socket);
return 0;
}
Answer the question
In order to leave comments, you need to log in
It is difficult to quickly understand the code without knowing what was before the changes and what changes were made. According to the task of the echo server, this is not a chat. The task of the echo server is to send the same data to the client that he sent to him, this is his meaning. The meaning of the chat is the opposite, you need to send data from the client to all other connected clients on the server, except for this client (so that there is no echo).
The server code does not show that you are creating a list of clients connected to it. Just a separate thread to connect. Therefore, the client sees only what he sent. And in chat mode, data would have to be transferred between threads through some kind of common message queue, because clients can be connected at different speeds and not everyone can cope with receiving a message at the same time.
Try to study the chat code implemented inhttps://eax.me/libevent/ , for one and explore the great library. Creating a thread per connection leads to a rapid exhaustion of resources and leads to the so-called C10K (10000 connections) problem. In a world where billions of clients can access a server, thread-per-connection solutions have an architectural flaw.
Didn't find what you were looking for?
Ask your questionAsk a Question
731 491 924 answers to any question