我正在尝试在C中创建一个多线程服务器 - 客户端文件传输系统。有些客户端将发送或列出或做一些其他选择(在您可以看到的交换机情况下)和存储文件的服务器和为很多客户服务。
就我所见,多线程意识形态真的很难。它需要太多的经验而不是知识。我已经在这个项目上工作了一个多星期,而且我还没有能够解决这些问题。
有4种选择:第一种是在其目录中列出客户端的本地文件,第二种是在客户端和服务器之间传输的列表文件,第三种是从用户读取文件名并将文件复制到服务器的目录中
我这里的重要问题是关于多线程。我无法连接多个客户端。我已经阅读了从a到z堆的代码,但我真的无法理解我的错误而且卡住了。
另一个问题是客户端会在SIGINT
被捕获时结束,但是,例如,在按ctrl-c选择列表文件后它就不会停止。服务器文件也存在同样的问题。与客户端捕获相比,它更麻烦,因为当服务器获得SIGINT
时,客户端将分别与服务器断开连接。
感谢您的帮助!
server.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);
}
client.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);
}
答案 0 :(得分:1)
我修复了其他人提到的大部分错误。
获得多线程/多客户端的关键点:
消除互斥锁。
将先前由socket_index
编入索引的所有数组合并到一个新的&#34;控件&#34;结构。主线程为struct执行malloc,填充它,并将结构指针传递给线程。
从主线程中删除pthread_join
并运行所有分离的线程。 main不再为客户端线程进行任何关闭/清理。
客户端线程现在执行关闭/清理/免费。
即便如此,服务器/客户端代码仍然需要一些工作,但现在, 使用多个同时发生的客户端连接,我认为这是主要问题。
注意:之前我已经回答了类似的问题:executing commands via sockets with popen()要特别注意对#34;标志的讨论&#34;字符。
无论如何,这是代码。我已对其进行了清理,对错误和修复进行了注释,并使用#if 0
包装了旧/新代码。注意一些&#34; old&#34;代码不是纯粹的原始代码,而是我的临时版本。 [请原谅无偿的风格清理]:
server.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;
}
client.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);
}
<强>更新强>
我已解决了连接中断问题,但信号仍在发生。我在文件发送和信号处理方面留下了两个问题
我已经重新设计了客户端信号处理,以便它按预期工作[即打印消息并停止客户端]。
我还解决了只能发送一个文件的问题。要理解这一点,请考虑客户端和服务器的操作。
要发送文件,客户端提示输入文件名,请使用文件名进行send
调用。然后它打开文件并执行读/写循环以将文件数据发送到服务器[然后关闭文件描述符]。
要接收文件,服务器会进行recv
调用以获取文件名。然后它打开文件[用于输出]并执行读/写操作以将数据从套接字写入文件[然后关闭文件描述符]。
问题在于:服务器读/写循环的终止条件是等到read(connect_socket,...)
调用返回0.但是,它将不返回零[除非套接字已关闭]。
因此,现在客户端进行send
调用以发送第二个文件名。但是,这个数据不是进入服务器的recv
调用,而只是read
缓冲区的一部分。也就是说,第二个文件名将作为数据附加到第一个文件。
解决方案是让客户端告诉服务器文件大小是多少。因此,代替客户执行send
filename
,它现在执行send
filesize,filename
服务器现在将解码此文件大小并在recv
缓冲区中拆分文件名。现在,服务器的读/写循环将保持仍然需要读取多少字节的计数,并且当剩余计数达到零时循环停止。
还有一两个小错误。我已经使用错误修复和注释
更新了client.c和server.c