在C Linux中通过Socket发送图像(JPEG)

13

我正在编写一个小型C程序,以便能够使用TCP/IP套接字在两台运行Linux的计算机之间传输图像文件(从服务器到客户端),但似乎存在错误,因为我的图片在另一侧显示为损坏。

我的服务器代码如下:

#include<stdio.h>
#include<string.h>
#include<sys/socket.h>
#include<arpa/inet.h>   
#include<unistd.h>  
#include<iostream>
#include<fstream>
#include<errno.h>

using namespace std;

int send_image(int socket){

FILE *picture;
int size, read_size;
char send_buffer[10240], verify;

picture = fopen("2.jpg", "r");
printf("Getting Picture Size\n");   

if(picture == NULL) {
   printf("Error Opening Image File");
} 

fseek(picture, 0, SEEK_END);
size = ftell(picture);
fseek(picture, 0, SEEK_SET);

//Send Picture Size
printf("Sending Picture Size\n");
write(socket, (void *)&size, sizeof(int));

if(read_size = read(socket, &verify , sizeof(char)) < 0) {
   puts("\nError Receiving Verification");
}


if(verify == '1'){
    printf("5\n");
    //Send Picture as Byte Array
    printf("Sending Picture as Byte Array\n");

    while(!feof(picture)) {

          //Read from the file into our send buffer
          read_size = fread(send_buffer, 1, sizeof(send_buffer)-1, picture);

          //Send data through our socket 
          write(socket, send_buffer, read_size);                        

          //Wait for the verify signal to be received 
          while(read(socket, &verify , sizeof(char)) < 0);

          if(verify != '1') {
             printf("Error Receiving the Handshake signal\n %s",&verify);
          }

          verify = '';

          //Zero out our send buffer
          bzero(send_buffer, sizeof(send_buffer));
   }
}
}

int main(int argc , char *argv[])
{
int socket_desc , new_socket , c, read_size,buffer = 0;
struct sockaddr_in server , client;
char *readin;

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

//Prepare the sockaddr_in structure
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons( 8889 );

//Bind
if( bind(socket_desc,(struct sockaddr *)&server , sizeof(server)) < 0)
{
    puts("bind failed");
    return 1;
}

puts("bind done");

//Listen
listen(socket_desc , 3);

//Accept and incoming connection
puts("Waiting for incoming connections...");
c = sizeof(struct sockaddr_in);

if((new_socket = accept(socket_desc, (struct sockaddr *)&client, (socklen_t*)&c))){
    puts("Connection accepted");
}

fflush(stdout);

if (new_socket<0)
{
    perror("Accept Failed");
    return 1;
}

send_image(new_socket);

    close(socket_desc);
    fflush(stdout);
return 0;
}
客户端接收数据的代码如下:
 #include<stdio.h>
 #include<string.h>    //strlen
 #include<sys/socket.h>
 #include<sys/ioctl.h>
 #include<arpa/inet.h>    
 #include<unistd.h>
 #include<iostream>
 #include<errno.h>
 using namespace std;

 //This function is to be used once we have confirmed that an image is to be sent
 //It should read and output an image file
 int receive_image(int socket){

int buffersize = 0, recv_size = 0,size = 0, read_size, write_size;
char imagearray[10241],verify = '1';
FILE *image;

//Find the size of the image
read(socket, &size, sizeof(int));



//Send our verification signal
write(socket, &verify, sizeof(char));
//Make sure that the size is bigger than 0
if(size <= 0 ){
    printf("Error has occurred. Size less than or equal to 0\n");
    return -1;
}

image = fopen("2.jpg", "w");

if( image == NULL) {
    printf("Error has occurred. Image file could not be opened\n");
    return -1;
}

//Loop while we have not received the entire file yet
while(recv_size < size) {
    ioctl(socket, FIONREAD, &buffersize); 

    //We check to see if there is data to be read from the socket    
    if(buffersize > 0 ) {

        if(read_size = read(socket,imagearray, buffersize) < 0){
            printf("%s", strerror(errno));
        }

        //Write the currently read data into our image file
        write_size = fwrite(imagearray,1,(buffersize), image);

        if(write_size != buffersize) {
          printf("write and buffersizes wrong");
        }

        if(read_size !=write_size) {
            printf("error in read write");
        }

        //Increment the total number of bytes read
        recv_size += read_size;

                    //Send our handshake verification info
        write(socket, &verify, sizeof(char));

    }
 }

fclose(image);
printf("Image successfully Received!\n");
return 1;
}

int main(int argc , char *argv[])
{

int socket_desc;
struct sockaddr_in server;
char *parray;


//Create socket
socket_desc = socket(AF_INET , SOCK_STREAM , 0);

if (socket_desc == -1) {
    printf("Could not create socket");
}

memset(&server,0,sizeof(server));
server.sin_addr.s_addr = inet_addr("10.42.0.1");
server.sin_family = AF_INET;
server.sin_port = htons( 8889 );

//Connect to remote server
if (connect(socket_desc , (struct sockaddr *)&server , sizeof(server)) < 0) {
    cout<<strerror(errno);
    close(socket_desc);
    puts("Connect Error");
    return 1;
}

puts("Connected\n");

receive_image(socket_desc);

close(socket_desc);

return 0;
}

有人能帮我解决这个问题吗?我实在是想不出来这个错误的原因了。

编辑:我将fwrites和freads改回常规write和read,但仍然发送损坏的图像。


1
你不需要使用 ioctlFIONREAD;如果没有数据,就让它阻塞。反正你在这段时间里也没有做其他事情。 - icktoofay
你确认每个read_size都对应每个write_size了吗? - Dan Nissenbaum
可能是在Linux中使用C/C++(GCC/G++)的套接字编程发送和接收文件的重复问题。 - Ciro Santilli OurBigBook.com
3个回答

10
你有几个问题:
- 你需要以二进制模式打开文件(读取用 "rb",写入用 "wb"),而不是默认的文本模式。在Windows和其他进行换行符转换的系统上,stdio库会将LF(字节0x0A)转换为CRLF对(两个字节0x0D 0x0A)进行写入,并且在读取时进行反向转换。对于像JPEG文件这样的非文本数据,这会破坏数据。 - 每次发送后都不需要发送“握手”字节。TCP/IP已经处理了确认/重发/流量控制等。只要 send()/write() 返回一个正值,那么就表示对端接收到了相应数量的字节。 - send()/write() 可能无法发送您要求的所有数据 - 它们可能会进行部分发送。如果发生这种情况,您需要在循环中继续尝试发送缓冲区的其余部分。 - sizeof(char) 在 C 语言标准中保证是 1,因此很少需要使用 sizeof(char),而不使用它可以使代码更加清晰。 - 在客户端代码中,没有必要使用 ioctl 确定可以在不阻塞的情况下读取多少数据,因为您只是再次循环 - 当没有数据可用时,您的代码将旋转在100%CPU。只需让 read() 调用阻塞即可。如果您在笔记本电脑上运行此代码,你的电池将感谢你。 - 同样,客户端几乎肯定会收到部分读取,您不会在单个调用中接收整个文件。你需要写出任何你得到的数据,然后循环并再次接收。
  • 当你在开始时通过socket发送图像大小时,如果两个系统的字节顺序不同,可能会在客户端上得到不同的值。为了使你的代码更加健壮,你需要在发送数据时将其转换为网络字节顺序(大端序),然后在接收后将其转换为主机本地字节顺序。你可以使用ntohl(3)htonl(3) 函数来对4字节的值进行这些转换。

  • 我认为可以肯定地说,如果你#include <unistd.h>,那么你不必使用"rb"打开文件,使用"r"就可以了。 - Dietrich Epp
    对于代码的客户端部分,您提到它正在获取部分读取,我的代码是否已经处理了这个问题?我已经将其读入尽可能多的字节,然后从读取函数返回的字节数写入文件,并循环直到写入的字节数等于文件大小。 - user1721182
    此图像传输程序仅用于客户端和服务器,两者都将运行Ubuntu,因此在二进制模式下打开文件是否不必要? 注意:翻译为简体中文。 - user1721182
    @Adam,你建议使用C还是C++来编写套接字代码? - Lightsout

    9

    现在工作得很好。

    祝好,

    Mario。

    客户:

    #include<stdio.h>
    #include<string.h>
    #include<sys/socket.h>
    #include<arpa/inet.h>   
    #include<sys/ioctl.h>
    #include<unistd.h>  
    #include<iostream>
    #include<fstream>
    #include<errno.h>
    using namespace std;
    
    //This function is to be used once we have confirmed that an image is to be sent
    //It should read and output an image file
    
    int receive_image(int socket)
    { // Start function 
    
    int buffersize = 0, recv_size = 0,size = 0, read_size, write_size, packet_index =1,stat;
    
    char imagearray[10241],verify = '1';
    FILE *image;
    
    //Find the size of the image
    do{
    stat = read(socket, &size, sizeof(int));
    }while(stat<0);
    
    printf("Packet received.\n");
    printf("Packet size: %i\n",stat);
    printf("Image size: %i\n",size);
    printf(" \n");
    
    char buffer[] = "Got it";
    
    //Send our verification signal
    do{
    stat = write(socket, &buffer, sizeof(int));
    }while(stat<0);
    
    printf("Reply sent\n");
    printf(" \n");
    
    image = fopen("capture2.jpeg", "w");
    
    if( image == NULL) {
    printf("Error has occurred. Image file could not be opened\n");
    return -1; }
    
    //Loop while we have not received the entire file yet
    
    
    int need_exit = 0;
    struct timeval timeout = {10,0};
    
    fd_set fds;
    int buffer_fd, buffer_out;
    
    while(recv_size < size) {
    //while(packet_index < 2){
    
        FD_ZERO(&fds);
        FD_SET(socket,&fds);
    
        buffer_fd = select(FD_SETSIZE,&fds,NULL,NULL,&timeout);
    
        if (buffer_fd < 0)
           printf("error: bad file descriptor set.\n");
    
        if (buffer_fd == 0)
           printf("error: buffer read timeout expired.\n");
    
        if (buffer_fd > 0)
        {
            do{
                   read_size = read(socket,imagearray, 10241);
                }while(read_size <0);
    
                printf("Packet number received: %i\n",packet_index);
            printf("Packet size: %i\n",read_size);
    
    
            //Write the currently read data into our image file
             write_size = fwrite(imagearray,1,read_size, image);
             printf("Written image size: %i\n",write_size); 
    
                 if(read_size !=write_size) {
                     printf("error in read write\n");    }
    
    
                 //Increment the total number of bytes read
                 recv_size += read_size;
                 packet_index++;
                 printf("Total received image size: %i\n",recv_size);
                 printf(" \n");
                 printf(" \n");
        }
    
    }
    
    
      fclose(image);
      printf("Image successfully Received!\n");
      return 1;
      }
    
      int main(int argc , char *argv[])
      {
    
      int socket_desc;
      struct sockaddr_in server;
      char *parray;
    
    
      //Create socket
      socket_desc = socket(AF_INET , SOCK_STREAM , 0);
    
      if (socket_desc == -1) {
      printf("Could not create socket");
      }
    
      memset(&server,0,sizeof(server));
      server.sin_addr.s_addr = inet_addr("10.0.0.30");
      server.sin_family = AF_INET;
      server.sin_port = htons( 8889 );
    
      //Connect to remote server
      if (connect(socket_desc , (struct sockaddr *)&server , sizeof(server)) < 0) {
      cout<<strerror(errno);
      close(socket_desc);
      puts("Connect Error");
      return 1;
      }
    
      puts("Connected\n");
    
      receive_image(socket_desc);
    
      close(socket_desc);
    
      return 0;
      }
    

    服务器:

       #include<stdio.h>
       #include<string.h>
       #include<sys/socket.h>
       #include<arpa/inet.h>   
       #include<unistd.h>  
       #include<iostream>
       #include<fstream>
       #include<errno.h>
    
       using namespace std;
    
       int send_image(int socket){
    
       FILE *picture;
       int size, read_size, stat, packet_index;
       char send_buffer[10240], read_buffer[256];
       packet_index = 1;
    
       picture = fopen("capture.jpeg", "r");
       printf("Getting Picture Size\n");   
    
       if(picture == NULL) {
            printf("Error Opening Image File"); } 
    
       fseek(picture, 0, SEEK_END);
       size = ftell(picture);
       fseek(picture, 0, SEEK_SET);
       printf("Total Picture size: %i\n",size);
    
       //Send Picture Size
       printf("Sending Picture Size\n");
       write(socket, (void *)&size, sizeof(int));
    
       //Send Picture as Byte Array
       printf("Sending Picture as Byte Array\n");
    
       do { //Read while we get errors that are due to signals.
          stat=read(socket, &read_buffer , 255);
          printf("Bytes read: %i\n",stat);
       } while (stat < 0);
    
       printf("Received data in socket\n");
       printf("Socket data: %c\n", read_buffer);
    
       while(!feof(picture)) {
       //while(packet_index = 1){
          //Read from the file into our send buffer
          read_size = fread(send_buffer, 1, sizeof(send_buffer)-1, picture);
    
          //Send data through our socket 
          do{
            stat = write(socket, send_buffer, read_size);  
          }while (stat < 0);
    
          printf("Packet Number: %i\n",packet_index);
          printf("Packet Size Sent: %i\n",read_size);     
          printf(" \n");
          printf(" \n");
    
    
          packet_index++;  
    
          //Zero out our send buffer
          bzero(send_buffer, sizeof(send_buffer));
         }
        }
    
        int main(int argc , char *argv[])
        {
          int socket_desc , new_socket , c, read_size,buffer = 0;
          struct sockaddr_in server , client;
          char *readin;
    
          //Create socket
          socket_desc = socket(AF_INET , SOCK_STREAM , 0);
          if (socket_desc == -1)
          {
             printf("Could not create socket");
          }
    
          //Prepare the sockaddr_in structure
          server.sin_family = AF_INET;
          server.sin_addr.s_addr = INADDR_ANY;
          server.sin_port = htons( 8889 );
    
          //Bind
         if( bind(socket_desc,(struct sockaddr *)&server , sizeof(server)) < 0)
         {
           puts("bind failed");
           return 1;
         }
    
         puts("bind done");
    
         //Listen
         listen(socket_desc , 3);
    
          //Accept and incoming connection
          puts("Waiting for incoming connections...");
          c = sizeof(struct sockaddr_in);
    
         if((new_socket = accept(socket_desc, (struct sockaddr *)&client,(socklen_t*)&c))){
    puts("Connection accepted");
             }
    
        fflush(stdout);
    
        if (new_socket<0)
        {
          perror("Accept Failed");
          return 1;
        }
    
        send_image(new_socket);
    
        close(socket_desc);
        fflush(stdout);
        return 0;
        }
    

    0
    我修改了@mmirand6的答案,使得服务器在客户端发送图像时接收它们(即上面示例的反向)。此外,此示例使用本地主机。另一个区别是服务器永远不会退出并继续等待新连接。

    server.cpp

    #include<stdio.h>
    #include<string.h>
    #include<sys/socket.h>
    #include<arpa/inet.h>
    #include<unistd.h>
    #include<iostream>
    #include<fstream>
    #include<errno.h>
    
    using namespace std;
    
    int receive_image(int socket)
    { // Start function
    
    int buffersize = 0, recv_size = 0,size = 0, read_size, write_size, packet_index =1,stat;
    
    char imagearray[10241],verify = '1';
    FILE *image;
    
    //Find the size of the image
    do{
    stat = read(socket, &size, sizeof(int));
    }while(stat<0);
    
    /*printf("Packet received.\n");
    printf("Packet size: %i\n",stat);
    printf("Image size: %i\n",size);
    printf(" \n");*/
    
    char buffer[] = "Got it";
    
    //Send our verification signal
    do{
    stat = write(socket, &buffer, sizeof(int));
    }while(stat<0);
    
    printf("Reply sent\n");
    printf(" \n");
    
    image = fopen("res.ppm", "w");
    
    if( image == NULL) {
    printf("Error has occurred. Image file could not be opened\n");
    return -1; }
    
    //Loop while we have not received the entire file yet
    
    
    int need_exit = 0;
    struct timeval timeout = {10,0};
    
    fd_set fds;
    int buffer_fd, buffer_out;
    
    while(recv_size < size) {
    //while(packet_index < 2){
    
        FD_ZERO(&fds);
        FD_SET(socket,&fds);
    
        buffer_fd = select(FD_SETSIZE,&fds,NULL,NULL,&timeout);
    
        if (buffer_fd < 0)
           printf("error: bad file descriptor set.\n");
    
        if (buffer_fd == 0)
           printf("error: buffer read timeout expired.\n");
    
        if (buffer_fd > 0)
        {
            do{
                   read_size = read(socket,imagearray, 10241);
                }while(read_size <0);
    
                /*printf("Packet number received: %i\n",packet_index);
                printf("Packet size: %i\n",read_size);*/
    
    
            //Write the currently read data into our image file
             write_size = fwrite(imagearray,1,read_size, image);
             //printf("Written image size: %i\n",write_size);
    
                 if(read_size !=write_size) {
                     printf("error in read write\n");    }
    
    
                 //Increment the total number of bytes read
                 recv_size += read_size;
                 packet_index++;
                 /*printf("Total received image size: %i\n",recv_size);
                 printf(" \n");
                 printf(" \n");*/
        }
    
    }
    
    
      fclose(image);
      printf("Image successfully Received!\n");
      return 1;
      }
    
    
    int main(int argc , char *argv[])
    {
        int socket_desc , new_socket , c, read_size,buffer = 0;
        struct sockaddr_in server , client;
        char *readin;
    
        //Create socket
        socket_desc = socket(AF_INET , SOCK_STREAM , 0);
        if (socket_desc == -1)
        {
          printf("Could not create socket");
        }
    
        //Prepare the sockaddr_in structure
        server.sin_family = AF_INET;
        server.sin_addr.s_addr = INADDR_ANY;
        server.sin_port = htons( 8889 );
    
        //Bind
        if( bind(socket_desc,(struct sockaddr *)&server , sizeof(server)) < 0)
        {
          puts("bind failed");
          return 1;
        }
    
        puts("bind done");
        //Listen
        listen(socket_desc , 3); // int listen(int s, int backlog); | `backlog` (3)
        //limits the number of outstanding connections in the socket's listen
        //queue to the value specified by the backlog argument.
    
        while(true)
        {
          //Accept and incoming connection
          puts("Waiting for incoming connections...");
          c = sizeof(struct sockaddr_in);
          // waits for a connection. returns -1 on failure, a positive val on success
          if((new_socket = accept(socket_desc, (struct sockaddr *)&client,(socklen_t*)&c)))
          {
              puts("Connection accepted");
              cout << "new_socket = " << new_socket << "\n";
          }
          fflush(stdout);
          if (new_socket<0)
          {
              perror("Accept Failed, trying again");
              continue;
          }
    
          // receive image
          receive_image(new_socket);
        }
    
        close(socket_desc);
        fflush(stdout);
        return 0;
    }
    

    client.cpp

    #include<stdio.h>
    #include<string.h>
    #include<sys/socket.h>
    #include<arpa/inet.h>
    #include<sys/ioctl.h>
    #include<unistd.h>
    #include<iostream>
    #include<fstream>
    #include<errno.h>
    using namespace std;
    
    //This function is to be used once we have confirmed that an image is to be sent
    //It should read and output an image file
    
    
    int send_image(int socket)
    {
        FILE *picture;
        int size, read_size, stat, packet_index;
        char send_buffer[10240], read_buffer[256];
        packet_index = 1;
    
        picture = fopen("test.ppm", "r");
        printf("Getting Picture Size\n");
    
        if(picture == NULL) {
             printf("Error Opening Image File"); }
    
        fseek(picture, 0, SEEK_END);
        size = ftell(picture);
        fseek(picture, 0, SEEK_SET);
        printf("Total Picture size: %i\n",size);
    
        //Send Picture Size
        printf("Sending Picture Size\n");
        write(socket, (void *)&size, sizeof(int));
    
        //Send Picture as Byte Array
        printf("Sending Picture as Byte Array\n");
    
        do { //Read while we get errors that are due to signals.
           stat=read(socket, &read_buffer , 255);
           printf("Bytes read: %i\n",stat);
        } while (stat < 0);
    
        printf("Received data in socket\n");
        printf("Socket data: %c\n", read_buffer);
    
        while(!feof(picture)) {
        //while(packet_index = 1){
           //Read from the file into our send buffer
           read_size = fread(send_buffer, 1, sizeof(send_buffer)-1, picture);
    
           //Send data through our socket
           do{
             stat = write(socket, send_buffer, read_size);
           }while (stat < 0);
    
           printf("Packet Number: %i\n",packet_index);
           printf("Packet Size Sent: %i\n",read_size);
           printf(" \n");
           printf(" \n");
    
    
           packet_index++;
    
           //Zero out our send buffer
           bzero(send_buffer, sizeof(send_buffer));
          }
    }
    
    
    
    int main(int argc , char *argv[])
    {
    
        int socket_desc;
        struct sockaddr_in server;
        char *parray;
    
    
        //Create socket
        socket_desc = socket(AF_INET , SOCK_STREAM , 0);
    
        if (socket_desc == -1) {
            printf("Could not create socket");
        }
    
        memset(&server,0,sizeof(server));
        server.sin_addr.s_addr = inet_addr("127.0.0.1");
        server.sin_family = AF_INET;
        server.sin_port = htons( 8889 );
    
        //Connect to remote server
        if (connect(socket_desc , (struct sockaddr *)&server , sizeof(server)) < 0) {
            cout<<strerror(errno);
            close(socket_desc);
            puts("Connect Error");
            return 1;
        }
    
        puts("Connected\n");
    
        send_image(socket_desc);
    
        //close(socket_desc);
    
        return 0;
    }
    

    使用以下命令编译:$gcc client.cpp -lstdc++ -o client


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接