c - 在c套接字编程中使用write发送缓冲区

标签 c sockets posix system-calls

我正在使用 fork() 用 C 语言创建一个 TCP 聊天室。每个客户端消息都应到达服务器,并在缓冲区中包含用户名和消息,因此我使用 strcpy(buffer, name)strcat(buffer, ": ") 来组合缓冲区和名称。之后我使用

n = write (sockfd, buffer, strlen(buffer))

将缓冲区发送到服务器。所以就在这里发生了奇怪的事情,我的服务器什么也没显示,但之后当我使用

n = read(sockfd, buffer, 255);
printf("%s", buffer);

字符串已正确传输。这是我的服务器代码:

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

int main(){
    int port;
    int i = 1;
    printf("Enter port ->");
    scanf("%d", &port);

    int sockfd, newsockfd, n;
    char buffer[255];
    struct sockaddr_in6 serv_addr, cli_addr;
    int ret, flag;

    sockfd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); 
    if (sockfd < 0){
        printf("Error opening socket\n");
    }

    bzero((char *) &serv_addr, sizeof(serv_addr));
    flag = 1;
    ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));
    if (ret < 0){
        printf("Set sock opt errror\n");
    }
    serv_addr.sin6_family = AF_INET6;
    serv_addr.sin6_addr = in6addr_any;
    serv_addr.sin6_port = htons(port);

    if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0){
        printf("Error binding\n");
    }

    listen(sockfd, 5);
    int clilen = sizeof(cli_addr);
    int pid;

    while(1){
        newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
        if (newsockfd < 0){
            printf("Error on accept\n");
        }
        printf("New connection\n");
        pid = fork();
        if (pid < 0){
            printf("Error on pid\n");
        }
        if (pid == 0){
            close(sockfd);
            while(1){
                bzero(buffer, 255);
                n = read(newsockfd, buffer, 255);
                if (n < 0){
                    printf("Error on reading\n");
                }
                printf("%s", buffer);
                n = write(newsockfd, buffer, strlen(buffer));
                if (n < 0){
                    printf("Error on writing\n");
                }
            }
        } 
    }
    close(newsockfd);
    return 0;
}

和客户端代码:

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

int main(){

    int port;
    printf("Enter port ->");
    scanf("%d", &port);
    char ip[10];
    printf("Enter ip -> ");
    scanf("%s", &ip);
    char name[20];
    printf("Enter name -> ");
    scanf("%s", &name);
    int sockfd, n;
    char buffer[255];
    struct sockaddr_in6 serv_addr;

    sockfd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); 
    if (sockfd < 0){
        printf("Error opening socket\n");
    }
    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin6_family = AF_INET6;
    inet_pton(AF_INET6, "ip", &serv_addr.sin6_addr);
    serv_addr.sin6_port = htons(port);
    if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0){
        printf("Connection failed\n");
    }

    while(1){

        bzero(buffer, 255);
        fgets(buffer, 255, stdin);
        strcpy(buffer, name);
        strcat(buffer, ": ");
        n = write (sockfd, buffer, strlen(buffer));
        bzero(buffer,255);
        if (n < 0){
            printf("Error on writing\n");
        }
        n = read(sockfd, buffer, 255);
        printf("%s", buffer);
        if (n < 0){
            printf("Error on reading\n");
        }
    }

    close(sockfd);
}

所以也许有人可以看到为什么客户端无法将带有名称和消息的完整缓冲区发送到服务器的问题?我认为问题出在 name 上。当我只发送我使用的缓冲区时

fgets(buffer, 255, stdin);

服务器工作正常,但我需要客户端用户名。

我尝试测试 strlen(name)buffer,一切似乎都正常。我尝试做的是将名称手动转换为缓冲区,如下所示:

 for (int i = 0; i < strlen(name); i++){
        buffer[i] = name[i];
 }

但是如果这是执行此操作的唯一方法,我应该如何将缓冲区推送到正确的输入名称并且不丢失消息信息?

最佳答案

您的代码有很多错误:)。

首先 scanf 将指针作为第一个参数,因此当您想将它用于字符串时,请执行以下操作:

char ip[10];
...
scanf("%s", ip);//THIS IS CORRECT

不是 scanf("%s", &ip);

在服务器上,编译器提示 clilen 的类型。当您将该变量声明为简单的 int 时,accept 需要一个 sockelen_t(或等效的 unsigned int)。

<小时/>

主要问题

如下。在客户端中,您执行了 strcpy(buffer, name);,它从缓冲区的开头开始复制 name,但缓冲区的开头实际上包含您的数据。您应该使用 strcat 执行类似以下操作,就像在下一行中所做的那样:

    bzero(buffer, 255);
    fgets(buffer, 255, stdin);
    strcat(buffer, name);
    strcat(buffer, ": ");
<小时/>

进一步的一般建议

还要注意另一件事:避免将系统调用函数 read write getchar 与库函数(例如 fread)混合使用、fwritefgets。我当时有this problem这是因为 fgets 可能读取比预期更多的字节。在你的情况下应该没问题,因为你将它们用于不同的目的。

关于c - 在c套接字编程中使用write发送缓冲区,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55224021/

相关文章:

r - 将 "1984-03-25 02:00:00"转换为 POSIX 给出 NA

c++ - 初始化条件变量和/或互斥体时出现问题——在 IPC 设置中

c - #define ISR(x) #pragma isr=x

c - libwebsockets:如何在请求中存储 IP 地址

c++ - 首先 recv() 无法读取从服务器发送的消息

c++客户端在不同端口上有响应

python - Python字节似乎与套接字编程中的字节有所不同

c - 为什么 Mac OS 上的 C 运行时允许预组合和分解的 UTF-8?

c - 生产者消费者段错误(核心转储)

c - 如何正确传递单个字符作为对 C 中另一个方法的引用