c - 使用accept接收顺序数据

标签 c sockets networking

我正在尝试在 2 个站(A 和 B)之间建立通信。 A 应该向 B 发送多个数据(反之亦然):“多个数据”意味着我想在单个接受 session 中使用来自两个站的 sendrecv。目前我只是尝试为每个连接发送 1 个以上的数据包。由于我不熟悉处理套接字,因此我很可能没有完全理解套接字背后的概念。在以下代码中,接收器成功检索到发送器发送的第一个 int,但无法检索到后续的。你能告诉我我做错了什么吗?

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

#define DATA_TO_SEND_LENGTH 9
#define PRINT_ERRNO_EXIT(who,id) { \
        int verrno=errno; \
        printf("%s ERRNO: %d\nexit id: %d\n",who==0?"sender":"receiver",verrno,id); \
        return id; \
}

int main(){
    char* ipaddress="127.0.0.1";
    int portNumber=12345;
    int socketOptionalValue=1;
    const int dataToSend[]={1,2,3,4,5,6,7,8,9};

    if (fork()==0){
        // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
        // @@@@@@@@@@@@@@@ CHILD: HE IS THE SENDER @@@@@@@@@@@@@@@@
        // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
        int socketSenderDescriptor;
        struct sockaddr_in destinationInfos;
        struct sockaddr_in tmp;
        int i;
        // ************** CREATE AND OPEN THE SOCKET *****************

        inet_aton(ipaddress,&tmp.sin_addr);
        socketSenderDescriptor = socket(AF_INET, SOCK_STREAM, 0); // socket file descriptor
        if (socketSenderDescriptor < 0 ){
            PRINT_ERRNO_EXIT(0,1);
        }
        if (setsockopt(socketSenderDescriptor,SOL_SOCKET,SO_REUSEADDR,&socketOptionalValue,sizeof(socketOptionalValue))!=0){
            PRINT_ERRNO_EXIT(0,2);
        }
        if (setsockopt(socketSenderDescriptor,SOL_SOCKET,SO_KEEPALIVE,&socketOptionalValue,sizeof(socketOptionalValue))!=0){
            PRINT_ERRNO_EXIT(0,2);
        }
        memset(&destinationInfos,0,sizeof(destinationInfos)); //clean the structure used to store destination information
        destinationInfos.sin_family=AF_INET; //we want to use inet class
        destinationInfos.sin_addr.s_addr=tmp.sin_addr.s_addr;//Set destination IP
        destinationInfos.sin_port=htons(portNumber); //communiction port number
        //connection
        if (connect(socketSenderDescriptor,(struct sockaddr *)&(destinationInfos),sizeof(destinationInfos)) < 0){
            PRINT_ERRNO_EXIT(0,3);
        }
        // ******************* SEND DATA **********************
        sleep(1); //wait for the parent to start listening...
        for (i=0;i<DATA_TO_SEND_LENGTH;i++){
            printf("sender: i'm sending %d\n",dataToSend[i]);
            send(socketSenderDescriptor,&(dataToSend[i]),sizeof(dataToSend[i]),0);
            sleep(1);
        }
        // ******************* CLOSE SOCKET *******************
        if (close(socketSenderDescriptor)!=0){
            PRINT_ERRNO_EXIT(0,4);
        }
        printf("SENDER FINISH!\n");
        return EXIT_SUCCESS;
    }else {
        // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
        // @@@@@@@@@@@@@@@@ PARENT: HE IS THE RECEIVER @@@@@@@@@@@@
        // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
        struct sockaddr_in remoteSocketInfos;
        struct sockaddr_in localSocketInfos;
        int localSocketDescriptor;
        int sessionSocket;
        int valueReceived;
        int i=0;
        static socklen_t socksize=sizeof(struct sockaddr_in);
        // ******************* CREATE AND BIND THE SOCKET ******************
        //write inside localSocketInfos the information about our server
        memset(&localSocketInfos,0,sizeof(localSocketInfos));
        localSocketInfos.sin_family=AF_INET;
        localSocketInfos.sin_addr.s_addr=htonl(INADDR_ANY);
        localSocketInfos.sin_port= htons(portNumber);

        //create the main socket where all connection to our server will be received
        localSocketDescriptor=socket(AF_INET,SOCK_STREAM,0);
        if (localSocketDescriptor<0){
            PRINT_ERRNO_EXIT(1,1);
        }
        //On Linux, SO_REUSEADDR allows you to bind to an address unless an active connection is present
        //see https://stackoverflow.com/questions/4979425/difference-between-address-in-use-with-bind-in-windows-and-on-linux-errno
        setsockopt(localSocketDescriptor,SOL_SOCKET,SO_REUSEADDR,&socketOptionalValue,sizeof(socketOptionalValue));
        //Bind our socket to the socketDescription: this allows the machine to know the data
        //received on the port specified in the sockaddr_in should be handled by our specified socket.
        if (bind(localSocketDescriptor,(struct sockaddr*)&localSocketInfos,sizeof(struct sockaddr))!=0){
            PRINT_ERRNO_EXIT(1,2);
        }
        //tells our program to start listening the socket we have created. This will NOT block the flow of the program because
        //we are not waiting anything at all.
        if (listen(localSocketDescriptor,10)!=0){
            PRINT_ERRNO_EXIT(1,3);
        }
        // ************************ WAIT FOR CONNECTIONS ********************
        do {
            i++;
            printf("Waiting transmission on %s, port %d...\n",inet_ntoa(localSocketInfos.sin_addr),localSocketInfos.sin_port);
            sessionSocket=accept(localSocketDescriptor,(struct sockaddr*)&remoteSocketInfos,&socksize);
            if (sessionSocket<0){
                int verrno=errno;
                printf("receiver ERRNO: %d\n",verrno);
                continue;
            }
            printf("Incoming transmission from %s, port %d...\n",inet_ntoa(remoteSocketInfos.sin_addr),remoteSocketInfos.sin_port);
            recv(sessionSocket,&valueReceived,sizeof(valueReceived),0);
            printf("DATA READ! %d\n",valueReceived);
            close(sessionSocket);
        } while(i<DATA_TO_SEND_LENGTH);
        if (close(localSocketDescriptor)==0){
            PRINT_ERRNO_EXIT(1,4);
        }
        printf("RECEIVER FINISH!\n");
        return EXIT_SUCCESS;
    }
}

我还发布了我的程序的输出,希望它可以帮助您找到我做错的地方:

Waiting transmission on 0.0.0.0, port 14640...
Incoming transmission from 127.0.0.1, port 63718...
sender: i'm sending 1
DATA READ! 1
Waiting transmission on 0.0.0.0, port 14640...
sender: i'm sending 2
sender: i'm sending 3
sender: i'm sending 4
sender: i'm sending 5
sender: i'm sending 6
sender: i'm sending 7
sender: i'm sending 8
sender: i'm sending 9
SENDER FINISH!

最佳答案

在接收进程中,accept()只需要调用一次。由于您将 accept() 放入 while 循环中,因此循环的第二次迭代将在 accept 上阻塞,因为没有新连接进来了。

一个简单的修复方法是将 accept 置于循环之外。您可以尝试以下修复并查看它是否有效。请注意,close 也会移到循环之后。

    // ************************ WAIT FOR CONNECTIONS ********************
    printf("Waiting transmission on %s, port %d...\n",inet_ntoa(localSocketInfos.sin_addr),localSocketInfos.sin_port);
    sessionSocket=accept(localSocketDescriptor,(struct sockaddr*)&remoteSocketInfos,&socksize);
    if (sessionSocket<0){
        int verrno=errno;
        printf("receiver ERRNO: %d\n",verrno);
        PRINT_ERRNO_EXIT(1,5);
    }
    do {
        i++;
        printf("Incoming transmission from %s, port %d...\n",inet_ntoa(remoteSocketInfos.sin_addr),remoteSocketInfos.sin_port);
        recv(sessionSocket,&valueReceived,sizeof(valueReceived),0);
        printf("DATA READ! %d\n",valueReceived);
    } while(i<DATA_TO_SEND_LENGTH);
    close(sessionSocket);

关于c - 使用accept接收顺序数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25691488/

相关文章:

c - 删除函数无法删除链表中除第一个节点之外的节点

c++ - fatal error : error when reading "C:\OpenSSL-Win32\lib\libeay32.lib": unexpected end-of-file

c++ - C select() 超时 STDIN 单字符(无 ENTER)

java - 安卓编译器: Cannot resolve symbol string

c++ - 解码 JPEG 以获得未压缩的数据

c - 从 C 中的用户输入获取数组值

java - 通过套接字发送文件 - 缓冲区大小

linux - 使用 Linux 网络设备别名时指定 TCP 套接字的源 IP 地址

java - 如果 ServerSocket 接受连接,它会继续等待进一步的请求吗?

linux - wget 失败,没有主机 wget 的路由