gpt4 book ai didi

C TCP套接字,发送文件的回显服务器,发送文件后挂断

转载 作者:可可西里 更新时间:2023-11-01 02:30:37 24 4
gpt4 key购买 nike

我想编写一个简单的 TCP 回显服务器应用程序。我设法完成了回声部分,但在客户端和服务器之间发送文件时遇到了一些问题。这个想法很简单:尽管发送普通消息,客户端可以向服务器发送一个特殊的命令(\SENDFILE filename.txt),服务器收到这个命令后应该向客户端请求这个文件,并从客户端获取文件。 (此外,我想从一个客户端获取文件,然后将其发送给另一个客户端)。

我认为这里的“协议(protocol)”很简单,但是,在客户端键入\SENDFILE 后,客户端挂断,并且不会从服务器接收任何进一步的消息。此外(服务器和客户端在不同的目录中)在服务器端只有一个来自客户端的空文件,里面没有内容。

知道这里有什么问题吗?

客户端.c

#include<stdio.h> //printf
#include<string.h> //
#include <sys/stat.h>
#include<sys/socket.h> //socket
#include<arpa/inet.h> //inet_addr
#include <fcntl.h>
#define SERVER_PORT 9034
#define BUFF_SIZE 2000

int sendall(int s, char *buf, int len)
{
int total = 0;
int bytesleft = len;
int n;

while(total < len)
{
n = send(s, buf+total, bytesleft, 0);
if (n == -1)
break;
total += n;
bytesleft -= n;
}

return n==-1?-1:0;
}

void SendMsgToSender(char *msg, int connfd)
{
write(connfd, msg, strlen(msg));
memset(msg, 0, BUFF_SIZE);
}

int main(int argc , char *argv[])
{
int sock;
struct sockaddr_in server;
char bufferOUT[BUFF_SIZE] , bufferIN[BUFF_SIZE];
struct stat file_stat;

memset(bufferOUT, 0, BUFF_SIZE);
memset(bufferIN, 0, BUFF_SIZE);

//Create socket
sock = socket(AF_INET , SOCK_STREAM , 0);
if (sock == -1)
{
printf("Could not create socket");
}
// puts("Socket created");

server.sin_addr.s_addr = inet_addr("127.0.0.1");
server.sin_family = AF_INET;
server.sin_port = htons( SERVER_PORT );

//Connect to remote server
if (connect(sock , (struct sockaddr *)&server , sizeof(server)) < 0)
{
perror("Connect failed. Error");
return 1;
}

// puts("Connected\n");
int read_size = 10;

//keep communicating with server
while(1)
{
printf("> ");
fgets(bufferOUT, BUFF_SIZE, stdin);

//Send some data
if( send(sock , bufferOUT , BUFF_SIZE , 0) < 0)
{
perror("Send failed");
return 1;
}

//Receive a reply from the server
if( (read_size = recv(sock , bufferIN , BUFF_SIZE , 0)) < 0)
{
perror("Recv failed");
break;
}

if(read_size == 0)
break;

if(bufferIN[0] == '\\')
{
char tmp[BUFF_SIZE], filename[BUFF_SIZE], *param;
memset(filename, BUFF_SIZE, 0);
strcpy(tmp, bufferIN);

param = strtok(tmp, " ");
if(param != NULL)
{
if(!strcmp(param, "\\GIVEMEFILE"))
{
param = strtok(NULL, " ");
if(param != NULL)
{
strcpy(filename, param);
FILE * fp;
int nBytes;
char buffer[BUFF_SIZE], *s;
memset(buffer, 0, BUFF_SIZE);

fp = fopen(filename, "r");
if(fp == NULL)
{
perror("fopen");
fflush(stdout);
break;
}

int remain_data = file_stat.st_size;

do
{
s = fgets(buffer, BUFF_SIZE, fp);
if(s != NULL && buffer[0] != EOF)
{
nBytes = sendall(sock, buffer, BUFF_SIZE);
remain_data -= nBytes;
}
else
break;
}
while((s != NULL) && (nBytes > 0) && (remain_data > 0));
fclose(fp);

memset(bufferOUT, 0, BUFF_SIZE);
memset(bufferIN, 0, BUFF_SIZE);

continue;
}
}
}
}
else
{
printf("%s\n", bufferIN);
fflush(stdout);
}

memset(bufferOUT, 0, BUFF_SIZE);
memset(bufferIN, 0, BUFF_SIZE);
}

close(sock);
return 0;
}

server.c

    #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <time.h>
#include <fcntl.h>

#define SERVER_PORT 9034
#define BUFF_SIZE 2000

void StripNewline(char *s)
{
while(*s != '\0')
{
if(*s == '\r' || *s == '\n')
{
*s = '\0';
}
s++;
}
}

void SendMsgToSender(char *msg, int connfd)
{
write(connfd, msg, strlen(msg));
memset(msg, 0, BUFF_SIZE);
}

// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET)
{
return &(((struct sockaddr_in*)sa)->sin_addr);
}

return &(((struct sockaddr_in6*)sa)->sin6_addr);
}

int GetFileFromClient(int connfd, char *filename)
{
FILE * fp = NULL;
int bytes;
char buffer[BUFF_SIZE];
memset(buffer, 0, BUFF_SIZE);

fp = fopen(filename, "w");
if(fp == NULL)
return 0;

memset(buffer, 0, BUFF_SIZE);
sprintf(buffer, "\\GIVEMEFILE %s \r\n", filename);
SendMsgToSender(buffer, connfd);

while(1)
{
memset(buffer ,0 , BUFF_SIZE);
if((bytes = recv(connfd , buffer , BUFF_SIZE , 0) ) <= 0)
return 0;
else
fprintf(fp, "%s\n", buffer);
}

fclose(fp);
sleep(1);

memset(buffer, 0, BUFF_SIZE);
sprintf(buffer, "\r\n");
SendMsgToSender(buffer, connfd);

return 1;
}

int main(void)
{
fd_set master;
fd_set read_fds;
int fdmax;

int listener;
int client_sock;
struct sockaddr_storage remoteaddr;
socklen_t addrlen;

char bufferIN[BUFF_SIZE], bufferOUT[BUFF_SIZE], tmp[BUFF_SIZE], *datetime;
int nbytes;

char remoteIP[INET6_ADDRSTRLEN];

int yes=1;
int i, j, rv;

struct addrinfo hints, *ai, *p;

FD_ZERO(&master);
FD_ZERO(&read_fds);

memset(bufferIN, 0, BUFF_SIZE);
memset(bufferOUT, 0, BUFF_SIZE);
memset(tmp, 0, BUFF_SIZE);

memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;

char port[16] = "9034";
if (getaddrinfo(NULL, port, &hints, &ai) < 0)
{
fprintf(stderr, "selectserver: %s\n", gai_strerror(rv));
exit(1);
}

for(p = ai; p != NULL; p = p->ai_next)
{
listener = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (listener < 0)
{
continue;
}

setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));

if (bind(listener, p->ai_addr, p->ai_addrlen) < 0)
continue;

break;
}

if (p == NULL)
exit(2);

freeaddrinfo(ai);

if (listen(listener, 10) == -1)
{
perror("listen");
exit(3);
}

FD_SET(listener, &master);
fdmax = listener;

printf("Server is running ...\n\n");

for(;;)
{
read_fds = master;
if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1)
{
perror("select");
exit(4);
}

for(i = 0; i <= fdmax; i++)
{
if (FD_ISSET(i, &read_fds))
{
if (i == listener)
{
addrlen = sizeof remoteaddr;
client_sock = accept(listener,
(struct sockaddr *)&remoteaddr,
&addrlen);

if (client_sock == -1)
{
perror("accept");
}
else
{
FD_SET(client_sock, &master);
if (client_sock > fdmax)
fdmax = client_sock;
}
}
else
{
if ((nbytes = recv(i, bufferIN, BUFF_SIZE, 0)) <= 0)
{
if (nbytes == 0)
close(i);

else if(nbytes == -1)
{
perror("recv");
fflush(stdout);
}

close(i);
FD_CLR(i, &master);
}
else
{
bufferIN[nbytes-1] = '\0';
StripNewline(bufferIN);
strcpy(tmp, bufferIN);

if(bufferIN[0] == '\\')
{
char *command, *param;
command = strtok(bufferIN, " ");

if(!strcmp(command, "\\QUIT"))
{
close(i);
FD_CLR(i, &master);
break;
}

else if(!strcmp(command, "\\SENDFILE"))
{
param = strtok(tmp, " ");
if(param != NULL)
{
param = strtok(NULL, " ");
if(param != NULL)
{
printf("Client is sending me a file '%s'...\n", param);
GetFileFromClient(i, param);
}
}
}
else
{
SendMsgToSender(bufferIN, i);
}

memset(bufferIN, 0, BUFF_SIZE);
memset(bufferOUT, 0, BUFF_SIZE);
}
else
{
SendMsgToSender(bufferIN, i);
}
}
} // END handle data from client
} // END got new incoming connection
} // END looping through file descriptors
} // END for(;;)

memset(bufferIN, 0, BUFF_SIZE);
memset(bufferOUT, 0, BUFF_SIZE);

return 0;
}

最佳答案

strcpy(tmp, bufferIN);

这里您假设读取的内容以 null 结尾。

            param = strtok(tmp, " ");
if(param != NULL)
{
if(!strcmp(param, "\\GIVEMEFILE"))

此处您假设已收到整条消息。

                strcpy(filename, param);

同上。

                        memset(buffer, 0, BUFF_SIZE);

毫无意义。删除。

                        do
{
s = fgets(buffer, BUFF_SIZE, fp);

这里假设文件由行组成。

                            if(s != NULL && buffer[0] != EOF)

测试 buffer[0] !=EOF 没有意义。如果你已经到达 EOF,s 将是空的,假设文件由行组成,但是除了它不是' t 行终止符。

                        memset(bufferOUT, 0, BUFF_SIZE);
memset(bufferIN, 0, BUFF_SIZE);

都没有意义。删除。

        memset(bufferOUT, 0, BUFF_SIZE);
memset(bufferIN, 0, BUFF_SIZE);

同上。

void StripNewline(char *s)

这个方法看起来毫无意义。删除。

void SendMsgToSender(char *msg, int connfd)
{
write(connfd, msg, strlen(msg));

这里您向对等方发送一个字符串没有尾随的空值,对等方正在上面的strlen() 中查找。仔细考虑您的应用程序协议(protocol)实际需要什么。

    memset(msg, 0, BUFF_SIZE);

毫无意义。删除。

int GetFileFromClient(int connfd, char *filename)
{
FILE * fp = NULL;
int bytes;
char buffer[BUFF_SIZE];
memset(buffer, 0, BUFF_SIZE);

毫无意义。删除。

    memset(buffer, 0, BUFF_SIZE);

同上。

    sprintf(buffer, "\\GIVEMEFILE %s \r\n", filename);
SendMsgToSender(buffer, connfd);

while(1)
{
memset(buffer ,0 , BUFF_SIZE);

毫无意义。删除。

        if((bytes =  recv(connfd , buffer , BUFF_SIZE , 0) ) <= 0)
return 0;

这里需要区分(1)bytes == 0,表示对端断开,(2)byte == -1,表示一个错误,您需要通过errnostrerror() 和 friend 记录

        else
fprintf(fp, "%s\n", buffer);

更改为 fprintf(fp, "%.*s\n", bytes, buffer)。您始终假设所有消息都由 TCP 以空值终止。他们不是。

    sleep(1);

毫无意义。删除。

    memset(buffer, 0, BUFF_SIZE);

同上。

    sprintf(buffer, "\r\n");
SendMsgToSender(buffer, connfd);

向对等方发送行终止符似乎完全没有意义。

    memset(bufferIN, 0, BUFF_SIZE);
memset(bufferOUT, 0, BUFF_SIZE);
memset(tmp, 0, BUFF_SIZE);

一切毫无意义。删除。

        if (bind(listener, p->ai_addr, p->ai_addrlen) < 0)
continue;

这里你需要打印错误信息而不是忽略条件。

        if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1)

您还没有将监听套接字置于非阻塞模式。因此,使用 select() 毫无意义。

                        bufferIN[nbytes-1] = '\0';
StripNewline(bufferIN);

为什么?

                        strcpy(tmp, bufferIN);

为什么?继续使用 bufferIN 有什么问题?

                        if(bufferIN[0] == '\\')
{
char *command, *param;
command = strtok(bufferIN, " ");

这里您再次假设收到了一个完整的命令,并以尾随 null 完成。

                            memset(bufferIN, 0, BUFF_SIZE);
memset(bufferOUT, 0, BUFF_SIZE);

都没有意义。消除。这只是 cargo 崇拜编程。 recv() 返回一个长度。使用它。

    memset(bufferIN, 0, BUFF_SIZE);
memset(bufferOUT, 0, BUFF_SIZE);

毫无疑问,同上。

基本上您遇到了应用程序协议(protocol)问题。具体来说,您没有应用程序协议(protocol)。只是一大堆毫无根据的假设。如果您想要一个尾随空值,(a) 发送 一个尾随空值,并且 (b) 循环 读取直到您收到它。您还对正在发送的文件的内容有一个假设,这是完全没有必要的。只需从文件中读取字节并将它们发送到服务器。无需假设行或行终止符。如果您通过同一个连接发送多个文件,您需要在发送文件之前发送文件大小,这样接收方将确切知道要读取多少字节并将其复制到文件。

本质上,您需要彻底重新考虑这一点。

关于C TCP套接字,发送文件的回显服务器,发送文件后挂断,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39635184/

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