我的 c 套接字程序在成功后在命令上吐出垃圾,不知道为什么
My c socket program spitting out garbage on the command following a successful one, no clue why
我什么都试过了。不知道我的程序发生了什么。我写了一个 tcp 服务器和一个客户端。我可以从客户端向服务器发出一些命令,它们是:ls-local、ls-remote、get filename、put filename(我还没有实现put,因为我卡在了get上)。
一切正常,直到我发出 get 命令。 server.c 和 client.c 文件位于不同的文件夹中。当您发出 'get filename' 时,其中 filename 是您要获取的文件的名称,服务器会检查该文件是否存在于其当前目录中,如果存在,则将其发送给客户端。
在第一次迭代中,这非常有效。该文件被发送到客户端,客户端创建该文件的副本。但是,除了 'ls-local' 之外的任何命令都会导致将纯垃圾打印到客户端,我不知道为什么。我认为这可能与我需要清除缓冲区有关,但我试过了但它仍然没有用,所以我显然做错了。
这是我在获取文件后发出命令时发生的情况:
当我再次发出 get filename 时,我得到一行垃圾,即使文件存在于服务器中,也没有发送任何内容。
如果我发出命令 ls-remote,这就是疯狂地不停地向我的客户端打印乱码的地方。
我已经包含了这两个文件,以防您想测试它们以了解我的意思。用法是:'get filename',没有单引号。请记住,它在第一次后中断,第一次工作正常。对不起,文件太长了,发送文件的代码在名为 sendfile 的方法中,我试着组织它以便于阅读。
在此方面需要一些重要帮助,在此先感谢!!
server.c
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <dirent.h>
/**
* Structure used to contain the type of command: 'get' or 'put'
* as well as the name of the file
*/
struct command{
char* type;
char* filename;
};
void syserr(char *msg) { perror(msg); exit(-1); }
/* function prototypes */
void handleConnection(int);
void sendDirectories(int);
struct command getCommand(char*);
int fileExists(char*);
void sendFile(char*, int);
int main(int argc, char *argv[]){
int sockfd, newsockfd, portno, processId;
struct sockaddr_in serv_addr, clt_addr;
socklen_t addrlen;
switch(argc) {
case 1:
portno = 5555;
break;
case 2 :
portno = atoi(argv[1]);
break;
default :
fprintf(stderr,"Usage: %s <port>\n", argv[0]);
return 1;
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0) syserr("can't open socket");
printf("create socket...\n");
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if(bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
syserr("can't bind");
}
printf("bind socket to port %d...\n", portno);
listen(sockfd, 5);
for(;;) {
printf("wait on port %d...\n", portno);
addrlen = sizeof(clt_addr);
newsockfd = accept(sockfd, (struct sockaddr*)&clt_addr, &addrlen);
if(newsockfd < 0) syserr("can't accept");
printf("connected to client....\n");
signal(SIGCHLD,SIG_IGN);//prevent zombie process
processId = fork();
if(processId < 0){
// printf("process id < 0, nothing closed\n");
syserr("ERROR while attempting to fork");
}
if(processId == 0){//child process
// printf("process id == 0, close(sockfd)\n");
close(sockfd);
handleConnection(newsockfd);
exit(0);
}
else{//parent process
// printf("else statement, close(newsockfd)\n");
close(newsockfd);
}
}
// printf("outside of loop, close(sockfd)\n");
// close(sockfd);
return 0;
}
/**
* Method that handles the commands received and routes them to the proper methods
* @param newsockfd the file descriptor created when the connection was accepted
*/
void handleConnection(int newsockfd){
int n;
char buffer[256];
while(1){
bzero(buffer,256);
printf("waiting for client's command.....\n");
n = recv(newsockfd, buffer, 255, 0);
if(n < 0) syserr("can't receive from client");
else buffer[n] = '[=10=]';
printf("SERVER GOT MESSAGE: %s\n", buffer);
/**
* exit command
*/
if (strcmp(buffer, "exit") == 0){
printf("Ending session with client....\n");
break;
}
/*
* ls-remote command
*/
if (strcmp(buffer, "ls-remote") == 0){
sendDirectories(newsockfd);
}else{
struct command userCommand = getCommand(buffer);
//USER SENDS VALID COMMAND
if(strcmp(userCommand.type, "invalid") != 0){
printf("Command %s %s is valid\n", userCommand.type, userCommand.filename);
/**
* 'get' command
*/
if(strcmp(userCommand.type, "get") == 0){
//File exists
if(fileExists(userCommand.filename) == 1){
sendFile(userCommand.filename, newsockfd);
}
//File does not exist
else{
n = send(newsockfd, "ERROR: FILE DOES NOT EXIST IN REMOTE DIRECTORY", 255, 0);
if(n < 0) syserr("Unable to send FILE DOES NOT EXIST ERROR to client");
printf("Sending FILE DOES NOT EXIST ERROR..\n");
}
}
/**
* 'put' command
* no check needed for 'put' because only possible options at this point are 'get' and 'put'
* since invalid commands are filtered out in getCommand method
*/
else{
}
}
//USER SENDS INVALID COMMAND
else{
n = send(newsockfd, "INVALID COMMAND: you may only send 'ls-remote' OR ' get ' or ' put ' followed by a filename", 255, 0);
if(n < 0) syserr("Unable to send INVALID COMMAND ERROR' to client");
printf("Sending INVALID COMMAND ERROR...\n");
}
}
}
// printf("in handleConnection, close(sockfd)\n");
// close(newsockfd);
}
/**
* Method that executes when the command 'ls-remote' is received, sends entire file listing in working directory
* @param newsockfd the file descriptor created when the connection was accepted, will be used to send file listing
*/
void sendDirectories(int newsockfd){
DIR* directory;
struct dirent *dir;
directory = opendir(".");
int n;
if (directory){
int count = 0;
while ((dir = readdir(directory)) != NULL){
//ignore current dir and parent dir names
if(strcmp(dir->d_name, ".") != 0 && strcmp(dir->d_name, "..") != 0){
// // printf("%s\n", dir->d_name);
// strcat(dir->d_name, "\n");//concatenates new line char to end of dir name, consider sending without new line and handle new line in client
// n = send(newsockfd, dir->d_name,255, 0);
// if(n < 0) syserr("can't send file list to client");
// printf("sending file name: '%s' to client...\n", dir->d_name);
count++;
}
}
int32_t convCount = htonl(count);
n = send(newsockfd, &convCount, sizeof(convCount), 0);
if(n < 0) syserr("can't send file list count");
printf("sending file list count: '%d' to client...\n", count);
rewinddir(directory);
while ((dir = readdir(directory)) != NULL){
//ignore current dir and parent dir names
if(strcmp(dir->d_name, ".") != 0 && strcmp(dir->d_name, "..") != 0){
// printf("%s\n", dir->d_name);
// strcat(dir->d_name, "\n");//concatenates new line char to end of dir name, consider sending without new line and handle new line in client
n = send(newsockfd, dir->d_name,255, 0);
if(n < 0) syserr("can't send file list to client");
printf("sending file name: '%s' to client...\n", dir->d_name);
}
}
closedir(directory);
}
}
/**
* Method that extracts a valid command from a given string
* @param string the string containing the commands
* @return the valid command if found, a command with type 'invalid' if not found
*/
struct command getCommand(char* string){
char* temp;
int count;
struct command userCommand;
count = 0;
//Split the string on spaces, if more than one space then # of arguments is > 2, thus invalid command
temp = strtok(string, " ");
while(temp != NULL){
if(count == 0){
userCommand.type = temp;
}else if(count == 1){
userCommand.filename = temp;
}else{
userCommand.type = "invalid";
break;
}
temp = strtok(NULL, " ");
count++;
}
//We test count ==1 because this means only one space in string but also only one word because count did not increment again
//which is still an invalid command
if(strcmp(userCommand.type, "get") != 0 && strcmp(userCommand.type, "put") != 0 || count == 1){
userCommand.type = "invalid";
}
return userCommand;
}
/**
* Method that searches server directory for a given file name
* @param filename the name of the file to search for
* @return 1 if the file is found, 0 if not found
*/
int fileExists(char* filename){
DIR* directory;
struct dirent *dir;
directory = opendir(".");
int n;
if (directory){
while ((dir = readdir(directory)) != NULL){
//ignore current dir and parent dir names
if(strcmp(dir->d_name, ".") != 0 && strcmp(dir->d_name, "..") != 0){
if(strcmp(dir->d_name, filename) == 0) {
printf("File: '%s' was FOUND!\n", filename);
return 1;
}
}
}
closedir(directory);
}
printf("Sorry file: '%s' was NOT found!\n", filename);
return 0;
}
/**
* Method that sends the specified file using the file descriptor passed
* @param filename the name of the file to be sent
* @param newsockfd the file descriptor to use to send the file
*/
void sendFile(char* filename, int newsockfd){
int n, fd;
struct stat file_stat;
char buffer[256];
char* file_buffer;
char file_chunk[BUFSIZ];
fd = open(filename, O_RDONLY);
if(fd < 0){
n = send(newsockfd, "ERROR: File could not be opened by server....",255, 0);
if(n < 0) syserr("Error opening file client has requested");
}
if (fstat(fd, &file_stat) < 0){
n = send(newsockfd, "ERROR:File stats could not be obtained by server....",255, 0);
if(n < 0) syserr("Error getting stats of file client has requested");
}
n = send(newsockfd, "Server found file and successfully opened...",255, 0);
if(n < 0) syserr("can't send file open confirmation to client");
printf("File found and successfully opened....\n");
// int data_remaining = file_stat.st_size;
// int32_t convSize = htonl(data_remaining);
// n = send(newsockfd, &convSize, sizeof(convSize), 0);
// if(n < 0) syserr("can't send file size ");
// printf("N IS: %d\n", n);
// printf("Sending file size to client, size is : %d bytes....\n", file_stat.st_size);
int data_remaining = file_stat.st_size;
int sent_bytes = 0;
char str[255];//store file size in string to send, for some reason the above code was sending fine but client could not receive, even though it works perfectly for ls-remote
sprintf(str, "%d", data_remaining);
file_buffer = (char*) malloc (sizeof(char)*data_remaining);
n = send(newsockfd,str,255, 0);
if(n < 0) syserr("can't send file size to client...");
printf("Sending file size to client, size is : %d bytes....\n", file_stat.st_size);
int read_bytes;
read_bytes = read(fd, file_buffer, data_remaining);
while((sent_bytes = send(newsockfd, file_buffer, file_stat.st_size, 0)) > 0 && data_remaining > 0){
data_remaining -= sent_bytes;
printf("Sent %d bytes of file, %d bytes remain\n", sent_bytes, data_remaining);
}
printf("File %s has finished sending....\n", filename);
close(fd);
}
client.c
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <dirent.h>
/**
* Structure used to contain the type of command: 'get' or 'put'
* as well as the name of the file
*/
struct command{
char* type;
char* filename;
};
void syserr(char* msg) { perror(msg); exit(-1); }
/*function prototypes*/
void listDirectories();
struct command getCommand(char*);
int main(int argc, char *argv[]){
int sockfd, portno, n;
int32_t *convCount, *convSize;
struct hostent* server;
struct sockaddr_in serv_addr;
FILE *received_file;
char buffer[256];
if(argc != 3){
fprintf(stderr, "Usage: %s <hostname> <port>\n", argv[0]);
return 1;
}
server = gethostbyname(argv[1]);
if(!server){
fprintf(stderr, "ERROR: no such host: %s\n", argv[1]);
return 2;
}
portno = atoi(argv[2]);
sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sockfd < 0) syserr("Error opening socket.");
printf("create socket...\n");
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr = *((struct in_addr*)server->h_addr);
serv_addr.sin_port = htons(portno);
if(connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0){
syserr("can't connect to server");
}
printf("connection to %s:%s established. Now awaiting commands...\n", argv[1], argv[2]);
while(1){
// printf("PLEASE ENTER MESSAGE: ");
printf("%s:%s> ", argv[1], argv[2]);
fgets(buffer, 255, stdin);
n = strlen(buffer);
if(n>0 && buffer[n-1] == '\n'){
buffer[n-1] = '[=11=]';
}
if(strcmp(buffer, "exit") == 0){
n = send(sockfd, buffer, strlen(buffer), 0);
if(n < 0) syserr("can't send 'exit' command to server");
printf("send...\n");
break;
}else if(strcmp(buffer, "ls-local") == 0){
listDirectories();
}else if(strcmp(buffer, "ls-remote") == 0){
n = send(sockfd, buffer, strlen(buffer), 0);
if(n < 0) syserr("can't send to server");
printf("send...\n");
n = recv(sockfd, convCount, sizeof(int32_t), 0);
if(n < 0) syserr("can't receive from server");
int fileCount = ntohl(*convCount);
// printf("CLIENT RECEIVED FILE COUNT: %d\n", fileCount);
printf("Files at server (%s:%s):\n", argv[1], argv[2]);
int i = 0;
while(i < fileCount){
n = recv(sockfd, buffer, 255, 0);
if(n < 0) syserr("can't receive from server");
printf("%s\n", buffer);
i++;
}
}else{
char cmdCopy[256];
strcpy(cmdCopy, buffer);
struct command userCommand = getCommand(cmdCopy);
if(strcmp(userCommand.type, "invalid") != 0){
printf("Command %s %s is valid\n", userCommand.type, userCommand.filename);
/**
* 'get' command
*/
if(strcmp(userCommand.type, "get") == 0){
//Re-append the entire command since
n = send(sockfd, buffer, strlen(buffer), 0);
if(n < 0) syserr("can't send to server");
printf("send GET request for file: %s\n", userCommand.filename);
n = recv(sockfd, buffer, 255, 0);
if(n < 0) syserr("can't receive from server");
else buffer[n] = '[=11=]';
if(strcmp(buffer, "ERROR: FILE DOES NOT EXIST IN REMOTE DIRECTORY") != 0
&& strcmp(buffer, "ERROR: File could not be opened by server....") != 0
&& strcmp(buffer, "ERROR:File stats could not be obtained by server....") != 0){
printf("%s\n", buffer);
//// Not working for some reason, must receive as char*
// n = recv(sockfd, convSize, sizeof(int32_t), 0);
// printf("N IS: %d\n", n);
// if(n < 0) syserr("can't receive file size from server");
// int fileSize = ntohl(*convSize);
// printf("Size of file: %s to be received is %d\n bytes",buffer, fileSize);
// printf("Receiving file: %s \n....", userCommand.filename);
n = recv(sockfd, buffer, 255, 0);
if(n < 0) syserr("can't receive size from server");
else buffer[n] = '[=11=]';
int data_remaining = atoi(buffer);
printf("file size is %d\n", data_remaining);
received_file = fopen(userCommand.filename, "w");
if (received_file == NULL){
syserr("Failed to open file.");
}else{
char file_buffer[BUFSIZ];
int bytes_received;
while((bytes_received = recv(sockfd, file_buffer, BUFSIZ, 0)) >0 && data_remaining > 0){
fwrite(file_buffer, 1, bytes_received, received_file);
data_remaining -= bytes_received;
printf("Received %d bytes of file, %d bytes remain\n", bytes_received, data_remaining);
}
printf("File receive complete\n");
fclose(received_file);
}
}else{
printf("%s\n", buffer);
}
}
/**
* 'put' command
* no check needed for 'put' because only possible options at this point are 'get' and 'put'
* since invalid commands are filtered out in getCommand method
*/
else{
// //File exists
// if(fileExists(userCommand.filename) == 1){
// sendFile(userCommand.filename, newsockfd);
// }
// //File does not exist
// else{
// printf( "ERROR: FILE DOES NOT EXIST IN LOCAL DIRECTORY\n");
// }
}
}
//USER TYPES INVALID COMMAND
else{
printf("INVALID COMMAND: you may only send 'ls-remote' OR ' get ' or ' put ' followed by a filename");
}
}
// n = send(sockfd, buffer, strlen(buffer), 0);
// if(n < 0) syserr("can't send to server");
// printf("send...\n");
// while( (n = recv(sockfd , buffer , 255 , 0)) > 0 ){
// printf("in while and n is: %d\n", n);
// printf("%s", buffer);
// }
// if(n < 0){
// printf("in if and n is: %d\n", n);
// syserr("can't receive from server");
// }else{
// printf("in else and n is: %d\n", n);
// buffer[n] = '[=11=]';
// }
// printf("in no loop and n is: %d\n", n);
}
close(sockfd);
return 0;
}
// printf("connection to %s:%s established. Now awaiting commands...\n", argv[1], argv[2]);
// do{
// printf("%s:%s> ", argv[1], argv[2]);
// fgets(buffer, 255, stdin);
// n = strlen(buffer);
// if(n>0 && buffer[n-1] == '\n'){
// buffer[n-1] = '[=11=]';
// }
// n = send(sockfd, buffer, strlen(buffer), 0);
// if(n < 0) syserr("can't send to server");
// printf("send...\n");
// n = recv(sockfd, buffer, 255, 0);
// if(n < 0) syserr("can't receive from server");
// else buffer[n] = '[=11=]';
// printf("CLIENT RECEIVED MESSAGE: %s\n", buffer);
// }while(strcmp(buffer, "exit") != 0);
// close(sockfd);
// return 0;
/**
* Method that extracts a valid command from a given string
* @param string the string containing the commands
* @return the valid command if found, a command with type 'invalid' if not found
*/
struct command getCommand(char* string){
char* temp;
int count;
struct command userCommand;
count = 0;
//Split the string on spaces, if more than one space then # of arguments is > 2, thus invalid command
temp = strtok(string, " ");
while(temp != NULL){
if(count == 0){
userCommand.type = temp;
}else if(count == 1){
userCommand.filename = temp;
}else{
userCommand.type = "invalid";
break;
}
temp = strtok(NULL, " ");
count++;
}
//We test count ==1 because this means only one space in string but also only one word because count did not increment again
//which is still an invalid command
if(strcmp(userCommand.type, "get") != 0 && strcmp(userCommand.type, "put") != 0 || count == 1){
userCommand.type = "invalid";
}
return userCommand;
}
/**
* Method that executes when the command 'ls-local' is received, sends entire file listing in working directory
*/
void listDirectories(){
DIR* directory;
struct dirent *dir;
directory = opendir(".");
if (directory){
printf("Files at the client:\n");
while ((dir = readdir(directory)) != NULL){
//ignore current dir and parent dir names
if(strcmp(dir->d_name, ".") != 0 && strcmp(dir->d_name, "..") != 0){
printf("%s\n", dir->d_name);
}
}
closedir(directory);
}
}
printf("SERVER GOT MESSAGE: %s\n", buffer);
没有,您没有收到消息。你有一些字节。 TCP 不是消息协议。如果要收发消息,就得写代码收发消息,你没做过
"TCP is a reliable, byte-stream protocol that does not preserve application message boundaries."记住这个。在理解其中的所有内容之前,不要编写任何使用 TCP 的代码,否则您的代码注定会失败。
另一个要点:在您对在 TCP 之上使用的协议有规范之前,不要编写任何使用 TCP 的代码。谁什么时候发送?有超时吗?哪一方实施它们?连接是如何关闭的?谁发起的?是否存在应用程序消息边界?它们是如何标记的?
等等。花时间详细记录这一点真的很值得。您可以查看使用 TCP(例如 HTTP、SMTP、IRC 等)的现有协议的规范,以了解协议规范应该是什么样子的示例。
除了 David Schwartz 指出的代码中的基本谬误之外,您还犯了所有常见错误。
bzero(buffer,256);
冗余。移除。
n = recv(newsockfd, buffer, 255, 0);
if(n < 0) syserr("can't receive from client");
else
此处缺少 n == 0
和 break
的测试。
buffer[n] = '[=12=]';
仅当数据完全是文本时才有效。在整个过程中,包括以下 printf()
,您应该做的是使用 n
作为您从缓冲区中检索内容的唯一上限。
如前所述,您没有理由相信这是一条完整的消息,或者只有一条消息。
解决您眼前的问题,有两个问题:
server.c第一名:
while((sent_bytes = send(newsockfd, file_buffer, file_stat.st_size, 0)) > 0 && data_remaining > 0){
每次发送时,您将发送 file_stat.st_size
个字节,而不是 data_remaining
个字节。
此外,您应该先检查 data_remaining > 0
。由于 &&
运算符是短路运算符,因此除非第一部分的计算结果为真,否则它不会计算第二部分。因此,如果您还剩下 0 个字节要发送,它将尝试发送它们。
所以上面一行应该是:
while((data_remaining > 0) && ((sent_bytes = send(newsockfd, file_buffer, file_stat.st_size, 0)) > 0)) {
与 client.c 类似,您有:
while((bytes_received = recv(sockfd, file_buffer, BUFSIZ, 0)) >0 && data_remaining > 0){
如果您收到了整个文件,它会再尝试一次 recv
,然后才意识到 data_remaining > 0
是错误的。由于服务器已完成发送,它会一直等待。
和以前一样,调换 &&
个操作数的顺序:
while((data_remaining > 0) && ((bytes_received = recv(sockfd, file_buffer, BUFSIZ, 0)) > 0)){
这至少应该让你 运行。
除此之外还有其他问题。正如 David Schwartz 所提到的,TCP 的流媒体特性不会保留消息边界。
这意味着仅仅因为您尝试读取 255 个字节并不意味着您实际上会得到那么多。在您期望特定大小的消息的任何地方,请确保您实际获得了那么多字节。如果您还没有读够,请继续阅读直到读完。
我什么都试过了。不知道我的程序发生了什么。我写了一个 tcp 服务器和一个客户端。我可以从客户端向服务器发出一些命令,它们是:ls-local、ls-remote、get filename、put filename(我还没有实现put,因为我卡在了get上)。
一切正常,直到我发出 get 命令。 server.c 和 client.c 文件位于不同的文件夹中。当您发出 'get filename' 时,其中 filename 是您要获取的文件的名称,服务器会检查该文件是否存在于其当前目录中,如果存在,则将其发送给客户端。
在第一次迭代中,这非常有效。该文件被发送到客户端,客户端创建该文件的副本。但是,除了 'ls-local' 之外的任何命令都会导致将纯垃圾打印到客户端,我不知道为什么。我认为这可能与我需要清除缓冲区有关,但我试过了但它仍然没有用,所以我显然做错了。 这是我在获取文件后发出命令时发生的情况: 当我再次发出 get filename 时,我得到一行垃圾,即使文件存在于服务器中,也没有发送任何内容。
如果我发出命令 ls-remote,这就是疯狂地不停地向我的客户端打印乱码的地方。
我已经包含了这两个文件,以防您想测试它们以了解我的意思。用法是:'get filename',没有单引号。请记住,它在第一次后中断,第一次工作正常。对不起,文件太长了,发送文件的代码在名为 sendfile 的方法中,我试着组织它以便于阅读。
在此方面需要一些重要帮助,在此先感谢!!
server.c
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <dirent.h>
/**
* Structure used to contain the type of command: 'get' or 'put'
* as well as the name of the file
*/
struct command{
char* type;
char* filename;
};
void syserr(char *msg) { perror(msg); exit(-1); }
/* function prototypes */
void handleConnection(int);
void sendDirectories(int);
struct command getCommand(char*);
int fileExists(char*);
void sendFile(char*, int);
int main(int argc, char *argv[]){
int sockfd, newsockfd, portno, processId;
struct sockaddr_in serv_addr, clt_addr;
socklen_t addrlen;
switch(argc) {
case 1:
portno = 5555;
break;
case 2 :
portno = atoi(argv[1]);
break;
default :
fprintf(stderr,"Usage: %s <port>\n", argv[0]);
return 1;
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0) syserr("can't open socket");
printf("create socket...\n");
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if(bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
syserr("can't bind");
}
printf("bind socket to port %d...\n", portno);
listen(sockfd, 5);
for(;;) {
printf("wait on port %d...\n", portno);
addrlen = sizeof(clt_addr);
newsockfd = accept(sockfd, (struct sockaddr*)&clt_addr, &addrlen);
if(newsockfd < 0) syserr("can't accept");
printf("connected to client....\n");
signal(SIGCHLD,SIG_IGN);//prevent zombie process
processId = fork();
if(processId < 0){
// printf("process id < 0, nothing closed\n");
syserr("ERROR while attempting to fork");
}
if(processId == 0){//child process
// printf("process id == 0, close(sockfd)\n");
close(sockfd);
handleConnection(newsockfd);
exit(0);
}
else{//parent process
// printf("else statement, close(newsockfd)\n");
close(newsockfd);
}
}
// printf("outside of loop, close(sockfd)\n");
// close(sockfd);
return 0;
}
/**
* Method that handles the commands received and routes them to the proper methods
* @param newsockfd the file descriptor created when the connection was accepted
*/
void handleConnection(int newsockfd){
int n;
char buffer[256];
while(1){
bzero(buffer,256);
printf("waiting for client's command.....\n");
n = recv(newsockfd, buffer, 255, 0);
if(n < 0) syserr("can't receive from client");
else buffer[n] = '[=10=]';
printf("SERVER GOT MESSAGE: %s\n", buffer);
/**
* exit command
*/
if (strcmp(buffer, "exit") == 0){
printf("Ending session with client....\n");
break;
}
/*
* ls-remote command
*/
if (strcmp(buffer, "ls-remote") == 0){
sendDirectories(newsockfd);
}else{
struct command userCommand = getCommand(buffer);
//USER SENDS VALID COMMAND
if(strcmp(userCommand.type, "invalid") != 0){
printf("Command %s %s is valid\n", userCommand.type, userCommand.filename);
/**
* 'get' command
*/
if(strcmp(userCommand.type, "get") == 0){
//File exists
if(fileExists(userCommand.filename) == 1){
sendFile(userCommand.filename, newsockfd);
}
//File does not exist
else{
n = send(newsockfd, "ERROR: FILE DOES NOT EXIST IN REMOTE DIRECTORY", 255, 0);
if(n < 0) syserr("Unable to send FILE DOES NOT EXIST ERROR to client");
printf("Sending FILE DOES NOT EXIST ERROR..\n");
}
}
/**
* 'put' command
* no check needed for 'put' because only possible options at this point are 'get' and 'put'
* since invalid commands are filtered out in getCommand method
*/
else{
}
}
//USER SENDS INVALID COMMAND
else{
n = send(newsockfd, "INVALID COMMAND: you may only send 'ls-remote' OR ' get ' or ' put ' followed by a filename", 255, 0);
if(n < 0) syserr("Unable to send INVALID COMMAND ERROR' to client");
printf("Sending INVALID COMMAND ERROR...\n");
}
}
}
// printf("in handleConnection, close(sockfd)\n");
// close(newsockfd);
}
/**
* Method that executes when the command 'ls-remote' is received, sends entire file listing in working directory
* @param newsockfd the file descriptor created when the connection was accepted, will be used to send file listing
*/
void sendDirectories(int newsockfd){
DIR* directory;
struct dirent *dir;
directory = opendir(".");
int n;
if (directory){
int count = 0;
while ((dir = readdir(directory)) != NULL){
//ignore current dir and parent dir names
if(strcmp(dir->d_name, ".") != 0 && strcmp(dir->d_name, "..") != 0){
// // printf("%s\n", dir->d_name);
// strcat(dir->d_name, "\n");//concatenates new line char to end of dir name, consider sending without new line and handle new line in client
// n = send(newsockfd, dir->d_name,255, 0);
// if(n < 0) syserr("can't send file list to client");
// printf("sending file name: '%s' to client...\n", dir->d_name);
count++;
}
}
int32_t convCount = htonl(count);
n = send(newsockfd, &convCount, sizeof(convCount), 0);
if(n < 0) syserr("can't send file list count");
printf("sending file list count: '%d' to client...\n", count);
rewinddir(directory);
while ((dir = readdir(directory)) != NULL){
//ignore current dir and parent dir names
if(strcmp(dir->d_name, ".") != 0 && strcmp(dir->d_name, "..") != 0){
// printf("%s\n", dir->d_name);
// strcat(dir->d_name, "\n");//concatenates new line char to end of dir name, consider sending without new line and handle new line in client
n = send(newsockfd, dir->d_name,255, 0);
if(n < 0) syserr("can't send file list to client");
printf("sending file name: '%s' to client...\n", dir->d_name);
}
}
closedir(directory);
}
}
/**
* Method that extracts a valid command from a given string
* @param string the string containing the commands
* @return the valid command if found, a command with type 'invalid' if not found
*/
struct command getCommand(char* string){
char* temp;
int count;
struct command userCommand;
count = 0;
//Split the string on spaces, if more than one space then # of arguments is > 2, thus invalid command
temp = strtok(string, " ");
while(temp != NULL){
if(count == 0){
userCommand.type = temp;
}else if(count == 1){
userCommand.filename = temp;
}else{
userCommand.type = "invalid";
break;
}
temp = strtok(NULL, " ");
count++;
}
//We test count ==1 because this means only one space in string but also only one word because count did not increment again
//which is still an invalid command
if(strcmp(userCommand.type, "get") != 0 && strcmp(userCommand.type, "put") != 0 || count == 1){
userCommand.type = "invalid";
}
return userCommand;
}
/**
* Method that searches server directory for a given file name
* @param filename the name of the file to search for
* @return 1 if the file is found, 0 if not found
*/
int fileExists(char* filename){
DIR* directory;
struct dirent *dir;
directory = opendir(".");
int n;
if (directory){
while ((dir = readdir(directory)) != NULL){
//ignore current dir and parent dir names
if(strcmp(dir->d_name, ".") != 0 && strcmp(dir->d_name, "..") != 0){
if(strcmp(dir->d_name, filename) == 0) {
printf("File: '%s' was FOUND!\n", filename);
return 1;
}
}
}
closedir(directory);
}
printf("Sorry file: '%s' was NOT found!\n", filename);
return 0;
}
/**
* Method that sends the specified file using the file descriptor passed
* @param filename the name of the file to be sent
* @param newsockfd the file descriptor to use to send the file
*/
void sendFile(char* filename, int newsockfd){
int n, fd;
struct stat file_stat;
char buffer[256];
char* file_buffer;
char file_chunk[BUFSIZ];
fd = open(filename, O_RDONLY);
if(fd < 0){
n = send(newsockfd, "ERROR: File could not be opened by server....",255, 0);
if(n < 0) syserr("Error opening file client has requested");
}
if (fstat(fd, &file_stat) < 0){
n = send(newsockfd, "ERROR:File stats could not be obtained by server....",255, 0);
if(n < 0) syserr("Error getting stats of file client has requested");
}
n = send(newsockfd, "Server found file and successfully opened...",255, 0);
if(n < 0) syserr("can't send file open confirmation to client");
printf("File found and successfully opened....\n");
// int data_remaining = file_stat.st_size;
// int32_t convSize = htonl(data_remaining);
// n = send(newsockfd, &convSize, sizeof(convSize), 0);
// if(n < 0) syserr("can't send file size ");
// printf("N IS: %d\n", n);
// printf("Sending file size to client, size is : %d bytes....\n", file_stat.st_size);
int data_remaining = file_stat.st_size;
int sent_bytes = 0;
char str[255];//store file size in string to send, for some reason the above code was sending fine but client could not receive, even though it works perfectly for ls-remote
sprintf(str, "%d", data_remaining);
file_buffer = (char*) malloc (sizeof(char)*data_remaining);
n = send(newsockfd,str,255, 0);
if(n < 0) syserr("can't send file size to client...");
printf("Sending file size to client, size is : %d bytes....\n", file_stat.st_size);
int read_bytes;
read_bytes = read(fd, file_buffer, data_remaining);
while((sent_bytes = send(newsockfd, file_buffer, file_stat.st_size, 0)) > 0 && data_remaining > 0){
data_remaining -= sent_bytes;
printf("Sent %d bytes of file, %d bytes remain\n", sent_bytes, data_remaining);
}
printf("File %s has finished sending....\n", filename);
close(fd);
}
client.c
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <dirent.h>
/**
* Structure used to contain the type of command: 'get' or 'put'
* as well as the name of the file
*/
struct command{
char* type;
char* filename;
};
void syserr(char* msg) { perror(msg); exit(-1); }
/*function prototypes*/
void listDirectories();
struct command getCommand(char*);
int main(int argc, char *argv[]){
int sockfd, portno, n;
int32_t *convCount, *convSize;
struct hostent* server;
struct sockaddr_in serv_addr;
FILE *received_file;
char buffer[256];
if(argc != 3){
fprintf(stderr, "Usage: %s <hostname> <port>\n", argv[0]);
return 1;
}
server = gethostbyname(argv[1]);
if(!server){
fprintf(stderr, "ERROR: no such host: %s\n", argv[1]);
return 2;
}
portno = atoi(argv[2]);
sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sockfd < 0) syserr("Error opening socket.");
printf("create socket...\n");
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr = *((struct in_addr*)server->h_addr);
serv_addr.sin_port = htons(portno);
if(connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0){
syserr("can't connect to server");
}
printf("connection to %s:%s established. Now awaiting commands...\n", argv[1], argv[2]);
while(1){
// printf("PLEASE ENTER MESSAGE: ");
printf("%s:%s> ", argv[1], argv[2]);
fgets(buffer, 255, stdin);
n = strlen(buffer);
if(n>0 && buffer[n-1] == '\n'){
buffer[n-1] = '[=11=]';
}
if(strcmp(buffer, "exit") == 0){
n = send(sockfd, buffer, strlen(buffer), 0);
if(n < 0) syserr("can't send 'exit' command to server");
printf("send...\n");
break;
}else if(strcmp(buffer, "ls-local") == 0){
listDirectories();
}else if(strcmp(buffer, "ls-remote") == 0){
n = send(sockfd, buffer, strlen(buffer), 0);
if(n < 0) syserr("can't send to server");
printf("send...\n");
n = recv(sockfd, convCount, sizeof(int32_t), 0);
if(n < 0) syserr("can't receive from server");
int fileCount = ntohl(*convCount);
// printf("CLIENT RECEIVED FILE COUNT: %d\n", fileCount);
printf("Files at server (%s:%s):\n", argv[1], argv[2]);
int i = 0;
while(i < fileCount){
n = recv(sockfd, buffer, 255, 0);
if(n < 0) syserr("can't receive from server");
printf("%s\n", buffer);
i++;
}
}else{
char cmdCopy[256];
strcpy(cmdCopy, buffer);
struct command userCommand = getCommand(cmdCopy);
if(strcmp(userCommand.type, "invalid") != 0){
printf("Command %s %s is valid\n", userCommand.type, userCommand.filename);
/**
* 'get' command
*/
if(strcmp(userCommand.type, "get") == 0){
//Re-append the entire command since
n = send(sockfd, buffer, strlen(buffer), 0);
if(n < 0) syserr("can't send to server");
printf("send GET request for file: %s\n", userCommand.filename);
n = recv(sockfd, buffer, 255, 0);
if(n < 0) syserr("can't receive from server");
else buffer[n] = '[=11=]';
if(strcmp(buffer, "ERROR: FILE DOES NOT EXIST IN REMOTE DIRECTORY") != 0
&& strcmp(buffer, "ERROR: File could not be opened by server....") != 0
&& strcmp(buffer, "ERROR:File stats could not be obtained by server....") != 0){
printf("%s\n", buffer);
//// Not working for some reason, must receive as char*
// n = recv(sockfd, convSize, sizeof(int32_t), 0);
// printf("N IS: %d\n", n);
// if(n < 0) syserr("can't receive file size from server");
// int fileSize = ntohl(*convSize);
// printf("Size of file: %s to be received is %d\n bytes",buffer, fileSize);
// printf("Receiving file: %s \n....", userCommand.filename);
n = recv(sockfd, buffer, 255, 0);
if(n < 0) syserr("can't receive size from server");
else buffer[n] = '[=11=]';
int data_remaining = atoi(buffer);
printf("file size is %d\n", data_remaining);
received_file = fopen(userCommand.filename, "w");
if (received_file == NULL){
syserr("Failed to open file.");
}else{
char file_buffer[BUFSIZ];
int bytes_received;
while((bytes_received = recv(sockfd, file_buffer, BUFSIZ, 0)) >0 && data_remaining > 0){
fwrite(file_buffer, 1, bytes_received, received_file);
data_remaining -= bytes_received;
printf("Received %d bytes of file, %d bytes remain\n", bytes_received, data_remaining);
}
printf("File receive complete\n");
fclose(received_file);
}
}else{
printf("%s\n", buffer);
}
}
/**
* 'put' command
* no check needed for 'put' because only possible options at this point are 'get' and 'put'
* since invalid commands are filtered out in getCommand method
*/
else{
// //File exists
// if(fileExists(userCommand.filename) == 1){
// sendFile(userCommand.filename, newsockfd);
// }
// //File does not exist
// else{
// printf( "ERROR: FILE DOES NOT EXIST IN LOCAL DIRECTORY\n");
// }
}
}
//USER TYPES INVALID COMMAND
else{
printf("INVALID COMMAND: you may only send 'ls-remote' OR ' get ' or ' put ' followed by a filename");
}
}
// n = send(sockfd, buffer, strlen(buffer), 0);
// if(n < 0) syserr("can't send to server");
// printf("send...\n");
// while( (n = recv(sockfd , buffer , 255 , 0)) > 0 ){
// printf("in while and n is: %d\n", n);
// printf("%s", buffer);
// }
// if(n < 0){
// printf("in if and n is: %d\n", n);
// syserr("can't receive from server");
// }else{
// printf("in else and n is: %d\n", n);
// buffer[n] = '[=11=]';
// }
// printf("in no loop and n is: %d\n", n);
}
close(sockfd);
return 0;
}
// printf("connection to %s:%s established. Now awaiting commands...\n", argv[1], argv[2]);
// do{
// printf("%s:%s> ", argv[1], argv[2]);
// fgets(buffer, 255, stdin);
// n = strlen(buffer);
// if(n>0 && buffer[n-1] == '\n'){
// buffer[n-1] = '[=11=]';
// }
// n = send(sockfd, buffer, strlen(buffer), 0);
// if(n < 0) syserr("can't send to server");
// printf("send...\n");
// n = recv(sockfd, buffer, 255, 0);
// if(n < 0) syserr("can't receive from server");
// else buffer[n] = '[=11=]';
// printf("CLIENT RECEIVED MESSAGE: %s\n", buffer);
// }while(strcmp(buffer, "exit") != 0);
// close(sockfd);
// return 0;
/**
* Method that extracts a valid command from a given string
* @param string the string containing the commands
* @return the valid command if found, a command with type 'invalid' if not found
*/
struct command getCommand(char* string){
char* temp;
int count;
struct command userCommand;
count = 0;
//Split the string on spaces, if more than one space then # of arguments is > 2, thus invalid command
temp = strtok(string, " ");
while(temp != NULL){
if(count == 0){
userCommand.type = temp;
}else if(count == 1){
userCommand.filename = temp;
}else{
userCommand.type = "invalid";
break;
}
temp = strtok(NULL, " ");
count++;
}
//We test count ==1 because this means only one space in string but also only one word because count did not increment again
//which is still an invalid command
if(strcmp(userCommand.type, "get") != 0 && strcmp(userCommand.type, "put") != 0 || count == 1){
userCommand.type = "invalid";
}
return userCommand;
}
/**
* Method that executes when the command 'ls-local' is received, sends entire file listing in working directory
*/
void listDirectories(){
DIR* directory;
struct dirent *dir;
directory = opendir(".");
if (directory){
printf("Files at the client:\n");
while ((dir = readdir(directory)) != NULL){
//ignore current dir and parent dir names
if(strcmp(dir->d_name, ".") != 0 && strcmp(dir->d_name, "..") != 0){
printf("%s\n", dir->d_name);
}
}
closedir(directory);
}
}
printf("SERVER GOT MESSAGE: %s\n", buffer);
没有,您没有收到消息。你有一些字节。 TCP 不是消息协议。如果要收发消息,就得写代码收发消息,你没做过
"TCP is a reliable, byte-stream protocol that does not preserve application message boundaries."记住这个。在理解其中的所有内容之前,不要编写任何使用 TCP 的代码,否则您的代码注定会失败。
另一个要点:在您对在 TCP 之上使用的协议有规范之前,不要编写任何使用 TCP 的代码。谁什么时候发送?有超时吗?哪一方实施它们?连接是如何关闭的?谁发起的?是否存在应用程序消息边界?它们是如何标记的? 等等。花时间详细记录这一点真的很值得。您可以查看使用 TCP(例如 HTTP、SMTP、IRC 等)的现有协议的规范,以了解协议规范应该是什么样子的示例。
除了 David Schwartz 指出的代码中的基本谬误之外,您还犯了所有常见错误。
bzero(buffer,256);
冗余。移除。
n = recv(newsockfd, buffer, 255, 0);
if(n < 0) syserr("can't receive from client");
else
此处缺少 n == 0
和 break
的测试。
buffer[n] = '[=12=]';
仅当数据完全是文本时才有效。在整个过程中,包括以下 printf()
,您应该做的是使用 n
作为您从缓冲区中检索内容的唯一上限。
如前所述,您没有理由相信这是一条完整的消息,或者只有一条消息。
解决您眼前的问题,有两个问题:
server.c第一名:
while((sent_bytes = send(newsockfd, file_buffer, file_stat.st_size, 0)) > 0 && data_remaining > 0){
每次发送时,您将发送 file_stat.st_size
个字节,而不是 data_remaining
个字节。
此外,您应该先检查 data_remaining > 0
。由于 &&
运算符是短路运算符,因此除非第一部分的计算结果为真,否则它不会计算第二部分。因此,如果您还剩下 0 个字节要发送,它将尝试发送它们。
所以上面一行应该是:
while((data_remaining > 0) && ((sent_bytes = send(newsockfd, file_buffer, file_stat.st_size, 0)) > 0)) {
与 client.c 类似,您有:
while((bytes_received = recv(sockfd, file_buffer, BUFSIZ, 0)) >0 && data_remaining > 0){
如果您收到了整个文件,它会再尝试一次 recv
,然后才意识到 data_remaining > 0
是错误的。由于服务器已完成发送,它会一直等待。
和以前一样,调换 &&
个操作数的顺序:
while((data_remaining > 0) && ((bytes_received = recv(sockfd, file_buffer, BUFSIZ, 0)) > 0)){
这至少应该让你 运行。
除此之外还有其他问题。正如 David Schwartz 所提到的,TCP 的流媒体特性不会保留消息边界。
这意味着仅仅因为您尝试读取 255 个字节并不意味着您实际上会得到那么多。在您期望特定大小的消息的任何地方,请确保您实际获得了那么多字节。如果您还没有读够,请继续阅读直到读完。