LINUX.ORG.RU

Консольный обмен сообщениями

 ,


0

2

Создать собственный клиент быстрых сообщений (возможно, и сервер – зависит от выбранной архитектуры), который бы работали в рамках сети.

Как я понял, это что-то на подобии чата. Есть эхо-сервер. Хочу переделать его, возможно ли это? И как лучше сделать?

Эхо сервер такой: client.c

#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);
 
    Connect(socket, (SA *) &servaddr, sizeof(servaddr));
    do_work(socket);        
    Close(socket);
    
    return 0;
}
server.c
/*
 * Конфигурация сервера.
 */
#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);
            
    for(;;) {
        csocket = Accept(lsocket, NULL, 0);
 
        arg = Malloc(sizeof(int));
        *arg = csocket;
        
        Pthread_create(&thread, NULL, serve_client, arg);
    }
    
    return 0;
}

Ну так у тебя всё есть, что ещё? Ну добавь микропротокол свой сообщений, например каждый клиент имеет ID и отправляет серверу alice:bob:message:hello bob:end сервер смотрит ага алиса шлёт бобу сообщение «hello bob» и шлёт бобу сообщение боб может ответить bob:alice: fuck you bitch:end сервер смотрит, ага боб шлёт алису и шлёт алисе что бы шла она куда подальше.

Deleted ()
Ответ на: комментарий от Deleted

Я случайно не тот код прикрепил. То была уже готовая реализация, но она не работала. Думал с ней разобраться, но не получилось.

timas-cs ()
Ответ на: комментарий от Deleted

Почти со всем разобрался. Решил все таки старый доделать, но передо мною одна проблема. После обмена одним сообщением сервер блокируется и не продолжает работу. С чем это связяно? Думаю код нет смыла выкладывать заново. Он должен быть доступен в «исправлениях».

timas-cs ()
Ответ на: комментарий от timas-cs

Никаких исправлений не видать, а чтобы ответить на твои вопросы нужен отладочный вывод. Сторонняя помощь редко нужна тем, у кого есть привычка валить детальную диагностику в консоль.

anonymous ()

За такой код надо руки отрывать. Вы хотя бы сами себе можете объяснить как у вас работают Read и reads?

level1 ()

Была такая же идея, но кому это нужно? 1.5 задрота?

unixwz ()

У нас была такая лаба в универе)

Похожее пилил, когда познакомился с пиколиспом летом - на нем эта задача вообще в несколько строк решается.

https://github.com/nihirash/picoSIC

nihirash ★★★ ()
Ответ на: комментарий от timas-cs

Делай отладочный вывод, используй дебаггер. Сократи вопрос до «Я вызываю `функция_из_какой_то_библиотеки`, с параметрами X, Y, Z при условиях T. Должно быть A, а происходит B. Что не так?». Код без конкретных вопросов разбирать никто бесплатно не будет.

oldstable ()
Ответ на: комментарий от no-such-file

Изобретаешь talkd?

Бесполезно, будет глас вопиющий в пустыне...

vodz ★★★★★ ()

Понял. Старый код не доступен. Тот, что в первом посте, не актуален

Сервер блокируется после обмена 1-2 сообщениями. Как я понял, из-за функциии accept. Что можно придумать, чтобы заработало нормально?

//client.c
#include<sys/types.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include <string.h>
#include<unistd.h>
void main(int argc,char **argv)
{
int sockfd;
int n;
int i;
char sendline[1000],recvline[1000];
struct sockaddr_in servaddr;
if(argc!=2){
    printf("Usage:a.out <IP address>\n");
    exit(1);
    }
bzero(sendline,1000);
bzero(recvline,1000);
if((sockfd = socket(AF_INET,SOCK_STREAM,0))<0){//AF or PF
    perror(NULL);
    exit(1);
}
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_port = htons(51000);
if(inet_aton(argv[1],&servaddr.sin_addr) ==0){
    printf("INvalid IP address\n");
    close(sockfd);
    exit(1);
}
if(connect(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr))<0){
    perror(NULL);
    close(sockfd);
    exit(1);
}
for(i=0;i<3;i++){
printf("Message:\n");
fflush(stdin);
fgets(sendline,1000,stdin);
if((n=write(sockfd,sendline,strlen(sendline)+1))<0){
    perror("Can\'t write \n");
    close(sockfd);
    exit(1);
}
if((n=read(sockfd,recvline,999))<0){
    perror("Can\'t read \n");
    close(sockfd);
    exit(1);
}
printf("%s",recvline);
bzero(sendline,1000);
bzero(recvline,1000);
}
close(sockfd);
}




//server.c

#include<sys/types.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdio.h>
#include<errno.h>
#include <string.h>
#include<unistd.h>
#include<stdlib.h>
void main(int argc,char **argv)
{
int sockfd,newsockfd1,newsockfd2;
int n,k,m;
int client;
char line1[1000], line2[1000];
struct sockaddr_in servaddr,cliaddr;
if((sockfd = socket(PF_INET,SOCK_STREAM,0))<0){
    perror(NULL);
    exit(1);
}
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_port = htons(51000);
servaddr.sin_addr.s_addr =htonl(INADDR_ANY);
if(bind(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr))<0){
    perror(NULL);
    close(sockfd);
    exit(1);
}
if(listen(sockfd,8)<0){
perror(NULL);
    close(sockfd);
    exit(1);}
while(1){{
client=sizeof(cliaddr);
if((newsockfd1=accept(sockfd,(struct sockaddr*)&cliaddr,&client))<0){
perror(NULL);
    close(sockfd);
    exit(1);
}
if((newsockfd2=accept(sockfd,(struct sockaddr*)&cliaddr,&client))<0){
perror(NULL);
    close(sockfd);
    exit(1);
}
if((n=read(newsockfd1,line1,999))>0){
if((n=write(newsockfd2,line1,strlen(line2)+1))<0)
{
perror(NULL);
close(sockfd);
close(newsockfd1);
close(newsockfd2);
exit(1);
}}
if((n=read(newsockfd2,line2,999))>0){
if((n=write(newsockfd1,line2,strlen(line2)+1))<0)
{
perror(NULL);
close(sockfd);
close(newsockfd1);
close(newsockfd2);
exit(1);
}}}
 
if(n<0){
perror(NULL);
close(sockfd);
close(newsockfd1);
close(newsockfd2);
exit(1);
}
//close(newsockfd1);
//close(newsockfd2);
}
}
timas-cs ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.