在 C 中使用多线程服务器时无法完成文件传输
Posted
技术标签:
【中文标题】在 C 中使用多线程服务器时无法完成文件传输【英文标题】:Can't finish transferring a file when using a multi-threaded server in C 【发布时间】:2015-03-17 07:37:37 【问题描述】:我有一个多线程客户端,可以将一批文件传输到客户端自己创建的新目录。我的客户端曾经使用单线程服务器。
对于一个任务,我应该将我的单线程服务器转换为多线程服务器,为每个客户端请求创建一个新线程。我还应该对整个操作进行计时并将其输出到客户端(当服务器是单线程时我开始工作)。多线程客户端(与单线程服务器一起使用)和多线程服务器(效果不佳)的代码如下:
client.c
#include <sys/uio.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <netdb.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <pthread.h>
#include <string.h>
#include <strings.h>
#include "Timer.h"
#define N_THREADS 10
char * files[] =
"/usr/share/dict/words",
"/usr/include/sqlite3.h",
"/usr/include/tclDecls.h",
"/usr/include/bfd.h",
"/usr/include/libmng.h",
"/usr/include/elf.h",
"/usr/include/gmpxx.h",
"/usr/include/tkDecls.h",
"/usr/include/H5overflow.h",
"/usr/include/tcl.h",
"/usr/include/gmp-x86_64.h",
"/usr/include/curses.h",
"/usr/include/lcms.h",
"/usr/include/netapi.h",
"/usr/include/gcrypt.h",
"/usr/include/zlib.h",
"/usr/include/ldap.h",
"/usr/include/geos_c.h",
"/usr/include/kdb.h",
"/usr/include/tk.h",
"/usr/include/yaml.h"
;
#define files_length() (sizeof files / sizeof files[0])
void error(char *msg)
perror(msg);
exit(-1);
struct sockaddr_in make_server_addr(char *host, short port)
struct sockaddr_in addr;
bzero(&addr, sizeof addr);
struct hostent *hp = gethostbyname(host);
if ( hp == 0 )
error(host);
addr.sin_family = AF_INET;
bcopy(hp->h_addr_list[0], &addr.sin_addr, hp->h_length);
addr.sin_port = htons(port);
return addr;
int connect_socket(char *host, short port)
int status;
int tries = 3;
struct sockaddr_in addr = make_server_addr(host, port);
int s = socket(AF_INET, SOCK_STREAM, 0);
if ( s == -1 )
error("socket()");
status = connect(s, (struct sockaddr*)&addr, sizeof addr);
if ( status < 0 )
error("connect refused");
return s;
void request_file_from_server(int server_socket, char *file)
int len = strlen(file);
int n = write(server_socket, file, len);
if ( n != len )
error("short write");
void read_file_from_server(int server_socket, char *file)
char buf[BUFSIZ];
int n;
mode_t mode = 0666;
int ofd = open(file, O_WRONLY | O_CREAT, mode);
if ( ofd == -1 )
perror("open()");
while ( (n = read(server_socket, buf, BUFSIZ)) > 0 )
write(ofd, buf, n);
close(ofd);
struct Thread_data
int id;
pthread_t thread_id;
char * host;
short port;
char path[BUFSIZ];
;
void make_file_name(char *local_name, char *dir, char *original_path)
char *p = rindex(original_path, '/');
if ( !p )
error("rindex()");
sprintf(local_name, "%s/%s", dir, p+1);
int remote_copy(struct Thread_data * data, char * file)
int server_socket = connect_socket(data->host, data->port);
request_file_from_server(server_socket, file);
char local_name[BUFSIZ];
make_file_name(local_name, data->path, file);
read_file_from_server(server_socket, local_name);
close(server_socket);
void make_empty_dir_for_copies(struct Thread_data * data)
mode_t mode = 0777;
sprintf(data->path, "./Thread_%d", (data->id + 1));
mkdir(data->path, mode);
#define N_FILES_TO_COPY files_length() // copy them all
void *thread_work(void *arg)
struct Thread_data * data = (struct Thread_data *)arg;
make_empty_dir_for_copies(data);
for ( int i=0; i < N_FILES_TO_COPY; ++i )
remote_copy(data, files[i]);
pthread_exit(0);
void start_threads(char *host, short port, struct Thread_data thread_args[])
for ( int i = 0; i < N_THREADS; ++i )
struct Thread_data * t = &thread_args[i];
t->id = i;
t->host = host;
t->port = port;
pthread_create(&t->thread_id, NULL, thread_work, t);
void join_threads(struct Thread_data thread_args[], double *eTime)
for ( int i=0; i < N_THREADS; i++ )
pthread_join(thread_args[i].thread_id, NULL);
Timer_elapsedUserTime(eTime);
printf("Elapsed time for transferring all files: %lf\n", *eTime);
pthread_exit(0);
int main(int argc, char *argv[])
if ( argc != 3 )
fprintf(stderr, "Usage: %s host port\n", argv[0]);
exit(-1);
struct Thread_data thread_args[N_THREADS];
char *host = argv[1];
short port = atoi(argv[2]);
double eTime;
Timer_start();
start_threads(host,port,thread_args);
join_threads(thread_args, &eTime);
server.c
#include <sys/types.h>
#include <signal.h>
#include <sys/uio.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <pthread.h>
#include <string.h>
#include "Timer.h"
#define BACKLOG 200
// more than this in the queue, and client connect will fail
#define NUM_THREADS 200
void error(char *msg)
fprintf(stderr, "%s\n", msg);
exit(-1);
struct sockaddr_in make_server_addr(short port)
struct sockaddr_in addr;
memset(&addr, 0, sizeof addr);
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
return addr;
int create_server_socket(short port)
int s = socket(AF_INET, SOCK_STREAM, 0);
int optval = 1;
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval);
struct sockaddr_in my_addr = make_server_addr(port);
if ( s == -1 )
error("socket()");
bind(s, (struct sockaddr*)&my_addr, sizeof my_addr);
listen(s, BACKLOG);
return s;
void get_file_request(int socket, char *fileName)
char buf[BUFSIZ];
int n = read(socket, buf, BUFSIZ);
if ( n < 0 )
error("read from socket");
buf[n] = '\0';
strcpy(fileName, buf);
printf("Server got file name of '%s'\n", fileName);
void write_file_to_client_socket(char *file, int socket)
char buf[BUFSIZ];
int n;
int ifd = open(file, O_RDONLY);
if ( ifd == -1 )
error("open()");
while ( (n = read(ifd, buf, BUFSIZ)) > 0 )
write(socket, buf, n);
close(ifd);
void * handle_request(void * c_socket)
int *client_socket = (int*)c_socket;
char fileName[BUFSIZ];
get_file_request(*client_socket, fileName);
write_file_to_client_socket(fileName, *client_socket);
close(*client_socket);
pthread_exit(0);
return NULL;
void time_out(int arg)
fprintf(stderr, "Server timed out\n");
exit(0);
void set_time_out(int seconds)
struct itimerval value = 0;
// bzero(&value, sizeof value);
/* timerclear(&value.it_interval); timerclear(&value.it_value); */
value.it_value.tv_sec = seconds;
setitimer(ITIMER_REAL, &value, NULL);
signal(SIGALRM, time_out);
void accept_client_requests(int server_socket)
pthread_t threads;
int client_socket;
struct sockaddr_in client_addr;
socklen_t sin_size = sizeof client_addr;
set_time_out(10);
while ( (client_socket =
accept(server_socket, (struct sockaddr*)&client_addr, &sin_size)) )
set_time_out(10);
pthread_create(&threads,0, handle_request,&client_socket);
int main(int argc, char *argv[])
if ( argc != 2 )
error("usage: server port");
short port = atoi(argv[1]);
int server_socket = create_server_socket(port);
accept_client_requests(server_socket);
shutdown(server_socket, 2);
return 0;
当我使用accept
并创建一个新的pthread
时,使用handle_request
时会出现此问题。无论最后一个文件是什么(在本例中为/usr/include/yaml.h
),它都会挂起,然后超时。如果没有超时,它将无限期地挂起。
我对使用 pthread 的多线程了解不多,所以我只是脱离了我的教授的说明,基本上说创建线程并像在单线程服务器中一样处理请求。在我的单线程服务器中,handle_request
以 int 形式传递(现在已转换)。
有谁知道为什么我的服务器会一直挂在最后传输的文件上直到超时?
【问题讨论】:
【参考方案1】:accept_client_requests
函数存在缺陷。你有一个变量
int client_socket;
那个变量的地址被传递给pthread_create
pthread_create(&threads,0, handle_request,&client_socket);
pthread_create
将指针传递给 handle_request
,后者将其存储为本地指针
int *client_socket = (int *)c_socket;
问题是指针仍然指向client_socket
函数中的client_socket
变量。所以当accept_client_requests
得到另一个连接时,client_socket
会发生变化,并且当前运行的每个线程的client_socket
都会发生变化,这应该会引起各种混乱。
解决方案是malloc
和int
来保存client_socket
,然后将该地址传递给线程。
int *temp = malloc( sizeof(int) );
*temp = client_socket;
pthread_create(&threads, 0, handle_request, temp);
pthread_detach(threads);
当线程结束时,它应该free
内存。
accept_client_requests
函数还应该在它创建的每个线程上调用pthread_detach
,以便在线程完成时可以回收资源。
如果没有pthread_detach
,系统会在清理线程之前看到pthread_join
。
【讨论】:
这确实有道理,它解决了我试图弄清楚如何不只是将指针传递给 handle_requests 函数的问题。但是当我尝试这个解决方案(并在handle_requests中的while循环之外释放内存)时,它只创建了几个目录,其中只有一个文件,并在崩溃之前给了我一堆连接被拒绝的错误消息。 @Alex,我忘了提pthread_detach
,更新了答案。
pthread_detach(pthread_self());使用 pthread_create 后确实创建了 10 个目录,但它似乎只复制了一个文件名,其中没有任何内容,并声明连接被拒绝。我应该在句柄请求中使用 pthread_detach 吗?
@Alex 我会将pthread_detach
放在pthread_create
之后(请参阅更新的答案)。请注意pthread_detach
只是告诉操作系统您不会在该线程上调用pthread_join
,因此操作系统可以在线程完成后立即清理。
非常感谢您帮助我,实际上我现在正在学习很多关于这些东西的知识。但是运行服务器然后客户端给了我这个:对于服务器:read from socket, read from socket, read from socket
,从客户端:Connection refused
。它似乎仍然不想复制足够多的目录,并且仍然只复制一个文件。 :((以上是关于在 C 中使用多线程服务器时无法完成文件传输的主要内容,如果未能解决你的问题,请参考以下文章