0

I created a simple tftp server that only handles read requests (RRQ). Everything was working fine until I started to make a multi-threaded version of the server. In the application, I simply receive requests in the main thread and I then forward the request to a new thread that does the packet analysis. Therefore, I need to forward the socket, the received packet and the sockaddr_in struct that contains the client information to the thread. With that said, I created a struct that holds all of these and forward them to the pthread.

I suspect the problem to be in the struct clientThread initialization part and forwarding part; since I'm certain of the correctness of the processing inside connection_handler.

Note: You can use the tftp client that comes with linux to test it.

Here's the code I've written so far (Threaded Version). Please compile it with the -pthread flag...

#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <time.h>

#define TIMEOUT 5000
#define RETRIES 3

void *connection_handler(void *);

struct clientThread 
{
    int clientSock;
    char buffer[1024];  
    struct sockaddr_in client;
};

int main()
{
    char buffer[1024];
    int udpSocket, client_socket, nBytes;
    struct sockaddr_in serverAddr, client;
    socklen_t addr_size;

    udpSocket = socket(AF_INET, SOCK_DGRAM, 0);

    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(69);
    serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    memset(serverAddr.sin_zero, '\0', sizeof serverAddr.sin_zero); 

    bind(udpSocket, (struct sockaddr *) &serverAddr, sizeof(serverAddr));

    while(1)
    {
        memset(buffer, 0, 1024);
        nBytes = recvfrom(udpSocket,buffer,1024,0,(struct sockaddr *)&client, &addr_size);

        // Creating a thread and passing the packet received, the socket and the sockaddr_in struct...
        pthread_t client_thread;
        struct clientThread clientT;
        strcpy(clientT.buffer,buffer);
        clientT.clientSock = udpSocket;
        clientT.client = client;
        pthread_create(&client_thread, NULL, connection_handler, &clientT);
    }

    return 0;
}


void* connection_handler (void *clientThreaded)
{
        char buffer[1024], filename[200], mode[20], *bufindex, opcode;

        struct clientThread *cthread = clientThreaded;
        int udpSocket = cthread->clientSock;
        strcpy(buffer, cthread->buffer);
        struct sockaddr_in client = cthread->client;

        bufindex = buffer;
        bufindex++;

        // Extracting the opcode from the packet...
        opcode = *bufindex++;

        // Extracting the filename from the packet...
        strncpy(filename, bufindex, sizeof(filename)-1);

        bufindex += strlen(filename) + 1;

        // Extracting the mode from the packet...
        strncpy(mode, bufindex, sizeof(mode)-1);

        // If we received an RRQ...
        if (opcode == 1)
        {
                puts("Received RRQ Request");
                struct timeval tv;
                tv.tv_sec = 5;
                char path[70] = "tmp/";
                char filebuf [1024];
                int count = 0, i;  // Number of data portions sent
                unsigned char packetbuf[1024];
                char recvbuf[1024];
                socklen_t recv_size;

                socklen_t optionslength = sizeof(tv);
                setsockopt(udpSocket, SOL_SOCKET, SO_RCVTIMEO, &tv, optionslength);

                FILE *fp;
                char fullpath[200];
                strcpy(fullpath, path);
                strncat(fullpath, filename, sizeof(fullpath) -1);
                fp = fopen(fullpath, "r");
                if (fp == NULL)
                    perror("");

                memset(filebuf, 0, sizeof(filebuf));


                while (1)               
                {
                        int acked = 0;
                        int ssize = fread(filebuf, 1 , 512, fp);
                        count++;
                        sprintf((char *) packetbuf, "%c%c%c%c", 0x00, 0x03, 0x00, 0x00);
                        memcpy((char *) packetbuf + 4, filebuf, ssize);
                        packetbuf[2] = (count & 0xFF00) >> 8;
                        packetbuf[3] = (count & 0x00FF);

                        int len = 4 + ssize;

                        memset(recvbuf, 0, 1024);
                        printf("\nSending Packet #%i", count);
                        sendto(udpSocket, packetbuf, len, 0, (struct sockaddr *) &client, sizeof(client));

                        for (i=0; i<RETRIES; i++)
                        {
                            int result = recvfrom(udpSocket, recvbuf, 1024, 0, (struct sockaddr *) &client, &recv_size);

                                if ((result == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
                                {
                                    sendto(udpSocket, packetbuf, len, 0, (struct sockaddr *) &client, sizeof(client));
                                    printf("\nRetransmitting Packet #%i", count);
                                }

                                else if (result == -1)
                                {
                                   // Handle Error
                                }

                                else
                                {
                                    acked++;
                                        printf("\nReceived ACK For Data Packet #%i", count);
                                        break;
                                }
                    }


                        if (acked!=1)
                        {
                            puts("\nGave Up Transmission After 3 Retries");
                                break;
                        }

                        if (ssize != 512)
                            break;
                }
    }

    return 0;
}

Here's my code for the Non-threaded version...

#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#define TIMEOUT 5000
#define RETRIES 3

int main()
{
    int udpSocket, nBytes;
    char buffer[1024], filename[200], mode[20], *bufindex, opcode;
    struct sockaddr_in serverAddr, client;
    struct sockaddr_storage serverStorage;
    socklen_t addr_size;

    udpSocket = socket(AF_INET, SOCK_DGRAM, 0);

    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(69);
    serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    memset(serverAddr.sin_zero, '\0', sizeof serverAddr.sin_zero); 

    bind(udpSocket, (struct sockaddr *) &serverAddr, sizeof(serverAddr));

    while(1)
    {
        memset(buffer, 0, 1024);
        nBytes = recvfrom(udpSocket,buffer,1024,0,(struct sockaddr *)&client, &addr_size);
        printf("%s",buffer);
        bufindex = buffer;
        bufindex++;

        // Extracting the opcode from the packet...     
        opcode = *bufindex++;

        // Extracting the filename from the packet...
        strncpy(filename, bufindex, sizeof(filename)-1);

        bufindex += strlen(filename) + 1;

        // Extracting the mode from the packet...       
        strncpy(mode, bufindex, sizeof(mode)-1);

        // If we received an RRQ...
        if (opcode == 1)
        {
                puts("Received RRQ Request");
                struct timeval tv;
                tv.tv_sec = 5;
                char path[70] = "tmp/";
                char filebuf [1024];
                int count = 0, i;  // Number of data portions sent
                unsigned char packetbuf[1024];
                char recvbuf[1024];
                socklen_t recv_size;

                socklen_t optionslength = sizeof(tv);
                setsockopt(udpSocket, SOL_SOCKET, SO_RCVTIMEO, &tv, optionslength);

                FILE *fp;
                char fullpath[200];
                strcpy(fullpath, path);
                strncat(fullpath, filename, sizeof(fullpath) -1);
                fp = fopen(fullpath, "r");
                if (fp == NULL)
                        perror("");

                 memset(filebuf, 0, sizeof(filebuf));


                while (1)
                {
                        int acked = 0;
                        int ssize = fread(filebuf, 1 , 512, fp);
                        count++;
                        sprintf((char *) packetbuf, "%c%c%c%c", 0x00, 0x03, 0x00, 0x00);
                        memcpy((char *) packetbuf + 4, filebuf, ssize);
                        packetbuf[2] = (count & 0xFF00) >> 8;
                        packetbuf[3] = (count & 0x00FF);

                        int len = 4 + ssize;

                        memset(recvbuf, 0, 1024);
                        printf("\nSending Packet #%i", count);
                        sendto(udpSocket, packetbuf, len, 0, (struct sockaddr *) &client, sizeof(client));

                        for (i=0; i<RETRIES; i++)
                        {
                                int result = recvfrom(udpSocket, recvbuf, 1024, 0, (struct sockaddr *) &client, &recv_size);

                                if ((result == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
                                {
                                    sendto(udpSocket, packetbuf, len, 0, (struct sockaddr *) &client, sizeof(client));
                        printf("\nRetransmitting Packet #%i", count);
                                }

                                else if (result == -1)
                                {
                                        // Handle Error
                                }

                                else
                                {
                                        acked++;
                        printf("\nReceived ACK For Data Packet #%i", count);
                                        break;
                                }
                        }

                         if (acked!=1)
                        {
                            puts("\nGave Up Transmission After 3 Retries");
                            break;
                        }

                        if (ssize != 512)
                                break;
                }
        }
    }   
    return 0;
}

Thanks in advance :)

4

2 回答 2

1

您在端口 69 上循环侦听,但实际数据传输将从另一个随机选择的端口执行(请阅读 RFC 1350)。然后你的主循环必须为每次新的传输创建一个新线程,这个新线程应该接收一个结构,其中包含要服务的文件的路径、目标 IP/端口、随机选择的本地端口等。

将结构指针传递给线程时必须考虑的是支持该结构的内存在你的情况下

     struct clientThread clientT;

是在堆栈中动态创建的,当然,当代码块超出范围时(在您的情况下,在每个循环中),该结构将被“丢弃”,这意味着您正在将指向“即将成为垃圾”的指针传递给正义创建线程。

我建议在将结构传递给刚刚创建的线程时使用 malloc/free。

最后,您的主线程(调度程序)应该维护一个结构,考虑到所有创建的线程及其状态。这对于检测死线程或在传输正在进行时需要关闭主程序是必要的。

正如您所见,即使对于像 TFTP 这样的简单协议,实现服务器也不是一件容易的事。

于 2015-04-15T13:34:51.167 回答
0

您的连接处理程序在这里是错误的,因为您传递给每个线程的套接字上没有任何锁定。

大多数基于 udp 的服务器实际上并没有分叉多个线程。TCP 服务器可以做到这一点,因为每次接受都会获得一个新套接字,该套接字可以委托给一个不会被任何其他线程使用的新线程。

但是对于 udp,您基本上对所有线程都使用相同的套接字,这是不行的。如果您要在套接字上提供保护,您可以使其工作,但您将失去通过使其成为多线程而试图获得的好处。

于 2015-04-15T10:27:10.787 回答