ラズベリーパイでechoサーバーを作ろう の続きです。

引き続きネットワークプログラミングです。ファイルを転送するプログラムです。

普通はftpとかfilezillaを使ってファイルを転送するわけですが、シンプルなものであればプログラミングで作れます。

前回は文字列を送受信するプログラムを作りました。

文字列をファイルに置き換えるとできます。

結局やっていることはソケットを使ってread, writeしているだけです。

ネットワークにつないでしまえば、後はどうにかなります。

サーバー側です。cpserver.c

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <sys/stat.h>


#define PORT 9877

int main(){
  
  int i = 0;
  int srcSocket;
  int dstSocket;
  
  struct sockaddr_in srcAddr;
  struct sockaddr_in dstAddr;
  int dstAddrSize = sizeof(dstAddr);
  // 各種パラメータ
  int status;
  int numrcv;
  char buf[1024];
  int len = 0;
  int size = 0;
  char filename[64];
  int fp;
  unsigned long filesize = 0;
  unsigned long totalrcv = 0;

  // sockaddr_in 構造体のセット
  bzero((char *)&srcAddr, sizeof(srcAddr));
  srcAddr.sin_port = htons(PORT);
  srcAddr.sin_family = AF_INET;
  srcAddr.sin_addr.s_addr = INADDR_ANY;
    
  srcSocket = socket(AF_INET, SOCK_STREAM, 0);
  bind(srcSocket, (struct sockaddr *)&srcAddr, sizeof(srcAddr));
  listen(srcSocket, 1);
  // 接続の受付け
  printf("接続を待っています\nクライアントプログラムを動かして下さい\n");
  dstSocket = accept(srcSocket, (struct sockaddr *)&dstAddr, &dstAddrSize);
  printf("%s から接続を受けました\n",inet_ntoa(dstAddr.sin_addr));

  while(1) {
memset(filename, 0, sizeof(filename));
len = read(dstSocket, buf, 2);
if(len < 0) {
 printf("エラー: ファイル名のエラーです。\n");
 return -1;
}

size = buf[0] << 8;      
    size |= buf[1];
len = read(dstSocket, buf, 4);
if(len < 0) {
 printf("エラー: ファイルサイズのエラーです。\n");
 return -1;
}
filesize = buf[0] << 24;
filesize |= buf[1] << 16;
filesize |= buf[2] << 8;
filesize |= buf[3];
printf("filesize = %ld byte\n", filesize);

len = read(dstSocket, filename, size);
if (len < 0) {
 printf("エラー: ファイル名を取得できませんでした。");
 return -1;
} else if (!strcmp(filename, "quit")) {
 printf("プログラムを終了します。\n\n");
 return 0;
}
printf("filename = %s\n", filename);

/* 書き込みファイルを開く */
if ((fp = open (filename, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) {
 printf("エラー ファイルを作成できません");
 return -1;
}

totalrcv = 0;
while(1){
 //パケットの受信
 numrcv = read(dstSocket, buf, 1024);
 //printf("%d %d\n", i, numrcv);
 if(numrcv <= 0 ){
break;
 }

 write(fp, buf, numrcv);
 totalrcv += numrcv;
 if(totalrcv >= filesize) {
printf("%s 転送完了\n\n", filename);
break;
 }
}
close(fp);
  }

  close(srcSocket);
  return(0);
}


クライアント側です。 cpclient.c
 
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/stat.h>
#include <fcntl.h>

#define PORT 9877 //サーバープログラムとポート番号を合わせてください
char buf[1024];
char filesize[4];

int main(){
  char destination[32] = "192.168.0.10";//サーバーのIPアドレスを入れて下さい。
  //ソケット,sockaddr_in 構造体
  int dstSocket;
  struct sockaddr_in dstAddr;
  struct hostent *hp;
  int    numrcv;
  char filename[100];
  int len = 0;
  int fp;
  int size;
  struct stat statBuf;

  //sockaddr_in 構造体のセット
  bzero((char *)&dstAddr, sizeof(dstAddr));
  dstAddr.sin_family = AF_INET;
  dstAddr.sin_port = htons(PORT);
  
  hp = gethostbyname(destination);
  bcopy(hp->h_addr, &dstAddr.sin_addr, hp->h_length);

  //ソケットの生成
  dstSocket = socket(AF_INET, SOCK_STREAM, 0);
  
  //接続
  if (connect(dstSocket, (struct sockaddr *)&dstAddr, sizeof(dstAddr)) < 0){
    printf("%s に接続できませんでした\n",destination);
    return(-1);
  }
  printf("%s に接続しました\n",destination);
  
  while(1) {
    len = 0;
    memset(filename, 0, sizeof(filename));

    printf("送信するファイル名を入力してください\n");
    printf("quitで終了します\n\n");
    scanf("%s",filename);

    if (!strcmp(filename, "quit")) {
      printf("->bye\n");
      write(dstSocket, "00quit", 6);
      close(dstSocket);
      break;
    }
    while(filename[len] != '\0') {
      len++;
    }
    len++;
    buf[0] = (len >> 8) & 0xFF;
    buf[1] = len & 0xFF;
  
    fp = open(filename, O_RDONLY);
    if (fp < 0) {
      printf("エラー: ファイルを開けません\n");
      return(-1);
    }
    
    
    fstat(fp, &statBuf);
    printf("filesize = %ld byte\n", statBuf.st_size);
    filesize[0] = (statBuf.st_size >> 24 ) & 0xFF;
    filesize[1] = (statBuf.st_size >> 16 ) & 0xFF;
    filesize[2] = (statBuf.st_size >> 8) & 0xFF;
    filesize[3] = statBuf.st_size & 0xFF;
    

    write(dstSocket, buf, 2);
    write(dstSocket, filesize, 4);
    write(dstSocket,filename, len);
    while (1){
      /* ファイルから読み込み */
      size = read (fp, buf, sizeof(buf));
      /* データが無ければループを抜ける */
      if (size <= 0) {
break;
      }
      //パケットの送信
      write(dstSocket, buf, size);
            
    }
    write(dstSocket,"filetransferred", sizeof("filetransferred"));
    printf("%s 転送完了しました\n\n",filename);
    close(fp);
  }
  close(dstSocket);
  return(0);
}

cpclientと同じディレクトリにファイルを置いておきます。
あとは実行してファイル名を指定するとをサーバー側にコピーすることができます。
 
実行例
./cpclient

送信するファイル名を入力してください
quitで終了します

crazy.mp3
filesize = 6211191 byte
crazy.mp3 転送完了しました