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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15445207/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 19:22:07  来源:igfitidea点击:

Sending Image (JPEG) through Socket in C Linux

c++imagefilesocketsnetworking

提问by user1721182

I'm writing a small C program in order to be able to transfer an image file between two computers (from server to client both running linux) using TCP/IP sockets but there seems to be an error as my picture appears on the other sides corrupted.

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

The code for my server is as such:

我的服务器的代码是这样的:

#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;
}

The client side code which is receiving the data is as such:

接收数据的客户端代码如下:

 #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;
}

Can anyone give me a hand with this? I can't see to figure out this error for the life of me.

任何人都可以帮我解决这个问题吗?我无法在我的生活中找出这个错误。

EDIT: I've changed the fwrites and freads back into regular write and read and it still sends a corrupted image

编辑:我已将 fwrites 和 freads 更改回常规写入和读取,但它仍然发送损坏的图像

回答by Adam Rosenfield

You have a number of problems:

你有很多问题:

  • You need to open the file in binary mode ("rb"for reading, "wb"for writing), not the default text mode. On Windows (and any other systems which do line ending translation), the stdio library converts LFs (bytes 0x0A) into CRLF pairs (the two bytes 0x0D 0x0A) when writing, and it does the inverse translation when reading. For non-text data like JPEG files, this corrupts the data.
  • There's no need to be sending your "handshake" bytes after each send. TCP/IP already handles acknowledgements/resends/flow control/etc. You can assume that as long as send()/write()returns a positive value, then that many bytes were received by the other peer.
  • send()/write()may not send all of the data you ask it to—they may do a partial send. If that happens, you need to keep trying to send the rest of the buffer in a loop.
  • sizeof(char)is guaranteed to be 1 by the C language standard, there's rarely a need to say sizeof(char)when instead your code will be much clearer without it
  • In the client code, there's no need to use that ioctlto determine how much data can be read without blocking because you're just looping again—your code will spin at 100% CPU while there's no data available. Just let the read()call block. If you're running this code on a laptop, your battery will thank you.
  • Likewise, the client will almost definitely be getting partial reads, you're not going to receive the whole file in a single call. You need to write out whatever data you get, then loop and receive again.
  • When you send the image size over the socket at the start, you might get a different value on the client if the two systems are not of the same endianness. In order to make your code bulletproof, you need to convert the data to network order (big-endian) when sending it, then convert it back to host (native) order after receiving it. You can use the ntohl(3)and htonl(3)functions to do these conversions for 4-byte values.
  • 您需要以二进制模式("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()呼叫阻塞。如果您在笔记本电脑上运行此代码,您的电池会感谢您的。
  • 同样,客户端几乎肯定会获得部分读取,您不会在一次调用中收到整个文件。你需要写出你得到的任何数据,然后循环并再次接收。
  • 当您在开始时通过套接字发送图像大小时,如果两个系统的字节顺序不同,您可能会在客户端上获得不同的值。为了使您的代码防弹,您需要在发送时将数据转换为网络顺序(big-endian),然后在收到后将其转换回主机(本机)顺序。您可以使用ntohl(3)htonl(3)函数对 4 字节值进行这些转换。

回答by mmirand6

Works nicely now.

现在很好用。

Best,

最好的事物,

Mario.

马里奥。

Client:

客户:

#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;
  }

Server:

服务器:

   #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;
    }