首页 > 解决方案 > 当程序之前运行良好时,C 中的 TCP 分段错误(核心转储)

问题描述

我从 GeeksForGeeks 复制了代码,以便开始制作 TCP 程序。在我运行它几次之后它工作得很好......然后它突然没有......

服务器:

#include <stdio.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#define MAX 80
#define PORT 8080
#define SA struct sockaddr

void func(int sockfd);

int main()
{
    int sockfd, connfd, len;
    struct sockaddr_in servaddr, cli;

    // socket create and verification
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1) {
        printf("socket creation failed...\n");
        exit(0);
    }
    else
        printf("Socket successfully created..\n");
    bzero(&servaddr, sizeof(servaddr));

    // assign IP, PORT
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(PORT);

    // Binding newly created socket to given IP and verification
    if ((bind(sockfd, (SA*)&servaddr, sizeof(servaddr))) != 0) {
        printf("socket bind failed...\n");
        exit(0);
    }
    else
        printf("Socket successfully binded..\n");

    // Now server is ready to listen and verification
    if ((listen(sockfd, 1)) != 0) {
        printf("Listen failed...\n");
        exit(0);
    }
    else
        printf("Server listening..\n");
    len = sizeof(cli);

    // Accept the data packet from client and verification
    connfd = accept(sockfd, (SA*)&cli, &len);
    if (connfd < 0) {
        printf("server acccept failed...\n");
        exit(0);
    }
    else
        printf("server acccept the client...\n");

    // Function for chatting between client and server
    func(connfd);

    // After chatting close the socket
    close(sockfd);
}

void func(int sockfd)
{
    char buff[MAX];
    int n;
    for(;;)
    {
        bzero(buff, sizeof(buff));
        n=0;

        read(sockfd, buff, sizeof(buff));
        printf("%s", buff);

    }

}

客户:

#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#define MAX 80
#define PORT 8080
#define SA struct sockaddr

void func(int sockfd);

int main()
{
    int sockfd, connfd;
    struct sockaddr_in servaddr, cli;

    // socket create and varification
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1) {
        printf("socket creation failed...\n");
        exit(0);
    }
    else
        printf("Socket successfully created..\n");
    bzero(&servaddr, sizeof(servaddr));

    // assign IP, PORT
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    servaddr.sin_port = htons(PORT);

    // connect the client socket to server socket
    if (connect(sockfd, (SA*)&servaddr, sizeof(servaddr)) != 0) {
        printf("connection with the server failed...\n");
        exit(0);
    }
    else
    {
        printf("connected to the server..\n");
    }
    printf("boop");
    // function for chat
    func(sockfd);

    // close the socket
    close(sockfd);
}

void func(int sockfd)
{
    printf("Heelloo");
    char buff[MAX];
    int n;
    for(;;)
    {
        bzero(buff, sizeof(buff));
        n=0;

        printf("To client: ");
        while((buff[n++] = getchar()) != '\n');
        write(sockfd, buff, sizeof(buff));


    }
}

该函数不完整,但问题似乎就在客户端 main() 中的 func() 调用之前。任何帮助,将不胜感激!

标签: csegmentation-fault

解决方案


    write(sockfd, buff, sizeof(buff));

每本教如何正确使用套接字的教科书都会解释说,你绝对不能保证,无论如何,它write()会实际将请求的字节数写入套接字。from 的返回值write()表示实际写入的字节数(可能小于其第三个参数请求的字节数),写入套接字的代码必须正确实现必要的逻辑才能继续将剩余数据写入插座,如果需要的话。

此外,这里没有初始化整个buff,只有它的初始部分,所以这会尝试编写整个buff,这在技术上是未定义的行为。

   read(sockfd, buff, sizeof(buff));

我解释的同样的事情write()也适用于read()。您必须检查 from 的返回值read()以确定实际从套接字读取了多少。完全有可能read()最终只读取第一个字符。其余部分buff未初始化,并且包含随机垃圾,并且以下printf搜索正确'\0'终止的字符串注定要彻底失败,并崩溃。

每次运行程序时,由于各种原因,客户端和服务器都可能最终从套接字读取和写入不同数量的字节。这解释了为什么您观察到该程序随机失败,并且看似有效,但没有可预测的模式。

一般来说,像 GeeksForGeeks 这样的网站并不是用来学习编程的。他们缺乏适当的教程、技术信息和说明材料。正如我在开头提到的,学习网络编程和其他技术主题的最佳方式是通过有指导的、基于教科书的学习课程。你能在 GeeksForGeeks 上找到任何东西,你从哪里复制了这个程序,解释说你必须read检查和的返回值write吗?不,因为它不打算用作学习材料,但每个网络编程教程都必须解释不仅这一点,还要解释其他几个基本的相关概念。


推荐阅读