Development note/C#

[C#] 소켓 통신을 이용해서 다수의 파일(폴더)를 전송하는 방법

v명월v 2021. 1. 8. 15:38

안녕하세요. 명월입니다.


이 글은 C#에서 소켓 통신을 이용해서 다수의 파일(폴더)를 전송하는 방법에 대한 글입니다.


이전에 제가 소켓 통신을 이용해서 파일을 전송하는 방법에 대한 글을 작성한 적이 있습니다.

링크 - [C#] 소켓 통신을 이용해서 파일을 전송하는 방법

그런데 어떤 분께서 파일만 전송하는 것이 아니라 폴더를 전송하는 방법에 대해 질문하신 분이 있어서 작성해 봤습니다.


파일을 전송하는 방법에 대해서는 두가지 방법이 있습니다.

먼저 클라이언트에서 폴더 자체를 압축해서 파일을 보내고 받는 쪽에서 수신이 되면 압축을 푸는 방법입니다.

이렇게 작성하면 제가 이전에 작성한 방법에서 파일 압축, 해제 기능만 넣으면 가능하겠네요.

링크 - [C#] Zip 압축 코드 소스

링크 - [C#] Zip 압축 해제 코드 소스

using System;
using System.IO;
using System.IO.Compression;
using System.Net;
using System.Net.Sockets;
namespace DirectoryServer
{
  // 상태 열거형	
  enum State
  {
    STATE,
    FILESIZE,
    FILEDOWNLOAD
  }
  // 파일 클래스	
  class File
  {
    // 상태	
    protected State state = State.STATE;
    // 파일	
    public byte[] Binary { get; set; }
  }
  // 파일 클래스를 상속 받는다.	
  class Client : File
  {
    // 클라이언트 소켓	
    private Socket socket;
    // 버퍼	
    private byte[] buffer;
    // 파일 다운로드 위치	
    private int seek = 0;
    // 다운로드 디렉토리	
    private string SaveDirectory = @"d:\work\";
    // 생성자	
    public Client(Socket socket)
    {
      // 소켓 받기	
      this.socket = socket;
      // 버퍼	
      buffer = new byte[1];
      // buffer로 메시지를 받고 Receive함수로 메시지가 올 때까지 대기한다.	
      this.socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, Receive, this);
      // 접속 환영 메시지	
      var remoteAddr = (IPEndPoint)socket.RemoteEndPoint;
      // 콘솔 출력	
      Console.WriteLine($"Client:(From:{remoteAddr.Address.ToString()}:{remoteAddr.Port},Connection time:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")})");
    }
    // 데이터 수신	
    private void Receive(IAsyncResult result)
    {
      // 접속이 연결되어 있으면...	
      if (socket.Connected)
      {
        // EndReceive를 호출하여 데이터 사이즈를 받는다.	
        // EndReceive는 대기를 끝내는 것이다.	
        int size = this.socket.EndReceive(result);
        // 상태	
        if (state == State.STATE)
        {
          switch (buffer[0])
          {
            // 0이면 파일 크기	
            case 0:
              state = State.FILESIZE;
              // 크기는 int형으로 받는다.	
              buffer = new byte[4];
              break;
            // 1이면 파일
            case 1:
              state = State.FILEDOWNLOAD;
              // 파일 버퍼 설정	
              buffer = new byte[Binary.Length];
              // 다운로드 위치 0	
              seek = 0;
              break;
          }
        }
        else if (state == State.FILESIZE)
        {
          // 데이터를 int형으로 변환(Bigendian)해서 파일 Binary를 할당한다.	
          Binary = new byte[BitConverter.ToInt32(buffer, 0)];
          // 상태를 초기화	
          buffer = new byte[1];
          state = State.STATE;
        }
        // 파일 다운로드	
        else if (state == State.FILEDOWNLOAD)
        {
          // 다운 받은 데이터를 FileName 변수로 옮긴다.	
          Array.Copy(buffer, 0, Binary, seek, size);
          // 받은 만큼 위치 옮긴다.	
          seek += size;
          // 위치와 파일 크기가 같으면 종료	
          if (seek >= Binary.Length)
          {
            // binary의 압축 풀고 디렉토리에 저장한다.	
            ExtractZip(Binary, SaveDirectory);
            // 접속을 끊는다.	
            this.socket.Disconnect(false);
            this.socket.Close();
            this.socket.Dispose();
            return;
          }
        }
        // buffer로 메시지를 받고 Receive함수로 메시지가 올 때까지 대기한다.	
        this.socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, Receive, this);
      }
    }
    // 압축 풀기 소스
    public void ExtractZip(byte[] binary, string destinationPath)
    {
      // 디렉토리가 있는지 확인.
      if (!Directory.Exists(destinationPath))
      {
        // 없으면 생성
        Directory.CreateDirectory(destinationPath);
      }
      // byte형식을 Stream 형식으로 변환
      using (var stream = new MemoryStream(binary))
      {
        // 압축 클래스에 넣는다.
        using (ZipArchive zip = new ZipArchive(stream))
        {
          // 압축 아이템 별로 
          foreach (ZipArchiveEntry entry in zip.Entries)
          {
            // 압축 파일의 상대 주소를 디스크의 물리 주소로 바꾼다.
            var filepath = Path.Combine(destinationPath, entry.FullName);
            // 파일의 디렉토리를 확인한다.
            var subDir = Path.GetDirectoryName(filepath);
            // 디렉토리가 없으면
            if (!Directory.Exists(subDir))
            {
              // 디렉토리 생성
              Directory.CreateDirectory(subDir);
            }
            // 압축 풀기(파일이 존재하면 덮어쓰기)
            entry.ExtractToFile(filepath, true);
          }
        }
      }
    }
  }
  // 메인 Program은 Socket을 상속받고 서버 Socket으로 사용한다.	
  class Program : Socket
  {
    // 생성자	
    public Program() : base(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
    {
      // 포트는 10000을 Listen한다.	
      base.Bind(new IPEndPoint(IPAddress.Any, 10000));
      // 서버 대기	
      base.Listen(10);
      // 비동기 소켓으로 Accept 클래스로 대기한다.	
      BeginAccept(Accept, this);
    }
    // 클라이언트가 접속하면 함수가 호출된다.	
    private void Accept(IAsyncResult result)
    {
      // EndAccept로 접속 Client Socket을 받는다. EndAccept는 대기를 끝나는 것이다.	
      // Client 클래스를 생성한다.	
      var client = new Client(EndAccept(result));
      // 비동기 소켓으로 Accept 클래스로 대기한다.	
      BeginAccept(Accept, this);
    }
    // 프로그램 시작 함수	
    static void Main(string[] args)
    {
      // Program 클래스를 실행한다.	
      new Program();
      // q키를 누르면 서버는 종료한다.	
      Console.WriteLine("Press the q key to exit.");
      while (true)
      {
        string k = Console.ReadLine();
        if ("q".Equals(k, StringComparison.OrdinalIgnoreCase))
        {
          break;
        }
      }
    }
  }
}

위는 서버 소스입니다. 이전의 파일의 프로토콜을 0부터 3까지 했습니다만, 압축 파일 안에 파일 이름과 경로의 정보가 있기 때문에 굳이 파일 이름을 따로 받을 필요가 없겠네요. 그래서 이번 프로토콜은 0과 1만해서 파일을 받습니다.

그리고 클라이언트에서는 압축 파일이 오기 때문에 해당 디렉토리에 압축을 풀어야 하는 함수를 만들어야 하네요.

즉, 클라이언트에서 디렉토리를 압축해서 데이터를 보내면 서버에서는 그 데이터를 받고 압축을 푸는 형식으로 디렉토리를 전송하는 것입니다.


다음은 디렉토리를 전송하는 클라이언트입니다.

using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Net;
using System.Net.Sockets;
namespace DirectoryClient
{
  class Program
  {
    // 파일 리스트를 가져온다.
    static List<String> GetFileList(String rootPath, List<String> fileList)
    {
      if (fileList == null)
      {
        return null;
      }
      var attr = File.GetAttributes(rootPath);
      // 해당 path가 디렉토리이면
      if ((attr & FileAttributes.Directory) == FileAttributes.Directory)
      {
        var dirInfo = new DirectoryInfo(rootPath);
        // 하위 모든 디렉토리는
        foreach (var dir in dirInfo.GetDirectories())
        {
          // 재귀로 통하여 list를 취득한다.
          GetFileList(dir.FullName, fileList);
        }
        // 하위 모든 파일은
        foreach (var file in dirInfo.GetFiles())
        {
          // 재귀를 통하여 list를 취득한다.
          GetFileList(file.FullName, fileList);
        }
      }
      // 해당 path가 파일이면 (재귀를 통해 들어온 경로)
      else
      {
        var fileInfo = new FileInfo(rootPath);
        // 리스트에 full path를 저장한다.
        fileList.Add(fileInfo.FullName);
      }
      return fileList;
    }
    // 파일 압축하기
    static byte[] CommpressDirectory(string sourcePath)
    {
      // 재귀 식으로 파일 리스트를 가져온다.
      var filelist = GetFileList(sourcePath, new List<String>());
      // 메모리 스트림으로 바이너리를 만든다.
      using (MemoryStream stream = new MemoryStream())
      {
        // 압축 클래스 생성
        using (ZipArchive zipArchive = new ZipArchive(stream, ZipArchiveMode.Create))
        {
          // 파일 리스트 별로 압축 아이템을 만든다.
          foreach (string file in filelist)
          {
            // 실제 경로로 부터 압축 경로로 바꾼다.
            string path = file.Substring(sourcePath.Length + 1);
            // 파일로 부터 압축
            zipArchive.CreateEntryFromFile(file, path);
          }
        }
        // 압축 바이너리를 byte형식으로 리턴
        return stream.GetBuffer();
      }
    }
    // 실행 함수	
    static void Main(string[] args)
    {
      // 업로드할 디렉토리
      var sourcepath = @"D:\sourcework";
      // 서버에 접속한다.	
      var ipep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 10000);
      // 디렉토리가 존재하는지	
      if (Directory.Exists(sourcepath))
      {
        // 바이너리 버퍼	
        var binary = CommpressDirectory(sourcepath);
        // 소켓 생성	
        using (Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
        {
          // 접속	
          client.Connect(ipep);
          // 상태 0 - 파일 크기를 보낸다.	
          client.Send(new byte[] { 0 });
          // 송신 - 파일 이름 크기 Bigendian	
          client.Send(BitConverter.GetBytes(binary.Length));
          // 상태 1 - 파일를 보낸다.	
          client.Send(new byte[] { 1 });
          // 송신 - 파일	
          client.Send(binary);
        }
      }
      else
      {
        // 콘솔 출력	
        Console.WriteLine("The directory is not exists.");
      }
      // 아무 키나 누르면 종료	
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}

클라이언트에서는 디렉토리를 압축하는 소스를 넣어야 합니다. 디렉토리를 압축하면 결국에는 파일 한개의 binary가 되기 때문에 그걸 파일 전송과 같은 방식으로 서버로 전송하면 됩니다.

참고로 Server와 Client 모두 압축 클래스를 사용하기 위해서는 System.IO.Compression를 참조해야 합니다.

C#의 압축 해제 글을 참고해 주세요.

링크 - [C#] Zip 압축 코드 소스

링크 - [C#] Zip 압축 해제 코드 소스


이제 실행하기 전에 먼저 제가 보낼 예제 디렉토리를 만들어야 겠네요.

클라이언트 소스에서 경로 D:\sourcework를 지정했습니다.

sourcework 폴더 안에는 test.txt 파일과 sub 디렉토리가 있습니다. 그럼 sub 디렉토리 안에는 file.txt 파일과 subsub 디렉토리가 있습니다.

마지막으로 subsub 디렉토리 안에는 check.txt 파일이 있습니다. 이걸 서버로 해서 work 폴더로 통채로 옮겨야 겠네요.


서버를 실행합니다.

클라이언트를 실행합니다.

이제 서버에서는 접속이 되었는지 확인을 합니다.

제대로 접속을 한 것 같습니다. 코드 상에도 에러가 발생하지 않았습니다.

d:/work 폴더를 확인해 보겠습니다.

역시 디렉토리가 전송이 된 것을 확인할 수 있습니다.


위 방법은 디렉토리를 압축해서 압축 파일을 전송해서 다시 디렉토리에 압축을 푸는 것으로 디렉토리를 옮긴 것입니다.

결과적으로는 디렉토리를 전송한 것이라고 할 수 있으나 내부 소스는 결국 파일 하나 옮긴 것과 같습니다. 그렇다면 이번에는 압축 코드를 사용하지 말고 진짜 파일 하나하나 전송하는 소스를 작성하겠습니다.


먼저 예전에 파일을 받는 소스에서 서버는 수정할 것이 없습니다. 왜냐하면 어차피 하나하나 파일을 전송하는 것이기 때문에 서버에서는 파일을 하나하나 받고 그대로 저장만 하면 됩니다.

그러나 이전 소스에서는 파일 전송이 끝나면 소켓을 접속을 끊었습니다. 요번에는 접속을 끊지 않고 그냥 초기 상태로 돌리기만 하면 됩니다.

using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace FileServer
{
  // 상태 열거형	
  enum State
  {
    STATE,
    FILENAMESIZE,
    FILENAME,
    FILESIZE,
    FILEDOWNLOAD
  }
  // 파일 클래스	
  class File
  {
    // 상태	
    protected State state = State.STATE;
    // 파일 이름	
    public byte[] FileName { get; set; }
    // 파일	
    public byte[] Binary { get; set; }
  }
  // 파일 클래스를 상속 받는다.	
  class Client : File
  {
    // 클라이언트 소켓	
    private Socket socket;
    // 버퍼	
    private byte[] buffer;
    // 파일 다운로드 위치	
    private int seek = 0;
    // 다운로드 디렉토리	
    private string SaveDirectory = @"d:\work\";
    // 생성자	
    public Client(Socket socket)
    {
      // 소켓 받기	
      this.socket = socket;
      // 버퍼	
      buffer = new byte[1];
      // buffer로 메시지를 받고 Receive함수로 메시지가 올 때까지 대기한다.	
      this.socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, Receive, this);
      // 접속 환영 메시지	
      var remoteAddr = (IPEndPoint)socket.RemoteEndPoint;
      // 콘솔 출력	
      Console.WriteLine($"Client:(From:{remoteAddr.Address.ToString()}:{remoteAddr.Port},Connection time:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")})");
    }
    // 데이터 수신	
    private void Receive(IAsyncResult result)
    {
      // 접속이 연결되어 있으면...	
      if (socket.Connected)
      {
        // EndReceive를 호출하여 데이터 사이즈를 받는다.	
        // EndReceive는 대기를 끝내는 것이다.	
        int size = this.socket.EndReceive(result);
        // 상태	
        if (state == State.STATE)
        {
          switch (buffer[0])
          {
            // 0이면 파일 이름 크기	
            case 0:
              state = State.FILENAMESIZE;
              // 크기는 int형으로 받는다.	
              buffer = new byte[4];
              break;
            // 1이면 파일 이름	
            case 1:
              state = State.FILENAME;
              // 파일 이름 버퍼 설정	
              buffer = new byte[FileName.Length];
              // 다운로드 위치 0	
              seek = 0;
              break;
            // 2이면 파일 크기	
            case 2:
              state = State.FILESIZE;
              // 크기는 int형으로 받는다.	
              buffer = new byte[4];
              break;
            // 3이면 파일	
            case 3:
              state = State.FILEDOWNLOAD;
              // 파일 버퍼 설정	
              buffer = new byte[Binary.Length];
              seek = 0;
              break;
          }
        }
        // 파일 이름 사이즈	
        else if (state == State.FILENAMESIZE)
        {
          // 데이터를 int형으로 변환(Bigendian)해서 FileName 변수를 할당한다.	
          FileName = new byte[BitConverter.ToInt32(buffer, 0)];
          // 상태를 초기화	
          buffer = new byte[1];
          state = State.STATE;
        }
        // 파일 이름	
        else if (state == State.FILENAME)
        {
          // 다운 받은 데이터를 FileName 변수로 옮긴다.	
          Array.Copy(buffer, 0, FileName, seek, size);
          // 받은 만큼 위치 옮긴다.	
          seek += size;
          // 위치와 파일 이름 크기가 같으면 종료	
          if (seek >= FileName.Length)
          {
            // 상태를 초기화	
            buffer = new byte[1];
            state = State.STATE;
          }
        }
        // 파일 크기	
        else if (state == State.FILESIZE)
        {
          // 데이터를 int형으로 변환(Bigendian)해서 파일 Binary를 할당한다.	
          Binary = new byte[BitConverter.ToInt32(buffer, 0)];
          // 상태를 초기화	
          buffer = new byte[1];
          state = State.STATE;
        }
        // 파일 다운로드	
        else if (state == State.FILEDOWNLOAD)
        {
          // 다운 받은 데이터를 FileName 변수로 옮긴다.	
          Array.Copy(buffer, 0, Binary, seek, size);
          // 받은 만큼 위치 옮긴다.	
          seek += size;
          // 위치와 파일 크기가 같으면 종료	
          if (seek >= Binary.Length)
          {
            var filepath = SaveDirectory + Encoding.UTF8.GetString(FileName);
            // 파일의 디렉토리를 확인한다.
            var subDir = Path.GetDirectoryName(filepath);
            // 디렉토리가 없으면
            if (!Directory.Exists(subDir))
            {
              // 디렉토리 생성
              Directory.CreateDirectory(subDir);
            }
            // IO를 이용해서 binary를 파일로 저장한다.	
            using (var stream = new FileStream(filepath, FileMode.Create, FileAccess.Write))
            {
              // 파일 쓰기	
              stream.Write(Binary, 0, Binary.Length);
            }
            // 콘솔 출력	
            Console.WriteLine($"Download file - ${SaveDirectory + Encoding.UTF8.GetString(FileName)}");
            // 접속을 끊는다. (이번에는 접속을 끊지 않고 상태를 초기화 한다.)
            //this.socket.Disconnect(false);
            //this.socket.Close();
            //this.socket.Dispose();
            //return;
            // 상태를 초기화	
            buffer = new byte[1];
            state = State.STATE;
          }
        }
        // buffer로 메시지를 받고 Receive함수로 메시지가 올 때까지 대기한다.	
        this.socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, Receive, this);
      }
    }
  }
  // 메인 Program은 Socket을 상속받고 서버 Socket으로 사용한다.	
  class Program : Socket
  {
    // 생성자	
    public Program() : base(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
    {
      // 포트는 10000을 Listen한다.	
      base.Bind(new IPEndPoint(IPAddress.Any, 10000));
      // 서버 대기	
      base.Listen(10);
      // 비동기 소켓으로 Accept 클래스로 대기한다.	
      BeginAccept(Accept, this);
    }
    // 클라이언트가 접속하면 함수가 호출된다.	
    private void Accept(IAsyncResult result)
    {
      // EndAccept로 접속 Client Socket을 받는다. EndAccept는 대기를 끝나는 것이다.	
      // Client 클래스를 생성한다.	
      var client = new Client(EndAccept(result));
      // 비동기 소켓으로 Accept 클래스로 대기한다.	
      BeginAccept(Accept, this);
    }
    // 프로그램 시작 함수	
    static void Main(string[] args)
    {
      // Program 클래스를 실행한다.	
      new Program();
      // q키를 누르면 서버는 종료한다.	
      Console.WriteLine("Press the q key to exit.");
      while (true)
      {
        string k = Console.ReadLine();
        if ("q".Equals(k, StringComparison.OrdinalIgnoreCase))
        {
          break;
        }
      }
    }
  }
}

위 소스는 기존 파일을 받는 소스에서 접속을 끊고 초기화하는 부분이랑 디렉토리가 없으면 디렉토리를 생성하는 부분만 수정되었습니다.

나머지는 완전히 똑같습니다.


이번에는 클라이언트입니다.

클라이언트는 소스 수정이 많이 있습니다.

using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace FileClient
{
  class Program
  {
    // 파일 전송 함수
    static void Send(Socket client, string name, string filename)
    {
      // FileInfo 생성	
      var file = new FileInfo(filename);
      // 파일이 존재하는지	
      if (file.Exists)
      {
        // 바이너리 버퍼	
        var binary = new byte[file.Length];
        using (var stream = new FileStream(file.FullName, FileMode.Open, FileAccess.Read))
        {
          // 파일을 IO로 읽어온다.	
          stream.Read(binary, 0, binary.Length);
          // 상태 0 - 파일 이름 크기를 보낸다.	
          client.Send(new byte[] { 0 });
          // 송신 - 파일 이름 크기 Bigendian	
          client.Send(BitConverter.GetBytes(name.Length));
          // 상태 1 - 파일 이름 보낸다.	
          client.Send(new byte[] { 1 });
          // 송신 - 파일 이름 	
          client.Send(Encoding.UTF8.GetBytes(name));
          // 상태 2 - 파일 크기를 보낸다.	
          client.Send(new byte[] { 2 });
          // 송신 - 파일 크기 Bigendian	
          client.Send(BitConverter.GetBytes(binary.Length));
          // 상태 3 - 파일를 보낸다.	
          client.Send(new byte[] { 3 });
          // 송신 - 파일	
          client.Send(binary);
        }
      }
      else
      {
        // 콘솔 출력	
        Console.WriteLine("The file is not exists. - " + filename);
      }
    }
    // 파일 리스트를 가져온다.
    static List<String> GetFileList(String rootPath, List<String> fileList)
    {
      if (fileList == null)
      {
        return null;
      }
      var attr = File.GetAttributes(rootPath);
      // 해당 path가 디렉토리이면
      if ((attr & FileAttributes.Directory) == FileAttributes.Directory)
      {
        var dirInfo = new DirectoryInfo(rootPath);
        // 하위 모든 디렉토리는
        foreach (var dir in dirInfo.GetDirectories())
        {
          // 재귀로 통하여 list를 취득한다.
          GetFileList(dir.FullName, fileList);
        }
        // 하위 모든 파일은
        foreach (var file in dirInfo.GetFiles())
        {
          // 재귀를 통하여 list를 취득한다.
          GetFileList(file.FullName, fileList);
        }
      }
      // 해당 path가 파일이면 (재귀를 통해 들어온 경로)
      else
      {
        var fileInfo = new FileInfo(rootPath);
        // 리스트에 full path를 저장한다.
        fileList.Add(fileInfo.FullName);
      }
      return fileList;
    }
    // 실행 함수	
    static void Main(string[] args)
    {
      // 업로드할 디렉토리
      var sourcePath = @"D:\sourcework";
      // 서버에 접속한다.	
      var ipep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 10000);
      // 소켓 생성	
      using (Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
      {
        // 접속	
        client.Connect(ipep);
        // 디렉토리의 정보를 취득하기
        foreach (var file in GetFileList(sourcePath, new List<string>()))
        {
          // 서버로 파일을 전송하기
          Send(client, file.Substring(sourcePath.Length + 1), file);
        }
      }
      // 아무 키나 누르면 종료	
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}

먼저 Main에서 이전에는 파일을 읽고 와서 소켓에 접속을 했는데 이번에는 먼저 소켓 접속을 먼저 했습니다.

그리고 업로드할 디렉토리 정보를 가져오고 파일 리스트를 하나하나 서버로 전송을 합니다. Send 함수에서 FileStream을 사용하기 때문에 파일 물리 주소와 실제 서버로 전송할 상대 주소를 분리해서 보내야 합니다.

(클래스화 하면 간단합니다만.. 귀찮네요....ㅠㅠ)


테스트 폴더는 이전과 같습니다.

서버를 실행시킵니다.

그리고 클라이언트를 실행합니다.

이제 서버에서는 접속이 되었는지 확인을 합니다.

제대로 접속을 한 것 같습니다. 코드 상에도 에러가 발생하지 않았습니다.


d:/work 폴더를 확인해 보겠습니다.

역시 디렉토리가 전송이 된 것을 확인할 수 있습니다.


방식은 두가지가 있습니다만 개인적으로 성능을 생각한다면 압축을 해서 보내는 것이 훨씬 좋겠습니다. 그러나 내부에 여러가지 제약(사양)이 있다면 가령, 파일 이름 또는 파일의 정보에 따라 업로드를 바꾼다거나 이름을 바꿔야 한다면 압축이 아닌 파일 하나하나 보내는 방식이 더 좋겠네요.

그건 사양에 따라 맞추어 사용하시면 됩니다.


여기까지 C#에서 소켓 통신을 이용해서 다수의 파일(폴더)를 전송하는 방법에 대한 글이었습니다.


궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.