using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Sockets;
using FUP; // FUP 네임스페이스를 사용합니다.
namespace FileSender
{
class MainApp
{
const int CHUNK_SIZE = 4096; // 파일 전송 시 청크 크기를 4096 바이트로 설정합니다.
static void Main(string[] args)
{
if (args.Length < 2) // 명령줄 인수가 2개 미만이면 사용법을 출력하고 프로그램을 종료합니다.
{
Console.WriteLine(
"사용법 : {0} <Server IP> <File Path>",
Process.GetCurrentProcess().ProcessName);
return;
}
string serverIp = args[0]; // 첫 번째 명령줄 인수를 서버 IP 주소로 저장합니다.
const int serverPort = 5425; // 서버 포트 번호를 5425로 설정합니다.
string filepath = args[1]; // 두 번째 명령줄 인수를 파일 경로로 저장합니다.
try
{
IPEndPoint clientAddress = new IPEndPoint(0, 0); // 클라이언트 IP 주소와 포트 번호를 0으로 설정하여 자동으로 할당합니다.
// (클라이언트는 OS에서 할당한 IP 주소와 포트에 바인딩합니다.)
IPEndPoint serverAddress = new IPEndPoint(IPAddress.Parse(serverIp), serverPort); // 서버 IP 주소와 포트 번호를 사용하여 IPEndPoint 객체를 생성합니다.
Console.WriteLine("클라이언트: {0}, 서버:{1}", clientAddress.ToString(), serverAddress.ToString()); // 클라이언트와 서버의 정보를 출력합니다.
uint msgId = 0; // 메시지 ID를 0으로 초기화합니다.
Message reqMsg = new Message(); // 파일 전송 요청 메시지를 생성합니다.
reqMsg.Body = new BodyRequest() // 메시지 본문에 파일 크기와 파일 이름을 설정합니다.
{
FILESIZE = new FileInfo(filepath).Length,
FILENAME = System.Text.Encoding.Default.GetBytes(filepath)
};
reqMsg.Header = new Header() // 메시지 헤더를 설정합니다.
{
MSGID = msgId++, // 메시지 ID를 설정하고 1 증가시킵니다.
MSGTYPE = CONSTANTS.REQ_FILE_SEND, // 메시지 유형을 파일 전송 요청으로 설정합니다.
BODYLEN = (uint)reqMsg.Body.GetSize(), // 메시지 본문의 크기를 설정합니다.
FRAGMENTED = CONSTANTS.NOT_FRAGMENTED, // 메시지가 조각화되지 않았음을 나타냅니다.
LASTMSG = CONSTANTS.LASTMSG, // 메시지가 마지막 메시지임을 나타냅니다.
SEQ = 0 // 메시지 순서를 0으로 설정합니다.
};
TcpClient client = new TcpClient(clientAddress); // TcpClient 객체를 생성하고 클라이언트 주소로 초기화합니다.
client.Connect(serverAddress); // 서버에 연결합니다.
NetworkStream stream = client.GetStream(); // 네트워크 스트림을 가져옵니다.
MessageUtil.Send(stream, reqMsg); // 파일 전송 요청 메시지를 서버에 전송합니다.
// (클라이언트는 서버에 접속 하자마자,
// 파일 전송 요청 메시지를 서버에 보냅니다.)
Message rspMsg = MessageUtil.Receive(stream); // 서버의 응답 메시지를 수신합니다.
// (그리고 서버의 응답을 받습니다.)
if (rspMsg.Header.MSGTYPE != CONSTANTS.REP_FILE_SEND) // 서버의 응답 메시지 유형이 파일 전송 응답이 아니면,
// 오류 메시지를 출력하고 프로그램을 종료합니다.
{
Console.WriteLine("정상적인 서버 응답이 아닙니다.{0}",
rspMsg.Header.MSGTYPE);
return;
}
if (((BodyResponse)rspMsg.Body).RESPONSE == CONSTANTS.DENIED) // 서버가 파일 전송을 거부하면,
// 메시지를 출력하고 프로그램을 종료합니다.
{
Console.WriteLine("서버에서 파일 전송을 거부했습니다.");
return;
}
using (Stream fileStream = new FileStream(filepath, FileMode.Open)) // 파일을 열고 FileStream 객체를 생성합니다.
// (서버에서 전송 요청을 수락했다면,
// 파일 스트림을 열어서 서버로 보낼 준비를 합니다.)
{
byte[] rbytes = new byte[CHUNK_SIZE]; // 파일에서 읽어온 데이터를 저장할 바이트 배열을 생성합니다.
long readValue = BitConverter.ToInt64(rbytes, 0); // rbytes 배열을 long 형식으로 변환합니다. (이 부분은 코드에서 사용되지 않습니다.)
int totalRead = 0; // 현재까지 읽은 바이트 수를 저장할 변수를 초기화합니다.
ushort msgSeq = 0; // 메시지 순서를 0으로 초기화합니다.
byte fragmented = (fileStream.Length < CHUNK_SIZE) ? CONSTANTS.NOT_FRAGMENTED : CONSTANTS.FRAGMENTED; // 파일 크기에 따라 메시지 조각화 여부를 결정합니다.
while (totalRead < fileStream.Length) // 파일의 끝에 도달할 때까지 반복합니다.
{
int read = fileStream.Read(rbytes, 0, CHUNK_SIZE); // 파일에서 CHUNK_SIZE 바이트만큼 읽어서 rbytes 배열에 저장합니다.
totalRead += read; // 읽은 바이트 수를 totalRead에 더합니다.
Message fileMsg = new Message(); // 파일 데이터 전송 메시지를 생성합니다.
byte[] sendBytes = new byte[read]; // 읽은 데이터를 저장할 바이트 배열을 생성합니다.
Array.Copy(rbytes, 0, sendBytes, 0, read); // rbytes 배열의 내용을 sendBytes 배열에 복사합니다.
fileMsg.Body = new BodyData(sendBytes); // 메시지 본문에 파일 데이터를 설정합니다.
fileMsg.Header = new Header() // 메시지 헤더를 설정합니다.
{
MSGID = msgId, // 메시지 ID를 설정합니다.
MSGTYPE = CONSTANTS.FILE_SEND_DATA, // 메시지 유형을 파일 데이터 전송으로 설정합니다.
BODYLEN = (uint)fileMsg.Body.GetSize(), // 메시지 본문의 크기를 설정합니다.
FRAGMENTED = fragmented, // 메시지 조각화 여부를 설정합니다.
LASTMSG = (totalRead < fileStream.Length) ? CONSTANTS.NOT_LASTMSG : CONSTANTS.LASTMSG, // 메시지가 마지막 메시지인지 여부를 설정합니다.
SEQ = msgSeq++ // 메시지 순서를 설정하고 1 증가시킵니다.
};
Console.Write("#"); // "#" 문자를 출력하여 전송 진행 상황을 표시합니다.
MessageUtil.Send(stream, fileMsg); // 파일 데이터 전송 메시지를 서버에 전송합니다.
// (모든 파일의 내용이 전송될 때까지,
// 파일 스트림을 0x03 메시지에 담아서 서버로 보냅니다.)
}
Console.WriteLine(); // 줄 바꿈을 합니다.
Message rstMsg = MessageUtil.Receive(stream); // 서버의 결과 메시지를 수신합니다.
// (서버에서 파일을 제대로 받았는지에 대한
// 응답을 받습니다.)
BodyResult result = ((BodyResult)rstMsg.Body); // 결과 메시지에서 BodyResult 객체를 가져옵니다.
Console.WriteLine("파일 전송 성공 : {0}", result.RESULT == CONSTANTS.SUCCESS); // 파일 전송 성공 여부를 출력합니다.
} // using 블록 종료 시 fileStream.Close() 자동 호출
stream.Close(); // 스트림을 닫습니다.
client.Close(); // 클라이언트를 닫습니다.
}
catch (SocketException e) // 소켓 예외 발생 시 처리
{
Console.WriteLine(e); // 예외 정보를 출력합니다.
}
Console.WriteLine("클라이언트를 종료합니다."); // 클라이언트 종료 메시지를 출력합니다.
}
}
}
using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Sockets;
using FUP; // FUP 네임스페이스를 사용합니다.
namespace FileSender
{
class MainApp
{
const int CHUNK_SIZE = 4096; // 파일 전송 시 청크 크기를 4096 바이트로 설정합니다.
static void Main(string[] args)
{
if (args.Length < 2) // 명령줄 인수가 2개 미만이면 사용법을 출력하고 프로그램을 종료합니다.
{
Console.WriteLine(
"사용법 : {0} <Server IP> <File Path>",
Process.GetCurrentProcess().ProcessName);
return;
}
string serverIp = args[0]; // 첫 번째 명령줄 인수를 서버 IP 주소로 저장합니다.
const int serverPort = 5425; // 서버 포트 번호를 5425로 설정합니다.
string filepath = args[1]; // 두 번째 명령줄 인수를 파일 경로로 저장합니다.
try
{
IPEndPoint clientAddress = new IPEndPoint(0, 0); // 클라이언트 IP 주소와 포트 번호를 0으로 설정하여 자동으로 할당합니다.
IPEndPoint serverAddress = new IPEndPoint(IPAddress.Parse(serverIp), serverPort); // 서버 IP 주소와 포트 번호를 사용하여 IPEndPoint 객체를 생성합니다.
Console.WriteLine("클라이언트: {0}, 서버:{1}", clientAddress.ToString(), serverAddress.ToString()); // 클라이언트와 서버의 정보를 출력합니다.
uint msgId = 0; // 메시지 ID를 0으로 초기화합니다.
Message reqMsg = new Message(); // 파일 전송 요청 메시지를 생성합니다.
reqMsg.Body = new BodyRequest() // 메시지 본문에 파일 크기와 파일 이름을 설정합니다.
{
FILESIZE = new FileInfo(filepath).Length,
FILENAME = System.Text.Encoding.Default.GetBytes(filepath)
};
reqMsg.Header = new Header() // 메시지 헤더를 설정합니다.
{
MSGID = msgId++, // 메시지 ID를 설정하고 1 증가시킵니다.
MSGTYPE = CONSTANTS.REQ_FILE_SEND, // 메시지 유형을 파일 전송 요청으로 설정합니다.
BODYLEN = (uint)reqMsg.Body.GetSize(), // 메시지 본문의 크기를 설정합니다.
FRAGMENTED = CONSTANTS.NOT_FRAGMENTED, // 메시지가 조각화되지 않았음을 나타냅니다.
LASTMSG = CONSTANTS.LASTMSG, // 메시지가 마지막 메시지임을 나타냅니다.
SEQ = 0 // 메시지 순서를 0으로 설정합니다.
};
TcpClient client = new TcpClient(clientAddress); // TcpClient 객체를 생성하고 클라이언트 주소로 초기화합니다.
client.Connect(serverAddress); // 서버에 연결합니다.
NetworkStream stream = client.GetStream(); // 네트워크 스트림을 가져옵니다.
MessageUtil.Send(stream, reqMsg); // 파일 전송 요청 메시지를 서버에 전송합니다.
Message rspMsg = MessageUtil.Receive(stream); // 서버의 응답 메시지를 수신합니다.
if (rspMsg.Header.MSGTYPE != CONSTANTS.REP_FILE_SEND) // 서버의 응답 메시지 유형이 파일 전송 응답이 아니면 오류 메시지를 출력하고 프로그램을 종료합니다.
{
Console.WriteLine("정상적인 서버 응답이 아닙니다.{0}",
rspMsg.Header.MSGTYPE);
return;
}
if (((BodyResponse)rspMsg.Body).RESPONSE == CONSTANTS.DENIED) // 서버가 파일 전송을 거부하면 메시지를 출력하고 프로그램을 종료합니다.
{
Console.WriteLine("서버에서 파일 전송을 거부했습니다.");
return;
}
using (Stream fileStream = new FileStream(filepath, FileMode.Open)) // 파일을 열고 FileStream 객체를 생성합니다.
{
byte[] rbytes = new byte[CHUNK_SIZE]; // 파일에서 읽어온 데이터를 저장할 바이트 배열을 생성합니다.
long readValue = BitConverter.ToInt64(rbytes, 0); // rbytes 배열을 long 형식으로 변환합니다. (이 부분은 코드에서 사용되지 않습니다.)
int totalRead = 0; // 현재까지 읽은 바이트 수를 저장할 변수를 초기화합니다.
ushort msgSeq = 0; // 메시지 순서를 0으로 초기화합니다.
byte fragmented = (fileStream.Length < CHUNK_SIZE) ? CONSTANTS.NOT_FRAGMENTED : CONSTANTS.FRAGMENTED; // 파일 크기에 따라 메시지 조각화 여부를 결정합니다.
while (totalRead < fileStream.Length) // 파일의 끝에 도달할 때까지 반복합니다.
{
int read = fileStream.Read(rbytes, 0, CHUNK_SIZE); // 파일에서 CHUNK_SIZE 바이트만큼 읽어서 rbytes 배열에 저장합니다.
totalRead += read; // 읽은 바이트 수를 totalRead에 더합니다.
Message fileMsg = new Message(); // 파일 데이터 전송 메시지를 생성합니다.
byte[] sendBytes = new byte[read]; // 읽은 데이터를 저장할 바이트 배열을 생성합니다.
Array.Copy(rbytes, 0, sendBytes, 0, read); // rbytes 배열의 내용을 sendBytes 배열에 복사합니다.
fileMsg.Body = new BodyData(sendBytes); // 메시지 본문에 파일 데이터를 설정합니다.
fileMsg.Header = new Header() // 메시지 헤더를 설정합니다.
{
MSGID = msgId, // 메시지 ID를 설정합니다.
MSGTYPE = CONSTANTS.FILE_SEND_DATA, // 메시지 유형을 파일 데이터 전송으로 설정합니다.
BODYLEN = (uint)fileMsg.Body.GetSize(), // 메시지 본문의 크기를 설정합니다.
FRAGMENTED = fragmented, // 메시지 조각화 여부를 설정합니다.
LASTMSG = (totalRead < fileStream.Length) ? CONSTANTS.NOT_LASTMSG : CONSTANTS.LASTMSG, // 메시지가 마지막 메시지인지 여부를 설정합니다.
SEQ = msgSeq++ // 메시지 순서를 설정하고 1 증가시킵니다.
};
Console.Write("#"); // "#" 문자를 출력하여 전송 진행 상황을 표시합니다.
MessageUtil.Send(stream, fileMsg); // 파일 데이터 전송 메시지를 서버에 전송합니다.
}
Console.WriteLine(); // 줄 바꿈을 합니다.
Message rstMsg = MessageUtil.Receive(stream); // 서버의 결과 메시지를 수신합니다.
BodyResult result = ((BodyResult)rstMsg.Body); // 결과 메시지에서 BodyResult 객체를 가져옵니다.
Console.WriteLine("파일 전송 성공 : {0}", result.RESULT == CONSTANTS.SUCCESS); // 파일 전송 성공 여부를 출력합니다.
} // using 블록 종료 시 fileStream.Close() 자동 호출
stream.Close(); // 스트림을 닫습니다.
client.Close(); // 클라이언트를 닫습니다.
}
catch (SocketException e) // 소켓 예외 발생 시 처리
{
Console.WriteLine(e); // 예외 정보를 출력합니다.
}
Console.WriteLine("클라이언트를 종료합니다."); // 클라이언트 종료 메시지를 출력합니다.
}
}
}
코드 설명
이 C# 코드는 TCP 소켓을 사용하여 파일을 전송하는 클라이언트 애플리케이션입니다.
CHUNK_SIZE: 파일 전송 시 데이터를 나누는 단위인 청크 크기를 4096 바이트로 설정합니다.Main 메서드:IPEndPoint 객체를 생성합니다.TcpClient 객체를 생성하고 서버에 연결합니다.NetworkStream을 사용하여 서버와 데이터를 주고받습니다.CHUNK_SIZE 크기의 바이트 배열을 사용하여 파일 데이터를 읽습니다.BodyData 객체에 저장하고, 메시지 헤더를 설정하여 파일 데이터 전송 메시지를 생성합니다.추가 설명
Message, Header, BodyRequest, BodyResponse, BodyData, BodyResult, MessageUtil, CONSTANTS는 사용자 정의 클래스 및 유틸리티입니다.FRAGMENTED는 파일을 여러 개의 메시지로 나누어 전송할 때 사용됩니다.LASTMSG는 현재 메시지가 마지막 메시지임을 나타냅니다.SEQ는 메시지의 순서를 나타냅니다.이 코드는 파일 전송 프로토콜을 구현하는 데 필요한 기본적인 기능을 보여줍니다.