gpt4 book ai didi

c - 使用套接字进行多线程文件传输

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:45:41 26 4
gpt4 key购买 nike

我正在尝试用 C 语言制作一个多线程的服务器-客户端文件传输系统。有些客户端会发送或列出或做一些其他选择(在一个开关盒中,您可以看到)和一个存储文件的服务器和为很多客户提供服务。

多线程思想在我看来真的很难。它需要太多的经验而不是知识。我已经在这个项目上工作了一个多星期,但我一直无法解决问题。

有4种选择:第一种是列出客户端在其目录下的本地文件,第二种是列出客户端和服务器之间传输的文件,第三种是从用户那里读取文件名并将文件复制到服务器目录中。

我这里的重要问题是关于多线程。我无法连接多个客户端。我已经从 a 到 z 一堆代码中阅读了很多次,但我真的无法捕捉到我的错误并且被卡住了。

另一个问题是,当 SIGINT 被捕获时,客户端将结束,但是,例如,在按 ctrl-c 选择列表文件后,它不会停止。服务器文件也有同样的问题。与客户端的捕获相比,它更麻烦,因为当服务器收到SIGINT时,客户端将分别与服务器断开连接。

感谢您的帮助!


服务器.c

/*
Soner
Receive a file over a socket.

Saves it to output.tmp by default.

Interface:

./executable [<port>]

Defaults:

- output_file: output.tmp
- port: 12345
*/

#define _XOPEN_SOURCE 700

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <arpa/inet.h>
#include <fcntl.h>
#include <netdb.h> /* getprotobyname */
#include <netinet/in.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <unistd.h>

#include <pthread.h>

pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;

enum { PORTSIZE = 5 };

void* forClient(void* ptr);
void sig_handler(int signo)
{
if (signo == SIGINT)
printf("!! OUCH, CTRL - C received by server !!\n");
}

int main(int argc, char **argv) {
struct addrinfo hints, *res;
int enable = 1;
int filefd;
int server_sockfd;
unsigned short server_port = 12345u;
char portNum[PORTSIZE];

socklen_t client_len[BUFSIZ];
struct sockaddr_in client_address[BUFSIZ];
int client_sockfd[BUFSIZ];
int socket_index = 0;

pthread_t threads[BUFSIZ];

if (argc != 2) {
fprintf(stderr, "Usage ./server <port>\n");
exit(EXIT_FAILURE);
}
server_port = strtol(argv[1], NULL, 10);

memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET; //ipv4
hints.ai_socktype = SOCK_STREAM; // tcp
hints.ai_flags = AI_PASSIVE; // fill in my IP for me

sprintf(portNum, "%d", server_port);
getaddrinfo(NULL, portNum, &hints, &res);

server_sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (server_sockfd == -1) {
perror("socket");
exit(EXIT_FAILURE);
}

if (setsockopt(server_sockfd, SOL_SOCKET, (SO_REUSEPORT | SO_REUSEADDR), &enable, sizeof(enable)) < 0) {
perror("setsockopt(SO_REUSEADDR) failed");
exit(EXIT_FAILURE);
}

if (bind(server_sockfd, res->ai_addr, res->ai_addrlen) == -1) {
perror("bind");
exit(EXIT_FAILURE);
}

if (listen(server_sockfd, 5) == -1) {
perror("listen");
exit(EXIT_FAILURE);
}
fprintf(stderr, "listening on port %d\n", server_port);


while (1) {
client_len[socket_index] = sizeof(client_address[socket_index]);
puts("waiting for client");
client_sockfd[socket_index] = accept(
server_sockfd,
(struct sockaddr*)&client_address[socket_index],
&client_len[socket_index]
);
if (client_sockfd[socket_index] < 0) {
perror("Cannot accept connection\n");
close(server_sockfd);
exit(EXIT_FAILURE);
}

pthread_create( &threads[socket_index], NULL, forClient, (void*)client_sockfd[socket_index]);

if(BUFSIZ == socket_index) {
socket_index = 0;
} else {
++socket_index;
}

pthread_join(threads[socket_index], NULL);
close(filefd);
close(client_sockfd[socket_index]);
}
return EXIT_SUCCESS;
}
void* forClient(void* ptr) {
int connect_socket = (int) ptr;
int filefd;
ssize_t read_return;
char buffer[BUFSIZ];
char *file_path;
char receiveFileName[BUFSIZ];

int ret = 1;
// Thread number means client's id
printf("Thread number %ld\n", pthread_self());
pthread_mutex_lock( &mutex1 );

// until stop receiving go on taking information
while (recv(connect_socket, receiveFileName, sizeof(receiveFileName), 0)) {

file_path = receiveFileName;

fprintf(stderr, "is the file name received? ? => %s\n", file_path);

filefd = open(file_path,
O_WRONLY | O_CREAT | O_TRUNC,
S_IRUSR | S_IWUSR);
if (filefd == -1) {
perror("open");
exit(EXIT_FAILURE);
}
do {
read_return = read(connect_socket, buffer, BUFSIZ);
if (read_return == -1) {
perror("read");
exit(EXIT_FAILURE);
}
if (write(filefd, buffer, read_return) == -1) {
perror("write");
exit(EXIT_FAILURE);
}
} while (read_return > 0);
}

pthread_mutex_unlock( &mutex1 );

fprintf(stderr, "Client dropped connection\n");
pthread_exit(&ret);
}

客户端.c

/*
Soner
Send a file over a socket.

Interface:

./executable [<sever_hostname> [<port>]]

Defaults:

- server_hostname: 127.0.0.1
- port: 12345
*/

#define _XOPEN_SOURCE 700

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <signal.h>

#include <arpa/inet.h>
#include <fcntl.h>
#include <netdb.h> /* getprotobyname */
#include <netinet/in.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <unistd.h>

// NOTE/BUG: this didn't provide enough space for a 5 digit port + EOS char
#if 0
enum { PORTSIZE = 5 };
#else
enum { PORTSIZE = 6 };
#endif

void
sig_handler(int signo)
{
if (signo == SIGINT)
printf("!! OUCH, CTRL - C received on client !!\n");
}

int
main(int argc, char **argv)
{
struct addrinfo hints,
*res;
char *server_hostname = "127.0.0.1";
char file_path[BUFSIZ];
char *server_reply = NULL;
char *user_input = NULL;
char buffer[BUFSIZ];
int filefd;
int sockfd;
ssize_t read_return;
struct hostent *hostent;
unsigned short server_port = 12345;
char portNum[PORTSIZE];
char remote_file[BUFSIZ];
int select;
char *client_server_files[BUFSIZ];
int i = 0;
int j;

// char filename_to_send[BUFSIZ];

if (argc != 3) {
fprintf(stderr, "Usage ./client <ip> <port>\n");
exit(EXIT_FAILURE);
}

server_hostname = argv[1];
server_port = strtol(argv[2], NULL, 10);

/* Prepare hint (socket address input). */
hostent = gethostbyname(server_hostname);
if (hostent == NULL) {
fprintf(stderr, "error: gethostbyname(\"%s\")\n", server_hostname);
exit(EXIT_FAILURE);
}

memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET; // ipv4
hints.ai_socktype = SOCK_STREAM; // tcp
hints.ai_flags = AI_PASSIVE; // fill in my IP for me

sprintf(portNum, "%d", server_port);
getaddrinfo(NULL, portNum, &hints, &res);

sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (sockfd == -1) {
perror("socket");
exit(EXIT_FAILURE);
}

/* Do the actual connection. */
if (connect(sockfd, res->ai_addr, res->ai_addrlen) == -1) {
perror("connect");
return EXIT_FAILURE;
}

while (1) {
if (signal(SIGINT, sig_handler)) {
break;
}

puts("connected to the server");
puts("-----------------");
puts("|1 - listLocal| \n|2 - listServer| \n|3 - sendFile| \n|4 - help| \n|5 - exit| ");
puts("-----------------");
while (1) {
scanf("%d", &select);

switch (select) {
case 1: // list files of client's directory
system("find . -maxdepth 1 -type f | sort");
break;

case 2: // listServer
puts("---- Files btw Server and the Client ----");
for (j = 0; j < i; ++j) {
puts(client_server_files[j]);
}
break;

case 3: // send file
memset(file_path, 0, sizeof file_path);
scanf("%s", file_path);

memset(remote_file, 0, sizeof remote_file);
// send file name to server
sprintf(remote_file, "%s", file_path);
send(sockfd, remote_file, sizeof(remote_file), 0);

filefd = open(file_path, O_RDONLY);
if (filefd == -1) {
perror("open send file");
//exit(EXIT_FAILURE);
break;
}

while (1) {
read_return = read(filefd, buffer, BUFSIZ);
if (read_return == 0)
break;
if (read_return == -1) {
perror("read");
//exit(EXIT_FAILURE);
break;
}
if (write(sockfd, buffer, read_return) == -1) {
perror("write");
//exit(EXIT_FAILURE);
break;
}
}

// add files in char pointer array
client_server_files[i++] = file_path;

close(filefd);
break;

case 5:
free(user_input);
free(server_reply);
exit(EXIT_SUCCESS);

default:
puts("Wrong selection!");
break;
}

}
}

free(user_input);
free(server_reply);
exit(EXIT_SUCCESS);
}

最佳答案

我修复了其他人提到的大部分错误。

让多线程/多客户端工作的关键点:

消除互斥。

将之前由 socket_index 索引的所有数组合并到一个新的“控制”结构中。主线程为结构执行 malloc,填充它,并将结构指针传递给线程。

从主线程中删除 pthread_join 并运行所有分离的线程。 main 不再为客户端线程做任何关闭/清理。

客户端线程现在执行关闭/清理/释放。

即便如此,服务器/客户端代码仍然需要一些工作,但现在,它确实可以处理多个同步客户端连接,我认为这是主要问题。

注意:我之前回答过类似的问题:executing commands via sockets with popen()特别注意“标志”字符的讨论。

无论如何,这是代码。我已经清理了它,注释了错误和修复,并用 #if 0 包装了旧/新代码。请注意,一些“旧”代码并非纯粹的原始代码,而是我的临时版本。 [请原谅不必要的样式清理]:


服务器.c:

/*
Soner
Receive a file over a socket.

Saves it to output.tmp by default.

Interface:

./executable [<port>]

Defaults:

- output_file: output.tmp
- port: 12345
*/

#define _XOPEN_SOURCE 700

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>

#include <arpa/inet.h>
#include <fcntl.h>
#include <netdb.h> /* getprotobyname */
#include <netinet/in.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <unistd.h>

#include <pthread.h>

// NOTE: this consolidates four arrays that were indexed by socket_index
struct client {
socklen_t client_len;
struct sockaddr_in client_address;
int client_sockfd;
pthread_t thread;
};

// NOTE: no longer used/needed for true multiclient
#if 0
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
#endif

// NOTE/BUG: this didn't provide enough space for a 5 digit port + EOS char
#if 0
enum { PORTSIZE = 5 };
#else
enum { PORTSIZE = 6 };
#endif

void *forClient(void *ptr);

void
sig_handler(int signo)
{
if (signo == SIGINT)
printf("!! OUCH, CTRL - C received by server !!\n");
}

int
main(int argc, char **argv)
{
struct addrinfo hints,
*res;
int enable = 1;
//int filefd; // NOTE: this is never initialized/used
int server_sockfd;
unsigned short server_port = 12345u;
char portNum[PORTSIZE];

// NOTE: now all client related data is malloc'ed
#if 0
int socket_index = 0;
#else
struct client *ctl;
#endif

if (argc != 2) {
fprintf(stderr, "Usage ./server <port>\n");
exit(EXIT_FAILURE);
}
server_port = strtol(argv[1], NULL, 10);

memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET; // ipv4
hints.ai_socktype = SOCK_STREAM; // tcp
hints.ai_flags = AI_PASSIVE; // fill in my IP for me

sprintf(portNum, "%d", server_port);
getaddrinfo(NULL, portNum, &hints, &res);

server_sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (server_sockfd == -1) {
perror("socket");
exit(EXIT_FAILURE);
}

if (setsockopt(server_sockfd, SOL_SOCKET, (SO_REUSEPORT | SO_REUSEADDR), &enable, sizeof(enable)) < 0) {
perror("setsockopt(SO_REUSEADDR) failed");
exit(EXIT_FAILURE);
}

if (bind(server_sockfd, res->ai_addr, res->ai_addrlen) == -1) {
perror("bind");
exit(EXIT_FAILURE);
}

if (listen(server_sockfd, 5) == -1) {
perror("listen");
exit(EXIT_FAILURE);
}
fprintf(stderr, "listening on port %d\n", server_port);

// NOTE: we want the threads to run detached so we don't have to wait
// for them to do cleanup -- the thread now does its own close/cleanup
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,1);

while (1) {
// NOTE/BUG: using a fixed list, if you actually let threads detach,
// you don't know which thread completes allowing its control struct
// to be reused
// the solution is to allocate a fresh one, fill it, pass it to the
// thread and let the _thread_ do all the closes and cleanup
#if 0
ctl = &control_list[socket_index];
#else
ctl = malloc(sizeof(struct client));
if (ctl == NULL) {
perror("malloc");
exit(EXIT_FAILURE);
}
#endif

ctl->client_len = sizeof(ctl->client_address);
puts("waiting for client");

ctl->client_sockfd = accept(server_sockfd,
(struct sockaddr *) &ctl->client_address, &ctl->client_len);

if (ctl->client_sockfd < 0) {
perror("Cannot accept connection\n");
close(server_sockfd);
exit(EXIT_FAILURE);
}

// NOTE: we're running the threads detached now and we're passing down
// extra information just in case the client loop needs it
#if 0
pthread_create(&ctl->thread, NULL, forClient, ctl);
#else
pthread_create(&ctl->thread, &attr, forClient, ctl);
#endif

#if 0
if (BUFSIZ == socket_index) {
socket_index = 0;
}
else {
++socket_index;
}
#endif

// NOTE/BUG: this is why you couldn't do multiple clients at the same
// time -- you are doing a thread join
// but you _had_ to because the main thread didn't know when a thread
// was done with the control struct without the join
#if 0
pthread_join(threads[socket_index], NULL);
close(filefd);
close(client_sockfd[socket_index]);
#endif
}

return EXIT_SUCCESS;
}

void *
forClient(void *ptr)
{
#if 0
int connect_socket = (int) ptr;
#else
struct client *ctl = ptr;
int connect_socket = ctl->client_sockfd;
#endif
int filefd;
ssize_t read_return;
char buffer[BUFSIZ];
char *file_path;
long long file_length;
char receiveFileName[BUFSIZ];

//int ret = 1;

// Thread number means client's id
printf("Thread number %ld\n", pthread_self());

// NOTE: to run parallel threads, this prevents that
#if 0
pthread_mutex_lock(&mutex1);
#endif

// until stop receiving go on taking information
while (recv(connect_socket, receiveFileName, sizeof(receiveFileName), 0)) {
// NOTE/FIX2: now we have the client send us the file length so we
// know when to stop the read loop below
file_length = strtoll(receiveFileName,&file_path,10);

if (*file_path != ',') {
fprintf(stderr,"syntax error in request -- '%s'\n",
receiveFileName);
exit(EXIT_FAILURE);
}
file_path += 1;

fprintf(stderr, "is the file name received? ? => %s [%lld bytes]\n",
file_path,file_length);

// NOTE: if you want to see _why_ sending the length is necessary,
// uncomment this line and the "unable to send two files" bug will
// reappear
//file_length = 1LL << 62;

filefd = open(file_path,
O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
if (filefd == -1) {
perror("open");
exit(EXIT_FAILURE);
}

// NOTE/BUG2/FIX: now we only read up to what we're told to read
// previously, we would keep trying to read, so on the _second_
// send, our read call here would get the data that _should_ have
// gone into the recv above
// in other words, we'd lose synchronization with what the client
// was sending us [and we'd put the second filename into the first
// file as data at the bottom]
for (; file_length > 0; file_length -= read_return) {
read_return = BUFSIZ;
if (read_return > file_length)
read_return = file_length;

read_return = read(connect_socket, buffer, read_return);
if (read_return == -1) {
perror("read");
exit(EXIT_FAILURE);
}
if (read_return == 0)
break;

if (write(filefd, buffer, read_return) == -1) {
perror("write");
exit(EXIT_FAILURE);
}
}

fprintf(stderr,"file complete\n");

// NOTE/BUG: filefd was never closed
#if 1
close(filefd);
#endif
}

#if 0
pthread_mutex_unlock(&mutex1);
#endif

fprintf(stderr, "Client dropped connection\n");

// NOTE: do all client related cleanup here
// previously, the main thread was doing the close, which is why it had
// to do the pthread_join
close(connect_socket);
free(ctl);

// NOTE: this needs a void * value like below
#if 0
pthread_exit(&ret);
#endif

return (void *) 0;
}

客户端.c:

/*
Soner
Send a file over a socket.

Interface:

./executable [<sever_hostname> [<port>]]

Defaults:

- server_hostname: 127.0.0.1
- port: 12345
*/

#define _XOPEN_SOURCE 700

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <signal.h>

#include <arpa/inet.h>
#include <fcntl.h>
#include <netdb.h> /* getprotobyname */
#include <netinet/in.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <unistd.h>

// NOTE/BUG: this didn't provide enough space for a 5 digit port + EOS char
#if 0
enum { PORTSIZE = 5 };
#else
enum { PORTSIZE = 6 };
#endif

// NOTE2: the "volatile" attribute here is critical to proper operation
volatile int signo_taken;

// NOTE/BUG2: don't use BUFSIZ when you really want something else
#define MAXFILES 1000

void
sig_handler(int signo)
{

// NOTE/BUG2/FIX: doing printf within a signal handler is _not_ [AFAIK] a
// safe thing to do because it can foul up the internal structure data of
// stdout if the base task was doing printf/puts and the signal occurred
// in the middle -- there are a number of other restrictions, such as
// _no_ malloc, etc.

// so, just alert the base layer and let it handle things when it's in a
// "safe" state to do so ...
signo_taken = signo;
}

int
main(int argc, char **argv)
{
struct addrinfo hints,
*res;
char *server_hostname = "127.0.0.1";
char file_path[BUFSIZ];
char *server_reply = NULL;
char *user_input = NULL;
char buffer[BUFSIZ];
int filefd;
int sockfd;
struct stat st;
ssize_t read_return;
struct hostent *hostent;
unsigned short server_port = 12345;
char portNum[PORTSIZE];
char remote_file[BUFSIZ];
int select;
char *client_server_files[MAXFILES];
int i = 0;
int j;

// char filename_to_send[BUFSIZ];

if (argc != 3) {
fprintf(stderr, "Usage ./client <ip> <port>\n");
exit(EXIT_FAILURE);
}

server_hostname = argv[1];
server_port = strtol(argv[2], NULL, 10);

/* Prepare hint (socket address input). */
hostent = gethostbyname(server_hostname);
if (hostent == NULL) {
fprintf(stderr, "error: gethostbyname(\"%s\")\n", server_hostname);
exit(EXIT_FAILURE);
}

memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET; // ipv4
hints.ai_socktype = SOCK_STREAM; // tcp
hints.ai_flags = AI_PASSIVE; // fill in my IP for me

sprintf(portNum, "%d", server_port);
getaddrinfo(NULL, portNum, &hints, &res);

sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (sockfd == -1) {
perror("socket");
exit(EXIT_FAILURE);
}

/* Do the actual connection. */
if (connect(sockfd, res->ai_addr, res->ai_addrlen) == -1) {
perror("connect");
return EXIT_FAILURE;
}

// NOTE/FIX2: this only needs to be done once, since the desired action is
// to [cleanly] stop the program
signal(SIGINT, sig_handler);

// NOTES:
// (1) instead of using signo_taken as is done, below there are alternate
// ways to handle signals with sigsetjmp and siglongjmp
// (2) but the main reason to _not_ do this is to prevent the handler
// from messing up a file transfer
while (! signo_taken) {
puts("connected to the server");
#if 0
puts("-----------------");
puts("|1 - listLocal| \n|2 - listServer| \n|3 - sendFile| \n|4 - help| \n|5 - exit| ");
puts("-----------------");
#endif

while (! signo_taken) {
// NOTE: not a bug, but it helps the user to output the menu each
// time
#if 1
puts("-----------------");
puts("|1 - listLocal| \n|2 - listServer| \n|3 - sendFile| \n|4 - help| \n|5 - exit| ");
puts("-----------------");
#endif

scanf("%d", &select);

// NOTE: we should check this after _any_ call that requests user
// input (e.g. scanf, fgets(...,stdin), etc.)
if (signo_taken)
break;

switch (select) {
case 1: // list files of client's directory
system("find . -maxdepth 1 -type f | sort");
break;

case 2: // listServer
puts("---- Files btw Server and the Client ----");
for (j = 0; j < i; ++j) {
puts(client_server_files[j]);
}
break;

case 3: // send file
fputs("Enter filename: ",stdout);
fflush(stdout);

memset(file_path, 0, sizeof file_path);
scanf("%s", file_path);

if (signo_taken)
break;

// NOTE/FIX: check the file _before_ sending request to server
// and we [now] want to know the file length so we can send
// that to the server so it will know when to stop receiving
#if 1
filefd = open(file_path, O_RDONLY);
if (filefd == -1) {
perror("open send file");
// exit(EXIT_FAILURE);
break;
}

// get the file's byte length
if (fstat(filefd,&st) < 0) {
perror("stat send file");
// exit(EXIT_FAILURE);
close(filefd);
break;
}
#endif

// send file name to server
memset(remote_file, 0, sizeof(remote_file));
#if 0
sprintf(remote_file, "%s", file_path);
#else
sprintf(remote_file, "%lld,%s",
(long long) st.st_size,file_path);
#endif
send(sockfd, remote_file, sizeof(remote_file), 0);

// NOTE/BUG2: this should be done above to _not_ confuse server
#if 0
filefd = open(file_path, O_RDONLY);
if (filefd == -1) {
perror("open send file");
// exit(EXIT_FAILURE);
break;
}
#endif

while (1) {
read_return = read(filefd, buffer, BUFSIZ);
if (read_return == 0)
break;

if (read_return == -1) {
perror("read");
// exit(EXIT_FAILURE);
break;
}

if (write(sockfd, buffer, read_return) == -1) {
perror("write");
// exit(EXIT_FAILURE);
break;
}
}

close(filefd);

// add files in char pointer array
// NOTE/BUG2: file_path gets overwritten, so we must save it
// here
#if 0
client_server_files[i++] = file_path;
#else
if (i < MAXFILES)
client_server_files[i++] = strdup(file_path);
#endif

puts("file complete");
break;

case 5:
free(user_input);
free(server_reply);
exit(EXIT_SUCCESS);
break;

default:
puts("Wrong selection!");
break;
}

}
}

// NOTE/FIX2: we output this here when it's save to do so
if (signo_taken)
printf("!! OUCH, CTRL - C received on client !!\n");

free(user_input);
free(server_reply);
exit(EXIT_SUCCESS);
}

更新:

I have solved my connection-interruption problem but signal is still occurring. I left two problems more times file sending and signal handling

我重新设计了客户端信号处理,使其按预期工作[即打印消息并停止客户端]。

我还解决了只能发送一个文件的问题。要理解这一点,请考虑客户端和服务器的操作。

要发送文件,客户端会提示输入文件名,然后使用其中的文件名执行 send 调用。然后它打开文件并执行读/写循环以将文件数据发送到服务器 [然后关闭文件描述符]。

要接收文件,服务器会执行 recv 调用以获取文件名。然后它打开文件 [用于输出] 并进行读/写以将数据从套接字写入文件 [然后关闭文件描述符]。

问题在于:服务器读/写循环的终止条件是等待 read(connect_socket,...) 调用返回 0。但是,它不会 返回零 [除非套接字已关闭]。

因此,现在客户端调用send 来发送第二个 文件名。但是,此数据不会进入服务器的 recv 调用,而只会成为 read 缓冲区的一部分。也就是说,第二个文件名 将作为数据附加到第一个文件

解决方案是让客户端告诉服务器文件大小是多少。因此,客户端不再执行 filenamesend,而是执行 filesize,filenamesend/p>

服务器现在将解码此文件大小并在 recv 缓冲区中拆分文件名。现在,服务器的读/写循环将保持对仍有多少字节需要读取的计数,并在剩余计数为零时循环停止。

还有一两个其他小错误。我已经用错误修复和注释更新了 client.c 和 server.c

关于c - 使用套接字进行多线程文件传输,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37497556/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com