【C言語】UDPソケットを使ったファイル転送プログラムの実装方法
C言語によるソケットプログラミングを利用すると、2台のコンピュータ間でデータをやり取りできるようになります。中でもユーザーデータグラムプロトコル(UDP)とシンプルなクライアント/サーバー構成を組み合わせれば、ファイルの送受信も比較的簡単に実現できます。
本記事で紹介するプログラムの基本的な仕様は以下のとおりです。
- セキュリティ:暗号化によってデータを保護します。
- プロトコル:UDP(User Datagram Protocol)
- 暗号化方式:XOR暗号
アルゴリズムの流れ
- サーバーを起動し、クライアントからのファイル名を待ち受けます。
- クライアントが取得したいファイル名を送信します。
- サーバーはファイル名を受信します。ファイルが存在する場合、ファイルの読み込みを開始し、ファイル終端(EOF)に達するまで、XOR暗号化したファイル内容を格納したバッファを送信し続けます。
- ファイルの終端はEOFでマークされます。
- クライアント側では、EOFを受信するまでバッファ単位でデータを受け取り、復号しながら出力します。
- ファイルが存在しない場合は、「File Not Found!」というメッセージが送信されます。
サーバーの実装
以下はUDPソケットを使用するサーバー側のコードです。指定ポート(15050番)でバインドし、クライアントからの要求を待ち受けます。
// UDPソケットプログラミング用サーバーコード
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#define IP_Protocol 0
#define Port_No 15050
#define Net_Buf_Size 32
#define CipherKey 'S'
#define SendRecvFlag 0
#define NoFile "File Not Found!"
// バッファをクリアする関数
void clearBuf(char* b1) {
int i;
for (i = 0; i < Net_Buf_Size; i++)
b1[i] = '\0';
}
// 暗号化を行う関数
char Cipher(char ch1) {
return ch1 ^ CipherKey;
}
// ファイルを送信する関数
int sendFile(FILE* fp1, char* buf1, int s1) {
int i, len;
if (fp1 == NULL) {
strcpy(buf1, NoFile);
len = strlen(NoFile);
buf1[len] = EOF;
for (i = 0; i <= len; i++)
buf1[i] = Cipher(buf1[i]);
return 1;
}
char ch1, ch2;
for (i = 0; i < s1; i++) {
ch1 = fgetc(fp1);
ch2 = Cipher(ch1);
buf1[i] = ch2;
if (ch1 == EOF)
return 1;
}
return 0;
}
// ドライバーコード
int main() {
int sockfd1, nBytes;
struct sockaddr_in addr_con;
int addrlen = sizeof(addr_con);
addr_con.sin_family = AF_INET;
addr_con.sin_port = htons(Port_No);
addr_con.sin_addr.s_addr = INADDR_ANY;
char net_buf1[Net_Buf_Size];
FILE* fp1;
// socket()
sockfd1 = socket(AF_INET, SOCK_DGRAM, IP_Protocol);
if (sockfd1 < 0)
printf("\nfile descriptor is not received!!\n");
else
printf("\nfile descriptor %d is received\n", sockfd1);
// bind()
if (bind(sockfd1, (struct sockaddr*)&addr_con,
sizeof(addr_con)) == 0)
printf("\nSuccessfully is binded!\n");
else
printf("\nBinding is Failed!\n");
while (1) {
printf("\nWaiting for name of file...\n");
// ファイル名を受信
clearBuf(net_buf1);
nBytes = recvfrom(sockfd1, net_buf1,
Net_Buf_Size, SendRecvFlag,
(struct sockaddr*)&addr_con,
&addrlen);
fp1 = fopen(net_buf1, "r");
printf("\nFile Name is Received: %s\n", net_buf1);
if (fp1 == NULL)
printf("\nFile open is failed!\n");
else
printf("\nFile Successfully is opened!\n");
while (1) {
// 処理
if (sendFile(fp1, net_buf1, Net_Buf_Size)) {
sendto(sockfd1, net_buf1, Net_Buf_Size,
SendRecvFlag,
(struct sockaddr*)&addr_con,
addrlen);
break;
}
// 送信
sendto(sockfd1, net_buf1, Net_Buf_Size,
SendRecvFlag,
(struct sockaddr*)&addr_con, addrlen);
clearBuf(net_buf1);
}
if (fp1 != NULL)
fclose(fp1);
}
return 0;
}
クライアントの実装
次にクライアント側のコードです。ローカルホスト(127.0.0.1)のサーバーに対してファイル名を送信し、返却されたデータを復号して表示します。
// UDPソケットプログラミング用クライアントコード
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#define IP_Protocol 0
#define IP_Address "127.0.0.1" // ローカルホスト
#define Port_No 15050
#define Net_Buf_Size 32
#define CipherKey 'S'
#define SendRecvFlag 0
// バッファをクリアする関数
void clearBuf(char* b1) {
int i;
for (i = 0; i < Net_Buf_Size; i++)
b1[i] = '\0';
}
// 復号化を行う関数
char Cipher(char ch1) {
return ch1 ^ CipherKey;
}
// ファイルを受信する関数
int recvFile(char* buf1, int s1) {
int i;
char ch1;
for (i = 0; i < s1; i++) {
ch1 = buf1[i];
ch1 = Cipher(ch1);
if (ch1 == EOF)
return 1;
else
printf("%c", ch1);
}
return 0;
}
// ドライバーコード
int main() {
int sockfd1, nBytes;
struct sockaddr_in addr_con;
int addrlen = sizeof(addr_con);
addr_con.sin_family = AF_INET;
addr_con.sin_port = htons(Port_No);
addr_con.sin_addr.s_addr = inet_addr(IP_Address);
char net_buf1[Net_Buf_Size];
FILE* fp1;
// socket()
sockfd1 = socket(AF_INET, SOCK_DGRAM, IP_Protocol);
if (sockfd1 < 0)
printf("\nfile descriptor is not received!!\n");
else
printf("\nfile descriptor %d is received\n", sockfd1);
while (1) {
printf("\nPlease enter the name of file to receive:\n");
scanf("%s", net_buf1);
sendto(sockfd1, net_buf1, Net_Buf_Size,
SendRecvFlag, (struct sockaddr*)&addr_con,
addrlen);
printf("\n---------Data is Received---------\n");
while (1) {
// 受信
clearBuf(net_buf1);
nBytes = recvfrom(sockfd1, net_buf1, Net_Buf_Size,
SendRecvFlag,
(struct sockaddr*)&addr_con,
&addrlen);
// 処理
if (recvFile(net_buf1, Net_Buf_Size)) {
break;
}
}
printf("\n-------------------------------\n");
}
return 0;
}
コンパイルと実行手順
それぞれのソースファイルをgccでコンパイルし、サーバーを先に起動してからクライアントを実行します。
# コンパイル
gcc udp_server.c -o udp_server
gcc udp_client.c -o udp_client
# 端末1:サーバーを起動
./udp_server
# 端末2:クライアントを起動してファイル名を入力
./udp_client
実装時の注意点
- UDPの特性:UDPはコネクションレス型で信頼性の保証がないため、パケットロスや到達順序の入れ替わりが発生する可能性があります。確実な転送が必要な場面ではTCPの採用を検討しましょう。
- 暗号強度:XOR暗号は仕組みの理解には最適ですが、実運用での機密保護には不十分です。実務ではAESなどの標準的な暗号方式を使用してください。
- バッファサイズ:このサンプルでは32バイト固定のため、大きなファイルは多数のパケットに分割されて送信されます。
- エラー処理:サンプルを簡潔にするため最小限のエラー処理にとどめています。実用化する際はタイムアウト処理や再送制御の追加を推奨します。
このように、UDPソケットとシンプルなXOR暗号を組み合わせることで、C言語だけで基本的なファイル転送システムを構築できます。まずはローカル環境で動かしながら、ソケットAPIの挙動やUDPの特性を体感してみてください。
-
PCで使えるおすすめファイル転送アプリ6選|スマホ・PC間のデータ移行を簡単に
かつては、パソコンから別のパソコンやスマートフォンへデータを移すのに、面倒な手順を踏む必要がありました。しかし現在では、PCとスマホ、あるいはPC同士の間でデータを手軽に転送できるアプリが数多く登場しています。そこで本記事では、数あるPC用ファイル転送アプリの中から、用途に応じて使い分けられるおすすめの6つのツールを厳選してご紹介します。Part 1:PCとスマホ間のデータ転送に最適なアプリパソコンからスマートフォンへ(またはその逆に)データを転送したいだけであれば、MobileTransやAirDroidといったアプリが便利です。アプリ1:Wondershare MobileTransPC向
-
【保存版】AndroidからPCへファイル転送するならこれ!おすすめアプリ8選
新しいスマホを購入したときや、古いスマホを復元するとき、多くの方はスマホからPCへデータを移行します。また、スマホのストレージがいっぱいになり、処理を中断せずに快適に使うために空き容量が必要な場合も、データをPCへ転送する機会は少なくありません。 こうした場面では、AndroidからPCへファイルを転送できるアプリが欠かせません。しかし、数あるアプリの中から手間のかからない適切なものを見つけるのは意外と難しいものです。そこで本記事では、調査と検証を重ねて厳選した「AndroidからPCへファイル転送できるおすすめアプリ8選」をご紹介します。ぜひ最後までご覧ください。 パート1:Android