多线程 TFTP 服务器 - 问题与 pthreads
Multi-threaded TFTP Server - Issue With pthreads
我创建了一个简单的 tftp 服务器,它只处理读取请求 (RRQ)。在我开始制作服务器的多线程版本之前,一切都运行良好。在应用程序中,我只是在主线程中接收请求,然后将请求转发到执行数据包分析的新线程。因此,我需要将套接字、接收到的数据包和包含客户端信息的 sockaddr_in
结构转发给线程。话虽如此,我创建了一个结构来保存所有这些并将它们转发给 pthread。
我收到两条相同的错误消息,一条在主错误消息中,另一条在连接处理程序中。问题似乎在于在结构中引用这些变量并在线程中检索它们。看来问题出在以下语句中:in connection_handler(): buffer = cthread->buffer;在 main() 中:clientT.buffer = buffer;
这是代码,到目前为止我已经写好了...
#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>
#define TIMEOUT 5000
#define RETRIES 3
void *connection_handler(void *);
struct clientThread
{
int clientSock;
char opcode;
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, '[=10=]', 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);
//opcode = cthread->opcode;
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;
}
提前致谢:)
在主循环中,变量clientT
在该循环内是局部变量,一旦循环迭代该变量将超出范围,任何指向它的指针都将失效。取消引用这样的指针将导致 undefined behavior.
相反,您应该使用 malloc
动态分配结构,并传递该指针。在线程中完成结构后,不要忘记 free
结构。
the current posted code, 7pm PDT,
causes the compiler to emit several warnings
(all of which need to be fixed)
plus some errors.
Errors like: 'buffer = cthread->buffer;'
is copying the address of 'cthread->buffer' to the address of the array 'buffer'.
That probably is not what is wanted.
suggest something similar to: strcpy(buffer, cthread->buffer);
#include <time.h> is missing
so this line: 'struct timeval tv;' is referencing an undefined struct.
The compiler needs to be run with all warnings enabled.
then fix the warnings and the errors.
as a minimum, for gcc, use the parameters:
-Wall -Wextra -Wshadow -pedantic
There are plenty of other error/warning messages that can be enabled
but the above list will catch ~99percent of all errors/warnings
Some googling should find info on how to fix the current errors and warnings.
(although to me, the error/warning messages
make it very clear as to the root cause of the problem.
however, I have been programming for 40 some years)
Each compiler message indicates:
1) which line in the current translation unit (file)
2) and what is wrong with that line.
我创建了一个简单的 tftp 服务器,它只处理读取请求 (RRQ)。在我开始制作服务器的多线程版本之前,一切都运行良好。在应用程序中,我只是在主线程中接收请求,然后将请求转发到执行数据包分析的新线程。因此,我需要将套接字、接收到的数据包和包含客户端信息的 sockaddr_in
结构转发给线程。话虽如此,我创建了一个结构来保存所有这些并将它们转发给 pthread。
我收到两条相同的错误消息,一条在主错误消息中,另一条在连接处理程序中。问题似乎在于在结构中引用这些变量并在线程中检索它们。看来问题出在以下语句中:in connection_handler(): buffer = cthread->buffer;在 main() 中:clientT.buffer = buffer;
这是代码,到目前为止我已经写好了...
#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>
#define TIMEOUT 5000
#define RETRIES 3
void *connection_handler(void *);
struct clientThread
{
int clientSock;
char opcode;
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, '[=10=]', 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);
//opcode = cthread->opcode;
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;
}
提前致谢:)
在主循环中,变量clientT
在该循环内是局部变量,一旦循环迭代该变量将超出范围,任何指向它的指针都将失效。取消引用这样的指针将导致 undefined behavior.
相反,您应该使用 malloc
动态分配结构,并传递该指针。在线程中完成结构后,不要忘记 free
结构。
the current posted code, 7pm PDT,
causes the compiler to emit several warnings
(all of which need to be fixed)
plus some errors.
Errors like: 'buffer = cthread->buffer;'
is copying the address of 'cthread->buffer' to the address of the array 'buffer'.
That probably is not what is wanted.
suggest something similar to: strcpy(buffer, cthread->buffer);
#include <time.h> is missing
so this line: 'struct timeval tv;' is referencing an undefined struct.
The compiler needs to be run with all warnings enabled.
then fix the warnings and the errors.
as a minimum, for gcc, use the parameters:
-Wall -Wextra -Wshadow -pedantic
There are plenty of other error/warning messages that can be enabled
but the above list will catch ~99percent of all errors/warnings
Some googling should find info on how to fix the current errors and warnings.
(although to me, the error/warning messages
make it very clear as to the root cause of the problem.
however, I have been programming for 40 some years)
Each compiler message indicates:
1) which line in the current translation unit (file)
2) and what is wrong with that line.