繁体   English   中英

C socket编程——TCP Server和Client的读写不同步

[英]C socket programming - Read and Write of TCP Server and Client out of sync

我在 C 中有一个简单的服务器和客户端。

我要实现的功能的步骤是:

  1. 服务器向客户端发送一个字符“%”,并打印在客户端的屏幕上。

  2. 然后客户端输入一条消息,该消息将发送到服务器并打印在服务器的屏幕上,上面写着“客户端:(来自客户端的消息)”。

  3. 服务器最终向客户端发送一条消息“收到消息”,并将其打印在客户端的屏幕上。

  4. 返回步骤 1。

我添加了一个名为valid for client 的指示器,用于告诉服务器缓冲区(来自客户端的消息)何时实际有效,以防止客户端写入服务器读取不同步。

然而,我得到的结果是这样的:

在此处输入图片说明

先感谢您 !

客户端代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

void error(const char *msg){
    perror(msg);
    exit(1);  //terminate program
}

int main(int argc, char *argv[]){   
    
    int sockfd/*(file descriptor)*/, portno, n;
    char buffer[255];
    int valid = 0;
    
    struct sockaddr_in serv_addr, cli_addr;
    
    
    portno = 1234;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd < 0){
        error("Error opening socket.");
    }
    
    
    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    serv_addr.sin_port = htons(1234);
    if( connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0 ){
        error("Connection failed");
    }
    
    
S:  bzero(buffer, 255);
    n = read(sockfd, buffer, 255);
    if(n < 0){
        error("Error reading from socket");
    }
    printf("%s", buffer);

    bzero(buffer, 255);
    fgets(buffer, 255, stdin);
    
    // valid is set to 1 and sent to server 
    // when the message in buffer is actually typed in and meant to be sent to the server 
    valid = 1;
    write(sockfd, &valid, sizeof(valid));
    write(sockfd, buffer, strlen(buffer));
    if(strncmp("exit", buffer, 4) == 0){
        goto Q;
    }

    bzero(buffer, 255);
    n = read(sockfd, buffer, 255);
    if(n < 0){
        error("Error reading from socket");
    }
    printf("%s", buffer);
    goto S;
    
Q:  printf("You have chosen to exit. Exit successful\n");
    close(sockfd);
    
    return 0;
}

服务器代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

void error(const char *msg){
    perror(msg);
    exit(1);  //terminate program
}

int main(int argc, char *argv[]){
    
    int sockfd/*(file descriptor)*/, newsockfd, portno, n;
    char buffer[255];
    int valid = 0;

    int count = 0;
    
    struct sockaddr_in serv_addr, cli_addr;
    socklen_t clilen;
    
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd < 0){
        error("Error opening socket.");
    }
    
    bzero((char *) &serv_addr, sizeof(serv_addr));
    
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    serv_addr.sin_port = htons(1234);
    
    if(bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0){
        error("Building failed");
    }
    
    listen(sockfd, 5); //integer stands for max clients
    clilen = sizeof(cli_addr);
    
    newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
    
    if(newsockfd < 0){
        error("Error on accept");
    }
    
    int num1, num2, ans, choice;
    
S:  
    valid = 0;
    bzero(buffer, 255);
    if(count == 0){
        count++;
        strcpy(buffer, "********************************\n** Welcome to the BBS server. **\n********************************\n%");
    }else{
        strcpy(buffer, "% ");
    }
    n = write(newsockfd, buffer, strlen(buffer));
    if(n < 0){
        error("Error writing to socket");
    }
    
M:
    // check if the message from client (buffer) is valid
    read(newsockfd, &valid, sizeof(int));
    // if not, return to M
    if(valid == 0){
        goto M;
    }
    // continue if valid
    bzero(buffer, 255);
    read(newsockfd, buffer, strlen(buffer));
    if(strncmp("exit", buffer, 4) == 0){
        goto Q;
    }
    printf("Client: %s\n", buffer);
    

    bzero(buffer, 255);
    strcpy(buffer, "Message received.\n");
    n = write(newsockfd, buffer, strlen(buffer));
    if(n < 0){
        error("Error writing to socket\n");
    }
    goto S;
    
    
Q:  close(newsockfd);
    close(sockfd);
    
    return 0;
}

您的代码有很多问题,但导致您询问的特定不当行为的问题来自服务器中的此代码:

 bzero(buffer, 255); read(newsockfd, buffer, strlen(buffer));

缓冲区刚刚清零,您可以确信strlen(buffer)将返回 0。因此read()实际上不会传输任何字节。 你可能想要sizeof而不是strlen

    bzero(buffer, 255);
    read(newsockfd, buffer, sizeof(buffer));

代码中很明显的其他问题包括

  • 假设每一方的write()调用将与另一方的read()调用一对一配对以传输完整的消息。 您使用的是流套接字,因此实际上没有消息边界。 通常会看到一次read从多个write接收字节,有时一次write的字节会拆分为两个甚至更多read 因此,

    • 如果您想以完整消息为单位交换数据,那么您需要在流的顶部分层,使参与者能够识别消息边界。

    • 每一方都必须准备好接收部分消息。

    • 您传输的valid信号值并不足以确保客户端和服务器保持同步。

  • readwrite调用不能保证传输指定的完整字节数。 如果您想传输特定数量的字节,那么您必须使用这些函数的返回值来确定您是否真的这样做了,如果不是,则需要传输多少字节。

  • 客户端和服务器在每次read最多接收缓冲区的完整大小,并且他们假设此后可以将缓冲区内容作为字符串处理。 但是,如果read实际上读取了最大数量的字节(并且没有嵌入的空字节),那么缓冲区将不会终止,并且其内容可以被视为字符串的假设将导致未定义的行为。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM