【问题标题】:C client/server socket errorC 客户端/服务器套接字错误
【发布时间】:2014-10-08 19:33:43
【问题描述】:

我正在用 C 语言编写一个基本的聊天程序。我已经为它构建了框架,但在修复我遇到的用户名错误之前,我无法继续进行下去。我的客户端和服务器正在通信,但是当我输入我的用户名时,服务器显示之前输入的用户名。

EX:客户端程序进入 Joe:服务器不显示任何内容

客户端程序进入 Tom:服务器显示“输入的用户名:Joe”

客户端程序进入 Rob:服务器显示“输入的用户名:Tom”

这是客户端.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(; ;){

}
}

这是服务器.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.
}

【问题讨论】:

  • 您在acceptint fd = *(int*)(arg); 之间存在竞争条件。如果accept 在线程可以取消引用您传递给它的指针之前完成,您将最终得到两个线程从同一个套接字读取。
  • @DavidSchwartz 我从未遇到过竞争条件或需要调试一个。关于如何在这种情况下这样做的任何提示?
  • 不要向线程传递指向其他线程使用的值的指针。相反,要么将值传递给它们,要么分配值的副本,将指针传递给它们,然后让它们释放它。

标签: c multithreading sockets client


【解决方案1】:

简单....

改变: scanf("%s\r\n\n", 有效载荷);

到: scanf("%s", 有效载荷);

【讨论】:

    【解决方案2】:

    如果协议是“你总是发送和接收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 和复制其参数的线程之间的竞争条件的评论。

    【讨论】:

    • VG 但您不想将 bytesRead 设置为 -1,您想不理会它,除非出现错误。您希望在循环之后读取的任何 内容都可用。
    • @EJP 我的假设是他的协议是“你必须写/读 512 个字节”。少于 512 个字节将构成难以理解的部分消息。这段代码的重点是具体实现该协议。
    • @DavidSchwartz 我可以根据我的指示读/写任何长度的消息。我要替换客户端和服务器中的每一个 bytesRead 和 read 函数吗?
    • 明确地说,每条消息不一定是 512 次。这只是最大尺寸
    • @BartoshSudak 然后你需要设计一个更复杂的协议。如果收件人收到的消息少于 512 个字节,他们如何知道他们是否收到了完整的消息?
    猜你喜欢
    • 2019-03-17
    • 1970-01-01
    • 2017-05-26
    • 2014-03-10
    • 2015-09-07
    • 2012-06-24
    • 1970-01-01
    • 2011-11-15
    • 1970-01-01
    相关资源
    最近更新 更多