2014-10-08 4 views
2

Я пишу базовую программу чата на C. Я создал фреймворк для него, но я не могу продолжить дальше, пока не исправлю ошибку имени пользователя, которую я получаю. Мой клиент и сервер обмениваются данными, но когда я ввожу свое имя пользователя, на сервере отображается ранее введенное имя пользователя.Ошибка сокета клиента/сервера

EX: программа-клиент входит Джо сервер ничего не отображается

клиентская программа

входит Том: сервер отображает «Имя введенное: Джо»

клиентской программа

входит Rob: сервер отображает «Имя пользователя введенное: Том "

Это client.c

#include <sys/socket.h> 
#include <pthread.h> 
#include <time.h> 
#include <netinet/in.h> 
#include <signal.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <unistd.h> 


#define SA struct sockaddr 
#define MAXLINE 4096 
//void leave(int sig); /* Things to do after signal handler is called*/ 

void error(const char *msg) 
{ 
perror(msg); 
exit(0); 
} 

/*Routine to send data*/ 
static void *do_send(void *arg) 
{ 
int fd = *(int*)(arg); //Socket File Descriptor 
char payload[512]; 
for(; ;) { 

    scanf("%s\r\n\n", payload); 
    write(fd, payload, sizeof(payload)); 

    //Choose an available client’s username and send it to the server 
    //if (the peer is ready to chat) 
    //for(; ;) { 
     //Code to send msg to its peer 
    //} 
} 
pthread_exit((void *)0); 
} 

/*Routine to receive data*/ 
static void *do_recv(void *arg) 
{ 

int fd = *(int*)(arg); 
char payload[512]; 
for(; ;) { 
    //Code to receive data 
    read(fd, payload, sizeof(payload)); 
    //Print data 
    printf("%s\n", payload); 
} 
pthread_exit((void *)0); 
} 

int main(int argc, char **argv) 
{ 
//Declare variables 
int sockfd, n; 
char recvline[MAXLINE+1]; 
struct sockaddr_in servaddr; 
int status; 
pthread_t chld_thr1, chld_thr2; 

//Create socket. 
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) 
{ 
    error("Socket error"); 
} 

memset(&servaddr, 0, sizeof(servaddr)); 
servaddr.sin_family = AF_INET; 
servaddr.sin_port = htons(1024); 
//Send a connection request to server 
if(connect(sockfd, (SA *) &servaddr, sizeof(servaddr)) < 0) 
{ 
    error("Connect error"); 
} 
//Register username 
/* Create a thread to send data */ 
pthread_create(&chld_thr1, NULL, do_send, (void *)&sockfd); 
/* Create a thread to receive data */ 
pthread_create(&chld_thr2, NULL, do_recv, (void *)&sockfd); 
//pthread_join(do_send, NULL); 
//Close connection and exit 

//Loops so program doesn't close 
for(; ;){ 

} 
} 

Это server.c

#include <sys/socket.h> 
#include <pthread.h> 
#include <time.h> 
#include <netinet/in.h> 
#include <unistd.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
/* Function prototypes and global variables */ 

#define SA struct sockaddr 
#define LISTENQ 1024 

/*Function executed by the new thread */ 
static void *do_child(void *arg) 
{ 
//Declare variables 
int fd = *(int*)(arg); //Socket Descriptor 
char payload[512]; 
int list = 1; 
int chat = 2; 
int exit = 3; 
int bytesRead = 0; 

//Initial verification and registration codes 
strcpy(payload, "Please enter your username:"); 
write(fd, payload, sizeof(payload)); 

for(; ;) { 
    //Receive messages from client 
    //Code to receive data 
    bytesRead = read(fd, payload, sizeof(payload)); 
    //Print data 
    if (bytesRead > 0){ 
     printf("Username entered: %s\n", payload); 
    } 

    //Check the msg_type of the message 
    //if(m1.msg_type == list) 
     //Send the list of available clients 
    //if(m1.msg_type ==chat) 
     //Forward messages to the peer that a client wants to chat with you 
     //if(m1.msg_type == chatMessage) 
     //Forward messages to its peer 
    //if(m1.msg_type == exit) 

     //Remove the client from the client list. Close the client connection 
} 
pthread_exit((void *)0); 
} 

int main(int argc, char *argv[]) { 
//Declare Variables 
int listenfd, connfd; //Socket ID's 
struct sockaddr_in servaddr; 
pthread_t chld_thr; 
char payload[512]; 

//Create Socket 
listenfd = socket(AF_INET, SOCK_STREAM, 0); 

memset(&servaddr, 0, sizeof(servaddr)); 
servaddr.sin_family = AF_INET; 
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); 

servaddr.sin_port = htons(1024); 

//Bind to the Address and Port Number Pair 
bind(listenfd, (SA *) &servaddr, sizeof(servaddr)); 

//Listen 
listen(listenfd, LISTENQ); 

for(; ;) { 
    connfd = accept(listenfd, (SA *) NULL, NULL); 
    if (connfd > 0){ 
     strcpy(payload, "Connection Accepted"); 
     printf("%s\n", payload); 

     /*Create a new thread to handle the connections. */ 
     pthread_create(&chld_thr, NULL, do_child, (void *)&connfd); 
    } 
    else 
     printf("%s\n", "No connection could be made."); 
} 
//Close listen connection and exit. 
} 
+1

У вас есть условие гонки между 'accept' и' int fd = * (int *) (arg); '. Если 'accept' завершается до того, как поток может разыменовать указатель, который вы его передали, вы завершите чтение двух потоков из одного и того же сокета. –

+0

@DavidSchwartz Я никогда не сталкивался с состоянием гонки или не нуждался в отладке. Любые советы о том, как это сделать для этого сценария? –

+0

Не пропускайте нить указатель на значение, используемое другими потоками. Вместо этого либо передайте им значение, либо выделите копию значения, передайте им указатель на него и попросите их отменить его. –

ответ

1

Простой ....

Изменение: зсапЕ ("% S \ г \ п \ п", полезная нагрузка);

по: scanf ("% s", полезная нагрузка);

1

Если протокол "вы всегда получать и отправлять 512 байт", то вам нужно заменить это:

bytesRead = read(fd, payload, sizeof(payload)); 

и это:

read(fd, payload, sizeof(payload)); 

С этим:

bytesRead = 0; 
do 
{ 
    ssize_t j = read(fd, payload + bytesRead, 512 - bytesRead)); 
    if (j <= 0) 
    { 
     bytesRead = -1; 
     break; 
    } 
    bytesRead += j; 
} while (bytesRead < 512); 

Если это не протокол, то что это такое?

Я предполагаю, что вас не волнует безопасность. Но если вы это сделаете, вам нужно рассмотреть возможность того, что отправленные 512 байтов не могут содержать терминатор.

См. Также мой комментарий о вашем состоянии гонки между accept и нитью, копирующей ее параметр.

+0

VG, но вы не хотите устанавливать bytesRead в -1, вы хотите оставить его в покое, возможно, если не была ошибка. Вы хотите, чтобы все * * считалось доступным после цикла. – EJP

+0

@ EJP Мое предположение заключается в том, что его протокол «вы должны написать/прочитать 512 байтов». Менее 512 байт будут представлять собой непонятное, частичное сообщение. Точка этого кода предназначена для реализации этого протокола. –

+0

@DavidSchwartz Я могу читать/писать любое сообщение длины в соответствии с моими инструкциями. я могу заменить каждую функцию bytesRead и read как на клиенте, так и на сервере? –