使用套接字

时间:2016-01-29 19:10:09

标签: c sockets client-server

我已经使用socket实现了mget命令。但我得到的输出是一些随机行为。有时我可以在客户端下载整个文件,有时我可以部分下载文件,有时服务器代码会出现分段错误,有时客户端会进入无限循环。

服务器代码:

      /* A simple server in the internet domain using TCP
   The port number is passed as an argument */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>

int main(int argc, char *argv[])
{
     int sockfd, newsockfd, portno;
     socklen_t clilen;
//     char buffer[256];
     struct sockaddr_in serv_addr, cli_addr;
     int n;
     if (argc < 2) {
         fprintf(stderr,"ERROR, no port provided\n");
         exit(1);
     }
     sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
     if (sockfd < 0) 
        error("ERROR opening socket");
     bzero((char *) &serv_addr, sizeof(serv_addr));
     portno = atoi(argv[1]);
     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) 
              error("ERROR on binding");
     listen(sockfd,5);
     clilen = sizeof(cli_addr);
     newsockfd = accept(sockfd, 
                 (struct sockaddr *) &cli_addr, 
                 &clilen);
     if (newsockfd < 0) 
          error("ERROR on accept");
     /*bzero(buffer,256);
     n = read(newsockfd,buffer,255);
     if (n < 0) error("ERROR reading from socket");
     printf("Here is the message: %s\n",buffer);
     n = write(newsockfd,"I got your message",18);
     if (n < 0) error("ERROR writing to socket");*/

     char command[512];
     bzero(command, 512);

    char buffer[4096];
     char output[5000];
     char f_size[20];

     int send_bytes;
     int written_bytes;
     int total_sent_bytes = 0;

     long int file_size;

     n = recv(newsockfd, command, 512, 0);  //try with MSG_WAITALL
     if(n < 0)error("Error receiving command");
     puts(command); //only file name

     FILE * fp;
     fp = fopen(command, "rb");
     if(fp == NULL)error("Can not open requested file");

     FILE * test_file_fp;
     test_file_fp = fopen("test_file.txt", "wb");
     if(test_file_fp == NULL)error("Can not open test file");

     fseek(fp,0, SEEK_END);
     file_size = ftell(fp);
     rewind(fp);

     sprintf(f_size,"%ld", file_size);

     send(newsockfd, f_size, strlen(f_size), 0);

     while(!feof(fp)){
         bzero(buffer, 4096);

         n = fread(buffer, sizeof(char), 4095, fp);
         sprintf(output, "read bytes using fread = %d", n);
         puts(output);

         send_bytes = send(newsockfd, buffer, strlen(buffer) + 1, MSG_MORE);
         total_sent_bytes += send_bytes;
         sprintf(output, "sent bytes using send = %d", send_bytes);
         puts(output);

         written_bytes = fwrite(buffer, sizeof(char), strlen(buffer), test_file_fp);
         sprintf(output, "written bytes using fwrite = %d", written_bytes);
         puts(output);

        //bzero(command, 512);

        //recv(newsockfd, buffer, 512, 0);

        puts("\n");
     }

     sprintf(output, "total sent bytes using send = %d\n", total_sent_bytes);
     puts(output);

     send(newsockfd, NULL, 1, 0);

     fclose(test_file_fp);
     fclose(fp);
     close(newsockfd);
     close(sockfd);
     return 0; 
}

客户代码:

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

void error(const char *msg)
{
    perror(msg);
    exit(0);
}

int main(int argc, char *argv[])
{
    int sockfd, portno, n;
    struct sockaddr_in serv_addr;
    struct hostent *server;
    int i;
//    char buffer[5000];
    if (argc < 3) {
       fprintf(stderr,"usage %s hostname port\n", argv[0]);
       exit(0);
    }
    portno = atoi(argv[2]);
    sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (sockfd < 0)
        error("ERROR opening socket");
    server = gethostbyname(argv[1]);
    if (server == NULL) {
        fprintf(stderr,"ERROR, no such host\n");
        exit(0);
    }
    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    bcopy((char *)server->h_addr,
         (char *)&serv_addr.sin_addr.s_addr,
         server->h_length);
    serv_addr.sin_port = htons(portno);
    if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
        error("ERROR connecting");


    char command[512];
    char output[5096];

    int send_command_bytes;
    int reveived_bytes;
    char buffer[4096];

    long int total_bytes;
    long int total_received_bytes;

    int written_bytes;

    sprintf(output, "IP Protocol Number = %d", IPPROTO_TCP);
     puts(output);

    FILE * fp;

    fp = fopen("received_file.txt", "wb");
    if(fp == NULL)error("Could not open file for receiving data");

    memcpy(command, "send_me.txt", 11); 

    send_command_bytes = send(sockfd, command, strlen(command), 0);
    puts(command);

    recv(sockfd, buffer, 4096, 0);
    total_bytes = atoi(buffer);

    sprintf(output, "Total bytes to be received = %ld", total_bytes);
    puts(output);

    total_received_bytes = 0;

    while(totala_received_bytes < total_bytes){
        bzero(buffer, 4096);
        reveived_bytes = recv(sockfd, buffer, 4095, 0); //try with MSG_WAITALL, MSG_DONTWAIT
        total_received_bytes += reveived_bytes;
        sprintf(output, "Number of bytes received = %d", reveived_bytes);
        puts(output);

        written_bytes = fwrite(buffer, sizeof(char), strlen(buffer), fp);
        sprintf(output, "Total written bytes = %d", written_bytes);
        puts(output);

        //send(sockfd, "1", 2, 0);

        sprintf(output, "total Number of bytes received so far = %ld", total_received_bytes);
        puts(output);
        puts("\n");
    }
    fclose(fp);
    close(sockfd);
    return 0;
}

从客户端我发送文件名&#34; send_me.txt&#34;需要下载的文件名由服务器在名为command的缓冲区中读取。此文件正在服务器中打开,然后读取然后发送到客户端。 我已经尝试了许多可能性来克服这个问题,但问题仍然存在。

1 个答案:

答案 0 :(得分:0)

在处理套接字通信时,您的代码存在两个常见问题

<强> 1。缺乏NUL终止
C中的字符串NUL已终止,因此您需要将NUL作为邮件的一部分发送,或者您需要在接收方添加NUL

<强> 2。关于邮件大小的假设
在套接字编程中,每个recv都会匹配相应的send,这是一个常见的误解。例如,如果I send 10个字节,后跟50个字节,后来是20个字节,则期望recv需要调用三次,并返回10个字节,50个字节,和20个字节。这完全是完全错误的。实际上,第一个recv将从1个字节返回到80个字节。也就是说,它可以从第一个send返回数据的任何部分,直到所有sends的所有数据。

您的代码示例
在服务器代码中,您可以执行此操作

 sprintf(f_size,"%ld", file_size);
 send(newsockfd, f_size, strlen(f_size), 0);

如果file_size为123,那么sprintf会将123\0写入f_size,即三个数字和NUL终止符。 strlen将返回3,因此代码只发送三位数,但不发送NUL终结符。

在客户端,你有这个

recv(sockfd, buffer, 4096, 0);
total_bytes = atoi(buffer);

由于服务器没有发送NUL字节而客户端没有添加NUL字节,atoi会看到recv放入buffer的内容然后是垃圾。如果你很幸运,第一个垃圾字符不会成为一个数字,atoi将正常工作。如果你不幸,第一个垃圾字符将是一个数字。

除了缺少NUL之外,recv可能只有1个字节,在这种情况下文件大小为1(假设第一个垃圾字符不是数字)。或者recv可能得到100个字节(3个字节长度加上文件中的一些字节)。如果文件恰好以数字开头,则大小错误。

修复代码
在服务器端,将文件大小作为包含NUL的固定长度消息发送,例如

#define MSGLEN 24
char buffer[MSGLEN];
snprintf( buffer, MSGLEN, "%*ld", MSGLEN-1, file_size );
send( sockfd, buffer, MSGLEN, 0 );

在客户端,读取一个循环,直到你拥有该大小的所有字节,但不再有

#define MSGLEN 24
char buffer[MSGLEN];
int count = 0;
int index;
for ( index = 0; index < MSGLEN; index += count )
{
    count = recv( sockfd, &buffer[index], MSGLEN - index, 0 );
    if ( count <= 0 )
        break;
}
if ( index != MSGLEN || buffer[MSGLEN-1] != '\0' )
{
    printf( "bad file size\n" );
    exit( 1 );
}
total_bytes = atoi( buffer );

最后一个注释
while(!feof(fp))-is-always-wrong