套接字TCP双向连接

时间:2015-06-01 21:21:49

标签: c sockets tcp

我试图在TCP中编写一个程序,其中客户端和服务器端都能够通信,直到任何一个发送退出,终止连接。现在,客户端能够发送内容,但是当服务器端发送内容时,客户端会出现seg故障。如果我的代码不符合标准,我会事先道歉,因为我对编码很新。任何帮助将不胜感激 这是我的代码:

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

#define SERVER_PORT 5432
#define MAX_LINE 256

int main(int argc, char * argv[])
{
    struct hostent  *hp;
    struct sockaddr_in serv_addr;
    char *host;
    char buf[MAX_LINE];
    int n, size;
    int sockfd;

    if (argc == 2) {
            host = argv[1];
    }
    else {
            fprintf(stderr, "usage: simplex-talk host\n");
            exit(1);
    }
    hp = gethostbyname(host);
    if (!hp) {
            fprintf(stderr, "error: can't find such host: %s\n", host);
            exit(1);
    }
    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    bcopy((char *)hp->h_addr,(char *)&serv_addr.sin_addr.s_addr,hp->h_length);
    serv_addr.sin_port = htons(SERVER_PORT);
    size = sizeof(serv_addr);
//active open
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd <0) {
            error("ERROR opening socket");
            exit(1);
    }
    printf("successfully opened socket\n");
    int quit = 1;
    while(quit == 1)
    {
            if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
            {
                    perror("ERROR: could not connect\n");
                    close(sockfd);
                    exit(1);
            }
            fgets(buf, sizeof(buf), stdin);
            if(strcmp(buf, "quit\n") == 0)
            {
                    quit = 0;
                    int send;
                    send = sendto(sockfd, buf, MAX_LINE, 0, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
                    if (send < 0)
                            error("ERROR: couldn't send data\n");
                    break;
            }
            int send;
            send = sendto(sockfd, buf, MAX_LINE, 0, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
            if (send < 0)
                    error("ERROR: couldn't send data to server\n");
            //receive data from server
            send = recvfrom(sockfd, buf, MAX_LINE, 0, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
                if(send < 0)
                    error("ERROR: couldn't receive from socket\n");
            if(strcmp(buf, "quit\n") == 0)
                    quit = 0;
            else
                    fputs(buf, stdout);  //print what is received 
    }
}

这是服务器端:

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

#define MAX_LINE 1024
void error(char *msg)
{
    perror(msg);
    exit(1);
}

int main(int argc, char *argv[])
{

    int sockfd, newsockfd, portno;
    char buffer[256];
    struct sockaddr_in serv_addr, cli_addr;
    int clilen;

    if (argc < 2)
    {
            fprintf(stderr,"ERROR, no port provided\n");
            exit(1);
    }
    portno = atoi(argv[1]);
    //create a 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(portno);
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    clilen = sizeof(serv_addr);
    if (sockfd < 0)
    {
        error("ERROR opening socket");
    }

    //bind address to socket
    if (bind(sockfd, (struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0)
            error("ERROR: could not bind");

    //listen for connection request
    listen(sockfd,5);

    int quit = 1;
    while(quit == 1)
    {
            if((newsockfd = accept(sockfd,(struct sockaddr *)&cli_addr, &clilen))<0)
            {
                    perror("Error: could no accept connection");
                    exit(1);
            }
            int n = recvfrom(newsockfd, buffer, MAX_LINE,0,(struct sockaddr *) &serv_addr,&clilen);

            if(strcmp(buffer, "quit\n")== 0)
            {
                    quit = 0;
                    break;
            }
            else
                    fputs(buffer, stdout);
            //get data to be sent
            fgets(buffer, MAX_LINE,stdin);
            if(strcmp(buffer, "quit\n") == 0) //if quit is entered, terminate conn
            {
        quit = 0;
                    int n;
                    n = sendto(newsockfd, buffer, MAX_LINE,0, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
                    if (n<0)
                            error("ERROR: could not send data");
                    break;
            }
            //send data
            n = sendto(newsockfd, buffer, MAX_LINE, 0, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
             if (n<0)
                    error("ERROR sending data");
    }
}

2 个答案:

答案 0 :(得分:1)

@ user58697关于邻近原因是正确的:sendto / recvfrom的最后一个参数必须是指针。但是,我会补充说明。

在此计划中使用recvfrom / sendto毫无意义。你有一个连接的TCP套接字;因此没有理由在每次调用中提供sockaddr参数。地址不会改变,你已经知道它们是什么(即客户端知道它自己的地址并在连接中指定发送方的地址;服务器知道它自己的地址并在接受中接收客户机的地址)。

因此,一旦建立连接,请使用更简单的sendrecv函数。这将简化您的代码,并应同时解决问题。

答案 1 :(得分:1)

双方都在使用此代码犯了一些重大错误。大多数错误的套接字管理和错误的缓冲区管理尝试更像这样的东西:

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

#define SERVER_PORT 5432
#define MAX_LINE 256

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

int main(int argc, char * argv[])
{
    struct hostent *hp;
    struct sockaddr_in serv_addr;
    char *host;
    char buf[MAX_LINE];
    int sockfd, n;

    if (argc != 2)
    {
        fputs("usage: simplex-talk host\n", stderr);
        exit(1);
    }

    host = argv[1];

    hp = gethostbyname(host);
    if (!hp)
    {
        fprintf(stderr, "error: can't find such host: %s\n", host);
        exit(1);
    }
    if (hp->h_addrtype != AF_INET)
    {
        fprintf(stderr, "error: host does not have an IPv4 address: %s\n", host);
        exit(1);
    }

    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    bcopy((char *)hp->h_addr, (char *)&serv_addr.sin_addr.s_addr, hp->h_length);
    serv_addr.sin_port = htons(SERVER_PORT);

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
        error("ERROR creating socket");

    printf("successfully created socket\n");

    if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
    {
        fputs("ERROR: could not connect\n", stderr);
        close(sockfd);
        exit(1);
    }

    printf("successfully connected to server\n");

    int quit = 0;
    while (quit == 0)
    {
        fgets(buf, sizeof(buf), stdin);
        if (strcmp(buf, "quit\n") == 0)
        {
            quit = 1;
            n = send(sockfd, buf, strlen(buf), 0);
            if (n < 0)
                fputs("ERROR: couldn't send data to server\n", stderr);
            break;
        }

        n = send(sockfd, buf, strlen(buf), 0);
        if (n < 0)
        {
            fputs("ERROR: couldn't send data to server\n", stderr);
            break;
        }

        //receive data from server
        n = recv(sockfd, buf, sizeof(buf)-1, 0);
        if (n < 0)
        {
            fputs("ERROR: couldn't receive from server\n", stderr);
            break;
        }
        if (n == 0)
        {
            printf("server disconnected\n");
            break;
        }
        buf[n] = 0;

        if (strcmp(buf, "quit\n") == 0)
            quit = 1;
        else
            fputs(buf, stdout);  //print what is received 
    }

    close(sockfd);
    return 0;
}

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

#define MAX_LINE 256

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

int main(int argc, char *argv[])
{
    int sockfd, clisockfd, portno, n;
    char buffer[MAX_LINE];
    struct sockaddr_in serv_addr, cli_addr;
    int clilen;

    if (argc < 2)
        error("ERROR, no port provided");

    portno = atoi(argv[1]);

    //create a 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(portno);

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
        error("ERROR creating socket");

    //bind address to socket
    if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
        error("ERROR: could not bind socket");

    //listen for connection request
    if (listen(sockfd, 5) < 0)
        error("ERROR: could not listen on socket");

    int quit = 0;
    while (quit == 0)
    {
        clilen = sizeof(serv_addr);
        clisockfd = accept(sockfd, (struct sockaddr *)&cli_addr, &clilen);
        if (clisockfd < 0)
            error("ERROR: could not accept connection");

        while (quit == 0)
        {
            n = recv(clisockfd, buffer, sizeof(buffer)-1, 0);
            if (n < 0)
            {
                fputs("ERROR: couldn't receive from client\n", stderr);
                break;
            }
            if (n == 0)
            {
                printf("client disconnected\n");
                break;
            }
            buffer[n] = 0;

            if (strcmp(buffer, "quit\n") == 0)
            {
                quit = 1;
                break;
            }

            fputs(buffer, stdout);

            //get data to be sent
            fgets(buffer, sizeof(buffer), stdin);
            if (strcmp(buffer, "quit\n") == 0) //if quit is entered, terminate conn
            {
                quit = 1;
                n = send(clisockfd, buffer, strlen(buffer), 0);
                if (n < 0)
                    fputs("ERROR: could not send data to client\n", stderr);
                break;
            }

            //send data
            n = send(clisockfd, buffer, strlen(buffer)-1, 0);
            if (n < 0)
            {
                fputs("ERROR sending data to client\n", stderr);
                break;
            }
        }

        close(clisockfd);
    }

    close(sockfd);
    return 0;
}

现在,说到这一点,请注意TCP是一种流式传输。 send()recv()之间没有一对一的关系,也没有消息的概念,就像这段代码所假设的那样。发件人可以发送"hello joe\n"之类的消息,接收者可以读取"hello" " joe" "\n",具体取决于TCP在传输过程中如何决定中断数据。你真的需要考虑到这一点。读取原始字节并将它们附加到缓冲区的末尾。检查缓冲区是否有消息终止符(在本例中为\n)。如果找到,请处理完成的消息并将其从缓冲区中删除。重复,直到缓冲区中找不到更多的终止符。将未处理的数据留在缓冲区中,以便随后的读取完成。

我将此作为练习留给你。