Переделываю шаблон клиент-сервера TCP для чат сервера с n-узлами.
Добавил buffer и прописал send(), recv()
Изменений 0, как был эхо сервер, так и остался, как исправить и сделать чат?
Сервер:
/*
* Шаблон параллельного эхо-сервера 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;
}