GithubHelp home page GithubHelp logo

nkt-rjh / file-transfer-protocol Goto Github PK

View Code? Open in Web Editor NEW
0.0 1.0 0.0 43 KB

2022 FTP 수행평가 (1 대 N 형식의 서버/클라이언트 시스템으로 클라이언트에서 서버에 파일을 저장 및 가져오기를 할 수 있는 CLI 프로그램)

C# 100.00%
csharp socket-programming thread

file-transfer-protocol's Introduction

File-Transfer-Protocol

1 대 N 형식의 서버/클라이언트 시스템으로 클라이언트에서 서버에 파일을 저장 및 가져오기를 할 수 있는 CLI 프로그램


시스템 구조

실행 이미지
image
 ● 클라이언트가 서버에 접속하는 모습

image
 ● 클라이언트가 서버에 파일을 업로드하는 모습

image
 ● 클라이언트가 서버의 파일을 다운로드하는 모습

image
 ● 클라이언트가 서버에 저장된 파일들을 목록을 확인하는 모습



주요 코드

Client
    ● 클라이언트 부분을 담당하는 코드입니다.
    ● 서버와의 연결, 서버에 저장되 파일 목록 읽기, 서버에 파일 업로드, 서버에 저장된 파일 다운로드 등의 기능을 수행할 수 있습니다,
자세한 코드
using System.Net.Sockets;
using System.Numerics;
using System.Text;

#pragma warning disable CS8600 // 가능한 null 참조 인수입니다.
#pragma warning disable CS8604 // 가능한 null 참조 인수입니다.
#pragma warning disable CS8602 // null 가능 참조에 대한 역참조입니다.
#pragma warning disable CS8625 // Null 리터럴을 null을 허용하지 않는 참조 형식으로 변환할 수 없습니다.

namespace NetworkClient
{
  internal class Client
  {
  	// 종료 문자열 상수
  	private readonly string breakCommand = "exit";
  	// 콘솔 지우기 상수
  	private readonly string clearCommand = "clear";
  	// 클라이언트 파일을 저장하는 디렉토리 경로
  	private readonly string DirectoryPath = string.Concat(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "\\ClientFile");

  	// 현재 클라이언트
  	TcpClient tcpClient = null;

  	private Client()
  	{
  		Run();
  	}

  	static void Main()
  	{
  		Client client = new Client();
  	}

  	// 클라이언트 메인 함수
  	private void Run()
  	{
  		#region 다운로드를 저장할 파일 생성
  		if (!Directory.Exists(DirectoryPath))
  		{
  			Directory.CreateDirectory(DirectoryPath);
  		}
  		#endregion

  		while (true)
  		{
  			try
  			{
  				#region 서버 연결 및 로그인 (TcpClient가 null일 때)
  				if (tcpClient is null)
  				{
  					// 서버 메시지 받는 문자열
  					string serverMessage;

  					// 접속할 아이피 입력받기
  					Console.Write("서버 아이피를 입력하세요 : ");
  					string ip = Console.ReadLine();

  					tcpClient = new TcpClient();
  					try
  					{
  						// 입력한 아이피로 연결
  						tcpClient.Connect(ip, 5000);

  						Console.WriteLine(serverMessage = GetData());
  						// 서버에서 Error를 보냈다면 연결 시도 중지
  						if (serverMessage.StartsWith('E'))
  						{
  							tcpClient = null;

  							continue;
  						}

  						while (true)
  						{
  							// 아이디 입력 및 송신
  							Console.Write("아이디 : ");
  							Send(string.Concat("\n", Console.ReadLine()));
  							Console.WriteLine(serverMessage = GetData());
  							// 서버에서 Error를 송신했다면 아이디가 틀렸다는 뜻이므로 반복
  							if (serverMessage.StartsWith('E')) continue;

  							// 아이디가 맞다면 반복문 탈출
  							break;
  						}

  						while (true)
  						{
  							// 비밀번호 입력 및 송신
  							Console.Write("비밀번호 : ");
  							Send(string.Concat("\n\n", Console.ReadLine()));
  							Console.WriteLine(serverMessage = GetData());
  							// 서버에서 Error를 송신했다면 비밀번호가 틀렸다는 뜻이므로 반복
  							if (serverMessage.StartsWith('E')) continue;

  							// 비밀번호가 맞다면 반복문 탈출
  							break;
  						}
  					}
  					// 시간 초과 시, 접속 실패 문구 출력 후, 다시 아이피 입력을 받는다
  					catch (SocketException)
  					{
  						if (ip.Equals(breakCommand)) break;

  						Console.WriteLine("Error : 접속 실패");
  						tcpClient = null;

  						continue;
  					}
  					continue;
  				}
  				#endregion

  				#region 명령어 입력
  				Console.Write(">>> ");

  				// 함수의 반환값이 참일 때만 서버 메시지 받기
  				switch (Send(Console.ReadLine()))
  				{
  					case 0:
  						Console.WriteLine(GetData());
  						break;
  					case 1:
  						tcpClient.Close();
  						return;
  				}
  				#endregion
  			}
  			catch (IOException)
  			{
  				#region 서버와의 연결이 끊겼을 때 예외 처리
  				Console.WriteLine("Error : 서버와의 접속이 끊겼습니다");
  				tcpClient = null;
  				#endregion
  			}
  		}

  		// while 문을 빠져나오면 클라이언트 종료
  		tcpClient.Close();
  	}

  	private int Send(string stringData)
  	{
  		#region 명령어 예외 처리
  		// null이면 2 반환
  		if (string.IsNullOrEmpty(stringData)) return 2;

  		// 콘솔 창 Clear 명령문이면 Console.Clear() 실행하고 2 반환
  		if (stringData.Equals(clearCommand))
  		{
  			Console.Clear();
  			return 2;
  		}

  		// 종료 명령어라면 1 반환
  		if (stringData.Equals(breakCommand)) return 1;

  		string[] message = stringData.Split(' ');
  		// upload 명령어가 형식에 맞지 않다면 에러 출력
  		if (message.Length > 2)
  		{
  			if (!message[0].Equals("/upload") && message.Length != 3)
  			{
  				Console.WriteLine("Error : 명령어 구문, 경로, 파일 이름 사이에 공백이 있으면 안됩니다");
  				return 2;
  			}
  		}
  		#endregion

  		#region 데이터 서버에 전송

  		// 아이디와 비밀번호 데이터를 전송할 떄는 데이터 앞에 \n이 붙음
  		// 만약 아이디 비밀번호 데이터라면 출력 후 2를 반환
  		if (message[0].StartsWith('\n'))
  		{
  			SendData(message[0]);
  			return 2;
  		}

  		SendData(message[0]);


  		#region 명령문 구별 및 실행
  		switch (message[0].Substring(1))
  		{
  			case "fileList":
  				#region 파일목록
  				if (message.Length > 1)
  				{
  					SendData("Error");
  					break;
  				}
  				return 0;
  				#endregion
  			// 클라이언트에게 파일 받기
  			case "upload":
  				#region 업로드
  				#region 파일을 보내기 전에 에러가 없는지 확인
  				// 명령어 형식이 맞지 않다면 Error1 전송
  				if (message.Length < 2)
  				{
  					SendData("Error1");
  					return 0;
  				}
  				else
  				{
  					SendData("Success");
  				}
  				GetData();

  				// 전송하려는 파일이 존재하지 않다면 Error2 전송
  				if (!File.Exists(message[1]))
  				{
  					SendData("Error2");
  					return 0;
  				}
  				else
  				{
  					SendData("Success");
  				}
  				GetData();

  				string[] splitFilePath = message[1].Split('\\');
  				string[] fileNames = message[1].Split('\\')[splitFilePath.Length - 1].Split(' ');
  				switch (fileNames.Length)
  				{
  					// 기존 파일 이름 그대로 전송
  					case 1:
  						// 파일 이름이 1글자 미만이거나, 확장자가 없는 경우 Error1 전송
  						if (fileNames[0].Length < 4 || fileNames[0].Substring(fileNames[0].Length - 4, 1) is not ".")
  						{
  							SendData("\aError1");
  							return 0;
  						}

  						SendData(fileNames[0]);
  						break;

  					// 파일 이름을 변경하여 전송 ex) /upload abc.png(기존 파일 이름) bbb.png(변경할 파일 이름)
  					case 2:
  						// 파일 이름이 1글자 미만이거나, 확장자가 없는 경우 Error1 전송
  						if (fileNames[0].Length < 4 || fileNames[1].Length < 4)
  						{
  							SendData("\aError1");
  							return 0;
  						}

  						string[] fileExtensions =
  						{
  							fileNames[0].Substring(fileNames[0].Length - 4, 4),
  							fileNames[1].Substring(fileNames[1].Length - 4, 4)
  						};

  						// 변경할 파일 이름이 기존 파일 이름과 같거나, 확장자가 없다면 Error1 전송
  						if (fileExtensions[0] != fileExtensions[1] || (fileExtensions[0][0] is not '.' || fileExtensions[1][0] is not '.'))
  						{
  							SendData("\aError2");
  							return 0;
  						}

  						// 파일 이름 데이터 전송
  						SendData(fileNames[1]);
  						break;
  				}
  				GetData();

  				// 서버에서 이미 해당 이름의 파일이 있다고 말한다면 덮어쓰기 여부 걸졍
  				if (GetData() is "Already Exists")
  				{
  					Console.Write("이미 파일이 있습니다. 덮어쓰시겠습니까? (Y/Any String) : ");
  					string str = Console.ReadLine();
  					SendData(str);

  					// 덮어쓰기를 하지 않으면 upload 취소
  					if (str is not "Y" && str is not "y") return 2;
  				}
  				#endregion

  				using (FileStream source = File.OpenRead(message[1]))
  				{
  					// 파일 크기 보내기
  					BigInteger fileSize = new BigInteger(new FileInfo(message[1]).Length);
  					SendData(fileSize.ToString());

  					Console.WriteLine("Working : 전송 중...");
  					while (true)
  					{
  						// 반복마다 최대 1048576 bit 만큼 파일 데이터 전송
  						byte[] buffer = new byte[104857600];

  						int bytesRead = source.Read(buffer, 0, buffer.Length);
  						fileSize = BigInteger.Subtract(fileSize, bytesRead);
  						tcpClient.GetStream().Write(buffer, 0, bytesRead);
  						// 파일 데이터를 모두 보냈다면 반복문 탈출
  						if (fileSize.Equals(0))
  						{
  							tcpClient.GetStream().Flush();
  							break;
  						}
  					}
  				}
  				#endregion
  				break;
  			// 클라이언트에게 파일 전송
  			case "download":
                  #region 다운로드
  				// 명령어 형식이 맞지 않다면 Error1 수신
                  if (message.Length < 2)
                  {
                      SendData("Error1");
                      return 0;
                  }
                  else
                  {
                      SendData("Success");
                  }
                  GetData();

                  SendData(message[1]);

  				// 서버에서 보낸 이름에 맞는 파일이 없다고 한다면 에러 문구 출력 후 download 종료
  				string canDownload = GetData();
  				if (canDownload.Equals("Error : 해당 파일이 존재하지 않습니다"))
  				{
  					Console.WriteLine(canDownload);
  					return 2;
  				}

  				string name = GetData();

  				using (FileStream fileStream1 = new FileStream(string.Concat(DirectoryPath, '\\', name), FileMode.OpenOrCreate, FileAccess.Write))
  				{
  					BigInteger fileSize = BigInteger.Parse(GetData());

  					Console.WriteLine("Working : 전송 중...");
  					while (true)
  					{
  						// 반복마다 최대 1048576 bit 만큼 서버가 보낸 데이터를 받아옴
  						byte[] byteData = new byte[1048576];
  						int readSize = tcpClient.GetStream().Read(byteData, 0, byteData.Length);
  						fileSize = BigInteger.Subtract(fileSize, readSize);
  						fileStream1.Write(byteData, 0, readSize);
  						fileStream1.Flush();
  						// 서버가 보낸 데이터를 모두 받았다면 반복문 탈출
  						if (readSize < byteData.Length)
  							break;
  					}

  					// 수신 받은 데이터의 용량이 원본과 같지 안다면 Error 전송
  					if (!fileSize.Equals(0))
  					{
  						SendData("Error");
  					}
  					else
  					{
  						SendData("Success");
  					}
  				}
  				#endregion
  				break;
  		}
  		#endregion

  		#endregion

  		// 데이터 전송 성공 시 0 반환
  		return 0;
  	}

  	// 서버에 데이터 전송
  	private void SendData(string message)
  	{
  		byte[] byteData = Encoding.UTF8.GetBytes(message, 0, message.Length);
  		tcpClient.GetStream().Write(byteData, 0, byteData.Length);
  		tcpClient.GetStream().Flush();
  	}

  	// 서버한테 데이터 수신 받기
  	private string GetData()
  	{
  		#region 서버에서 데이터 전달 받고 string 으로 변환
  		byte[] byteData = new byte[1024];
  		int bytes = tcpClient.GetStream().Read(byteData, 0, byteData.Length);
  		string message = Encoding.UTF8.GetString(byteData, 0, bytes);
  		#endregion

  		// 변환한 string 반환
  		return message;
  	}
  }
}

Server
    ● 서버 부분을 담당하는 코드입니다.
    ● 클라이언트 연결 관리, 명령어 인식 후 명령어 수행 혹은 실패 클라이언트에 전송, 파일 데이터 저장, 파일 데이터 클라이언트에 수신 등의 기능을 수행할 수 있습니다.

자세한 코드
using System.Net;
using System.Net.Sockets;
using System.Numerics;
using System.Text;

namespace NetworkServer
{
  internal class Server
  {
  	// 아이디, 비밀번호 상수
  	private const string ID = "admin";
  	private const string Password = "1234";
  	// 서버 파일을 저장하는 디렉토리 경로
  	private readonly string DirectoryPath = string.Concat(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "\\ServerFile");

  	// 서버 전역변수
  	private TcpListener server = null;
  	// 클라이언트 리스트 전역변수
  	private List<TcpClient> clients = new List<TcpClient>();

  	private Server()
  	{
  		Run();
  	}

  	private static void Main()
  	{
  		Server server = new Server();
  	}

  	// 서버 구동 메인 함수
  	private void Run()
  	{
  		#region 업로드를 저장할 파일 생성
  		if (!Directory.Exists(DirectoryPath))
  		{
  			Directory.CreateDirectory(DirectoryPath);
  		}
  		#endregion

  		#region 서버 실행
  		server = new TcpListener(IPAddress.Any, 5000);
  		server.Start();
  		#endregion

  		#region 서버 IP 출력
  		Console.WriteLine("Server Start");
  		Console.WriteLine("서버 IP : " + GetLocalIP());
  		#endregion

  		#region 클라이언트 연결 요청 받는 쓰레드 5개 실행
  		while (true)
  		{
  			// 접속 허용
  			TcpClient client = server.AcceptTcpClient();

  			// 클라이언트 개수가 5개 이상이라면 접속 해제
  			if (clients.Count >= 5)
  			{
  				SendData("Error : 인원 수가 꽉찼습니다", client);
  				client.Close();
  				continue;
  			}
  			
  			// 클라이언트를 리스트에 추가하고 쓰레드를 생성하여 통신 시작
  			clients.Add(client);
  			Thread thread = new Thread(() => ReceiveData(client));
  			thread.Start();
  		}
  		#endregion
  	}
  	
  	// 클라이언트에서 보낸 데이터 읽기
  	private void ReceiveData(TcpClient tcpClient)
  	{
  		ClientData callbackClient = new ClientData(tcpClient);
  		SendData("Sucess : 접속 성공", callbackClient.client);
  		try
  		{
  			// 콜백으로 받아온 데이터 ClientData로 형변환
  			while (true)
  			{
  				try
  				{
  					#region 명령어 구별
  					// 클라이언트에서 받아온 정보->UTF8->string 순으로 변환
  					byte[] byteData = new byte[1024];
  					int readBytes = callbackClient.client.GetStream().Read(callbackClient.readByteData, 0, callbackClient.readByteData.Length);

  					string readString = Encoding.UTF8.GetString(callbackClient.readByteData, 0, readBytes);


  					#region 받은 데이터의 앞에 / 가 없을 때 (아이디, 비밀번호인 경우)
  					// 클라이언트에게 데이터를 받을 때 문자열 앞에 \n\n 가 있으면 비밀번호, \n가 있으면 아이디이다
  					if (!readString.StartsWith('/'))
  					{
  						// 비밀번호인 경우
  						if (readString.Substring(0, 2).Equals("\n\n"))
  						{
  							SendData(readString.Substring(2).Equals(Password), "Success : 비밀번호 일치", "Error : 다시 입력해주세요", callbackClient);
  						}
  						// 아이디인 경우
  						else if (readString.Substring(0, 1).Equals("\n"))
  						{
  							SendData(readString.Substring(1).Equals(ID), "Success : 아이디 일치", "Error : 다시 입력해주세요", callbackClient);
  						}
  						// 아무 명령어에도 속하지 않은 경우
  						else
  						{
  							SendData("Error : 명령어를 사용해주세요", callbackClient.client);
  						}
  						continue;
  					}
  					#endregion

  					#region 받은 데이터에 / 가 있을 때 (명령어인 경우)

  					bool isSuccess = false;

  					switch (readString.Substring(1))
  					{
  						//저장된 파일 목록 제공
  						case "fileList":
  							#region 파일목록
  							// 서버 디렉토리 정보 가져오기
  							string[] filePathArray = Directory.GetFiles(DirectoryPath, "*.*", SearchOption.AllDirectories);

  							// 서버 디렉토리에 아무것도 없다면 목록을 출력하지 않고 continue
  							if (filePathArray.Length is 0)
  							{
  								SendData("\n** 파일 없음 **\n", callbackClient.client);
  								continue;
  							}

  							string message = "\n** 파일 목록 **\n";
  							for (int i = 0; i < filePathArray.Length; i++)
  							{
  								string[] filePathSplit = filePathArray[i].Split('\\');

  								decimal fileLength = new FileInfo(filePathArray[i]).Length;

  								string unit = string.Empty;

  								// bit로 받은 파일의 용량을 1024로 나눠서 용량을 단위로 나눠서 출력
  								int count;
  								for (count = 0; (fileLength /= 1024) >= 1024 && count <= 3; count++) ;
  								count++;
  								fileLength = Math.Round(fileLength, 2);
  								switch (count)
  								{
  									case 0:
  										unit = "byte";
  										break;

  									case 1:
  										unit = "KB";
  										break;

  									case 2:
  										unit = "MB";
  										break;

  									case 3:
  										unit = "GB";
  										break;
  								}

  								message = string.Concat(message, filePathSplit[filePathSplit.Length - 1], "    ", fileLength, unit, '\n');
  							}
  							message = string.Concat(message, "\n");

  							// 파일 목록과 각각의 용량 값 클라이언트에 전송
  							SendData(message, callbackClient.client);
  							#endregion
  							break;
  						// 클라이언트에게 파일 받기
  						case "upload":
  							#region 업로드
  							// 데이터를 옮기고 저장할 변수 선언
  							string stringData;
  							FileStream fileStream;
  							int readSize;

  							#region 경로 예외 처리
  							// 클라이언트에게 Error1을 받았다면
  							if (GetData(callbackClient, 1024).Equals("Error1"))
  							{
  								SendData("Error : 명령어가 형식에 맞지 않습니다", callbackClient.client);
  								continue;
  							}
  							else
  							{
  								SendData("Success", callbackClient.client);
  							}

  							// 클라이언트에게 Error2를 받았다면
  							if (GetData(callbackClient, 1024).Equals("Error2"))
  							{
  								SendData("Error : 그런 파일이 없습니다", callbackClient.client);
  								continue;
  							}
  							else
  							{
  								SendData("Success", callbackClient.client);
  							}

  							// 파일 경로 지정
  							stringData = GetData(callbackClient, 1024);
  							string path;
  							switch (stringData)
  							{
  								case "\aError1":
  									SendData("Error : 파일명에 확장자가 없으면 안됩니다", callbackClient.client);
  									continue;
  								case "\aError2":
  									SendData("Error : 새로 지을 이름과 기존 이름의 확장자가 다르면 안됩니다", callbackClient.client);
  									continue;
  								default:
  									path = string.Concat(DirectoryPath, "\\", stringData);
  									SendData("Success", callbackClient.client);
  									break;
  							}
  							#endregion

  							#region 클라이언트에게 받은 파일이 이미 있는지 확인
  							if (File.Exists(path))
  							{
  								// 이미 파일이 있다고 전송
  								SendData("Already Exists", callbackClient.client);

  								// 덮어쓸건지 취소할건지 응답 받고 동작
  								stringData = GetData(callbackClient, 1024, 1);

  								// Y 나 y 가 아니라면 모두 취소로 판단
  								if (!(stringData.Equals("Y") || stringData.Equals("y"))) continue;
  								// 취소가 아니라서 goto 문이 실행되지 않았다면 파일 덮어쓰기 실행
  								fileStream = new FileStream(path, FileMode.Open, FileAccess.Write);
  							}
  							else
  							{
  								SendData("Success", callbackClient.client);
  								fileStream = File.Create(path);
  							}
  							#endregion

  							#region 파일 크기 수신
  							byteData = GetData(callbackClient);

  							int integerLength = 0;
  							for (integerLength = 0; byteData[integerLength] >= 48 && byteData[integerLength] <= 57; integerLength++) ;

  							// long으로도 저장을 할 수 없기에 BigInteger로 용량 정보 값을 수신
  							BigInteger fileSize = BigInteger.Parse(Encoding.UTF8.GetString(byteData, 0, integerLength));
  							#endregion

  							#region 파일 byte 수신
  							try
  							{
  								while (true)
  								{
  									// 반복마다 최대 1048576 bit 만큼 클라이언트가 보낸 데이터를 받아옴
  									byteData = new byte[1048576];
  									readSize = callbackClient.client.GetStream().Read(byteData, 0, byteData.Length);
  									fileSize = BigInteger.Subtract(fileSize, readSize);
  									fileStream.Write(byteData, 0, readSize);
  									fileStream.Flush();
  									// 클라이언트가 보낸 데이터를 모두 받았다면 반복문 탈출
  									if (fileSize.Equals(0))
  										break;
  								}
  							}
  							// 예외 발생 시, 수신하던 파일 삭제 및 FileStream 종료
  							catch (IOException)
  							{
  								fileStream.Close();
  								File.Delete(path);
  								continue;
  							}
  							#endregion

  							fileStream.Close();

  							isSuccess = true;
  							#endregion
  							break;
  						// 클라이언트에게 파일 전송
  						case "download":
  							#region 다운로드
  							// 클라이언트에게 Error1을 받았다면
  							if (GetData(callbackClient, 1024).Equals("Error1"))
                              {
                                  SendData("Error : 명령어가 형식에 맞지 않습니다", callbackClient.client);
                                  continue;
                              }
                              else
                              {
                                  SendData("Success", callbackClient.client);
                              }

  							// 클라이언트에서 다운로드 받고 싶어하는 파일 이름을 수신 후 경로로 만듦
  							string name = GetData(callbackClient, 1024);
  							string downloadPath = string.Concat(DirectoryPath, '\\', name);

  							// 해당 이름의 파일이 존재한다면 Success, 존재하지 않다면 Error 전송
  							if (!File.Exists(downloadPath))
  							{
  								SendData("Error : 해당 파일이 존재하지 않습니다", callbackClient.client);
  								continue;
  							}
  							else
  							{
  								SendData("Success", callbackClient.client);
  							}

  							// 파일을 열어서 정보를 클라이언트에 수신
  							using (FileStream fileStream1 = File.OpenRead(downloadPath))
  							{
  								// 파일 이름 보내기
  								SendData(name, callbackClient.client);

  								// 파일 크기 보내기
  								BigInteger fileSize1 = new BigInteger(new FileInfo(downloadPath).Length);
  								SendData(fileSize1.ToString(), callbackClient.client);

  								try
  								{
  									while (true)
  									{
  										// 반복마다 최대 1048576 bit 만큼 파일 데이터를 전송
  										byte[] buffer = new byte[104857600];

  										int bytesRead = fileStream1.Read(buffer, 0, buffer.Length);
  										fileSize1 = BigInteger.Subtract(fileSize1, bytesRead);
  										callbackClient.client.GetStream().Write(buffer, 0, bytesRead);
  										// 파일 데이터를 모두 보냈다면 반복문 탈출
  										if (fileSize1.Equals(0))
  											break;
  									}
  								}
  								// 예외 발생 시, FileStream 종료
  								catch (IOException)
  								{
  									fileStream1.Close();
  									continue;
  								}
  							}

  							// 클라이언트에서 파일이 온전히 전송되지 않았다는 Error를 수신 받았다면, 에러 문구 클라이언트에 전송
  							if (GetData(callbackClient, 1024).Equals("Error"))
  							{
  								SendData("Error : 파일을 온전히 다운 받지 못하였습니다. 다시 시도해주세요", callbackClient.client);
  								continue;
  							}
  							// 아니라면 성공으로 간주
  							else
  							{
  								isSuccess = true;
  							}
  							#endregion
  							break;
  						// 아무 명령어에도 속하지 않은 경우
  						default:
  							isSuccess = false;
  							break;
  					}

  					// 명령어 수행 여부 메시지 전송
  					SendData(isSuccess, "Success : 명령어 수행 성공", "Error : 형식에 맞는 명령어인지 확인해주세요", callbackClient);

  					#endregion
  					#endregion
  				}
  				catch (ArgumentOutOfRangeException)
  				{
  					#region 클라이언트에서 과도한 명령어 전달 시 예외 처리
  					SendData("Warning : 과도한 데이터 전달", callbackClient.client);
  					#endregion
  				}
  			}
  		}
  		// 부가적인 에러 발생 시, 클라이언트와의 연결 종료
  		catch (IOException)
  		{
  			RemoveClient(tcpClient);
  		}
  		catch (InvalidOperationException)
  		{
  			RemoveClient(tcpClient);
  		}
  	}

  	// 리스트에 저장된 클라이언트 제거
  	private void RemoveClient(TcpClient tcpClient)
  	{
  		for (int count = 0; count < clients.Count; count++)
  		{
  			// 연결이 해지되었다면 해지된 클라이언트를 찾아서 삭제하고 다시 클라이언트 탐색
  			if (clients[count].Equals(tcpClient))
  			{
  				clients.RemoveAt(count);
  			}
  		}
  	}

  	#region 클라이언트에 데이터 전송
  	// 조건에 따라 string 데이터 byte 배열로 변환
  	private void SendData(bool result, string successMessage, string failedMessage, ClientData clientData)
  	{
  		byte[] byteData = Encoding.UTF8.GetBytes(result ? successMessage : failedMessage);
  		clientData.client.GetStream().Write(byteData, 0, byteData.Length);
  		clientData.client.GetStream().Flush();
  	}
  	// string 데이터 byte 배열로 변환
  	private byte[] SendData(string message, TcpClient tcpClient)
  	{
  		byte[] byteData = Encoding.UTF8.GetBytes(message);
  		tcpClient.GetStream().Write(byteData, 0, byteData.Length);
  		tcpClient.GetStream().Flush();

  		return byteData;
  	}
  	#endregion

  	#region 클라이언트에게서 데이터 수신
  	// 정해진 단위(arraySize)로 수신받고 수신받은 데이터를 정해진 단위(readSize)로 UTF8 형식 string으로 변환
  	private string GetData(ClientData clientData, long arraySize, int readSize)
  	{
  		clientData.client.GetStream().Flush();
  		byte[] byteData = new byte[arraySize];
  		clientData.client.GetStream().Read(byteData, 0, byteData.Length);

  		return Encoding.UTF8.GetString(byteData, 0, readSize);
  	}
  	// 정해진 단위(arraySize)로 클라이언트에게서 데이터를 수신
  	private string GetData(ClientData clientData, long arraySize)
  	{
  		clientData.client.GetStream().Flush();
  		byte[] byteData = new byte[arraySize];
  		int readSize = clientData.client.GetStream().Read(byteData, 0, byteData.Length);

  		return Encoding.UTF8.GetString(byteData, 0, readSize);
  	}
  	// 1024 단위로 클라이언트에게서 데이터를 수신
  	private byte[] GetData(ClientData clientData)
  	{
  		clientData.client.GetStream().Flush();
  		byte[] byteData = new byte[1024];
  		clientData.client.GetStream().Read(byteData, 0, byteData.Length);

  		return byteData;
  	}
  	#endregion

  	// 서버 컴퓨터의 IP를 가져오는 함수
  	private string GetLocalIP()
      {
          IPHostEntry host = Dns.GetHostEntry(Dns.GetHostName());
          string LocalIP = string.Empty;

          for (int i = 0; i < host.AddressList.Length; i++)
          {
              if (host.AddressList[i].AddressFamily == AddressFamily.InterNetwork)
              {
                  LocalIP = host.AddressList[i].ToString();
                  break;
              }
          }

          return LocalIP;
      }
  }
}

ClientData
    ● 서버에 연결된 클라이언트를 관리하기 쉽도록 만든 클래스입니다.

자세한 코드
using System.Net.Sockets;

namespace NetworkServer
{
  internal class ClientData
  {
  	// 클라이언트 정보
  	public TcpClient client { get; set; }
  	// 서버에서 수신 시, 한 번에 가져오는 byte 양
  	public byte[] readByteData { get; set; }

  	// 생성 시, 클래스 초기화
  	public ClientData(TcpClient client)
  	{
  		this.client = client;
  		readByteData = new byte[1024];
  	}
  }
}

file-transfer-protocol's People

Contributors

nkt-rjh avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.