我已经尝试了一切。不知道我的程序发生了什么。我写了一个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 命令,它就会在我的客户端终端上疯狂地不停地打印乱码。
我已经包含了这两个文件,以防您想测试它们以了解我的意思。用法是:'获取文件名',没有单引号。请记住,它在第一次之后就崩溃了,第一次工作得很好。抱歉文件太长,发送文件代码位于名为 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] = '\0';
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] = '\0';
}
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] = '\0';
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] = '\0';
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] = '\0';
// }
// 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] = '\0';
// }
// 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] = '\0';
// 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 不是消息协议(protocol)。如果你想发送和接收消息,你必须编写代码来发送和接收消息,而你还没有这样做。
“TCP 是一种可靠的字节流协议(protocol),不保留应用程序消息边界。”记住这一点。在您理解其中的所有内容之前,不要编写任何使用 TCP 的代码,否则您的代码注定会失败。
另一个要点:在您对 TCP 上使用的协议(protocol)有规范之前,不要编写任何使用 TCP 的代码。谁什么时候发送?有超时吗?由哪一方实现?连接如何关闭?谁发起的?是否存在应用程序消息边界?它们是如何标记的? 等等。确实值得花时间详细记录这一点。您可以查看使用 TCP 的现有协议(protocol)(例如 HTTP、SMTP、IRC 等)的规范,以获取协议(protocol)规范的示例。
关于c - 我的 c 套接字程序在成功执行命令后在命令上吐出垃圾,不知道为什么,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32833636/