SQLite를 활용한 TCP정보분배 데이타 수신및 저장

> SQLite를 활용한 TCP정보분배 데이타 수신및 저장

 

> 위의 프로그램에서 생성되어진 데이타베이스 파일을 상용SQLite DB Browser에서 열기(서로 파일이 호환됨)

SQLite는 MySQL나 PostgreSQL와 같은 데이터베이스 관리 시스템이지만, 서버가 아니라 응용 프로그램에 넣어 사용하는 비교적 가벼운 데이터베이스이다. 
일반적인 RDBMS에 비해 대규모 작업에는 적합하지 않지만, 중소 규모라면 속도에 손색이 없다. 또 API는 단순히 라이브러리를 호출하는 것만 있으며, 데이터를 저장하는 데 하나의 파일만을 사용하는 것이 특징이다. 버전 3.3.8에서는 풀텍스트 검색 기능을 가진 FTS1 모듈이 지원된다. 컬럼을 삭제하거나 변경하는 것 등이 제한된다.
구글 안드로이드 운영 체제에 기본 탑재된 데이터베이스이기도 하다.

RDBMS( oracle, mssql, sysbase, mysql, mariadb 등)와 동일하게 ansi sql을 사용할 수 있다.
다른 RDBMS와의 가장 큰 차이점은 아래와 같다.
데이터베이스별로 파일1개로 구성된다.
로컬에 파일로 저장하므로 원격접속이 안된다. 
( 웹서버와 DB서버를 별도로 구성 X, 다른 대안은 찾아보면 있다. 다른아이피의 PC를 마운트 해서 사용한다던가..등)
접속아이디나 패스워드가 없다.
다중 접속이 많아질 경우 예기치 않은 문제가 발생할 수 있다.
구글 안드로이드 운영 체제에 기본 탑재되어 있어 앱을 만들어본 경험이 있다면, 누구나 한번쯤은 사용해보고 들어봤을 것이다.
윈도우PC에서도 설치하여 사용할 수 있으며, 간단한 웹페이지를 구현할때도 별도의 설치과정이 없이 실행파일만 있으면 바로 사용할 수 있다.


1. SQLite 다운로드
http://www.sqlite.org/download.html 에 접속하여 sqllite-tools-win32-3340000.zip 파일을 다운로드 받는다.

2. 압축해제 후 sqlite3.exe 실행
적당한 곳(ex. C:\SQLite)에 압축을 푼 다음 sqlite.exe파일을 실행한다.
간혹 아래와 같이 Windows의 PC 보호라고 알림 창이 뜰 경우 "추가 정보"를 클릭 후 나타나는 "실행" 버튼을 클릭하면 된다.

3. sqlite 명령프롬프트
실행하면 윈도우 명령 프롬프트가 실행되면서 sqlite 상태로 들어간다.

5. 데이터베이스 생성
실행했으니 데이터베이스를 하나 만들고 테이블도 하나 만들어서 값을 입력하고 결과를 조회해보자

데이터베이스를 만드는 법은 2가지가 있는데, 명령 프롬프트에서 sqlite3를 실행하면서 만드는 방법과 sqlite3 안에 들어가서 만드는 방법이 있다.
방법1. sqlite3 실행과 동시에 만들기

6. 생성된 데이터베이스에 테이블 만들기
일반적인 DDL 문법으로 TEST_TABLE 이라는 테이블을 만들고 .table 명령으로 테이블 목록을 조회

7. 생성된 테이블에 데이터 넣고 조회 해보기

8. GUI 툴을 사용하여 편하게 입력한다.
  sqlitebrowser.org/dl/


개인작업내용)
SQLite를 사용하여 C#에서 테이블을 생성하고 데이터를 입력 및 조회하는 예제를 아래에 제공하겠습니다. 이 예제에서는 System.Data.SQLite를 사용합니다.

먼저, NuGet 패키지 관리자를 통해 System.Data.SQLite 패키지를 설치해야 합니다.
먼저, NuGet 패키지 관리자를 통해 System.Data.SQLite 패키지를 설치해야 합니다.

BUILD & ALL
$ find ./ -name "*Interop*.dll"
./frm/WinFrmTicker/packages/Microsoft.Office.Interop.Excel.15.0.4795.1001/lib/net20/Microsoft.Office.Interop.Excel.dll
./frm/WinFrmTicker/packages/Microsoft.Office.Interop.Excel.15.0.4795.1001/lib/netstandard2.0/Microsoft.Office.Interop.Excel.dll
./frm/WinFrmTicker/packages/MSOffice.Interop.16.0.55555/lib/Microsoft.Office.Interop.Access.Dao.dll
./frm/WinFrmTicker/packages/MSOffice.Interop.16.0.55555/lib/Microsoft.Office.Interop.Access.dll
./frm/WinFrmTicker/packages/MSOffice.Interop.16.0.55555/lib/Microsoft.Office.Interop.Excel.dll
./frm/WinFrmTicker/packages/MSOffice.Interop.16.0.55555/lib/Microsoft.Office.Interop.Visio.WorkflowAuthoring.dll
./frm/WinFrmTicker/WinFrmTicker/bin/Debug/Microsoft.Office.Interop.Visio.WorkflowAuthoring.dll
./frm/WinFrmTicker/WinFrmTicker/bin/Release/Microsoft.Office.Interop.Visio.WorkflowAuthoring.dll
./mdifrm/packages/Stub.Systehttp://m.Data.SQLite.Core.NetFramework.1.0.118.0/build/net20/x64/SQLite.Interop.dll
./mdifrm/packages/Stub.Systehttp://m.Data.SQLite.Core.NetFramework.1.0.118.0/build/net20/x86/SQLite.Interop.dll

dotnet40에서, 64비트)
dotnet40에서, 64비트)

./mdifrm/packages/Stub.Systehttp://m.Data.SQLite.Core.NetFramework.1.0.118.0/build/net40/x64/SQLite.Interop.dll

./mdifrm/packages/Stub.Systehttp://m.Data.SQLite.Core.NetFramework.1.0.118.0/build/net40/x86/SQLite.Interop.dll

dotnet45에서 , 64비트)
dotnet45에서 , 64비트)

./mdifrm/packages/Stub.Systehttp://m.Data.SQLite.Core.NetFramework.1.0.118.0/build/net45/x64/SQLite.Interop.dll



./mdifrm/packages/Stub.Systehttp://m.Data.SQLite.Core.NetFramework.1.0.118.0/build/net45/x86/SQLite.Interop.dll
./mdifrm/packages/Stub.Systehttp://m.Data.SQLite.Core.NetFramework.1.0.118.0/build/net451/x64/SQLite.Interop.dll
./mdifrm/packages/Stub.Systehttp://m.Data.SQLite.Core.NetFramework.1.0.118.0/build/net451/x86/SQLite.Interop.dll
./mdifrm/packages/Stub.Systehttp://m.Data.SQLite.Core.NetFramework.1.0.118.0/build/net46/x64/SQLite.Interop.dll
./mdifrm/packages/Stub.Systehttp://m.Data.SQLite.Core.NetFramework.1.0.118.0/build/net46/x86/SQLite.Interop.dll
./mdifrm/WINCalcMdi/bin/Release/x64/SQLite.Interop.dll
./mdifrm/WINCalcMdi/bin/Release/x86/SQLite.Interop.dll



1. SQLite 데이터베이스 및 테이블 생성

using System;
using Systehttp://m.Data.SQLite;

class Program
{
    static void Main()
    {
        string connectionString = "Data Source=sample.db;Version=3;";

        using (SQLiteConnection connection = new SQLiteConnection(connectionString))
        {
            connection.Open();

            string createTableQuery = @"
                CREATE TABLE IF NOT EXISTS users (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    name TEXT NOT NULL,
                    age INTEGER NOT NULL
                )";

            using (SQLiteCommand command = new SQLiteCommand(createTableQuery, connection))
            {
                command.ExecuteNonQuery();
            }

            connection.Close();
        }

        Console.WriteLine("Table created successfully.");
    }
}

2. 데이터 입력

using System;
using Systehttp://m.Data.SQLite;

class Program
{
    static void Main()
    {
        string connectionString = "Data Source=sample.db;Version=3;";

        using (SQLiteConnection connection = new SQLiteConnection(connectionString))
        {
            connection.Open();

            string insertQuery = "INSERT INTO users (name, age) VALUES (@name, @age)";

            using (SQLiteCommand command = new SQLiteCommand(insertQuery, connection))
            {
                command.Parameters.AddWithValue("@name", "Alice");
                command.Parameters.AddWithValue("@age", 30);
                command.ExecuteNonQuery();
            }

            connection.Close();
        }

        Console.WriteLine("Data inserted successfully.");
    }
}

3. 데이터 조회

using System;
using Systehttp://m.Data.SQLite;

class Program
{
    static void Main()
    {
        string connectionString = "Data Source=sample.db;Version=3;";

        using (SQLiteConnection connection = new SQLiteConnection(connectionString))
        {
            connection.Open();

            string selectQuery = "SELECT * FROM users";

            using (SQLiteCommand command = new SQLiteCommand(selectQuery, connection))
            {
                using (SQLiteDataReader reader = command.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        Console.WriteLine("ID: " + reader["id"]);
                        Console.WriteLine("Name: " + reader["name"]);
                        Console.WriteLine("Age: " + reader["age"]);
                        Console.WriteLine();
                    }
                }
            }

            connection.Close();
        }
    }
}

전체 코드 예제

using System;
using Systehttp://m.Data.SQLite;

class Program
{
    static void Main()
    {
        string connectionString = "Data Source=sample.db;Version=3;";

        // 테이블 생성
        using (SQLiteConnection connection = new SQLiteConnection(connectionString))
        {
            connection.Open();

            string createTableQuery = @"
                CREATE TABLE IF NOT EXISTS users (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    name TEXT NOT NULL,
                    age INTEGER NOT NULL
                )";

            using (SQLiteCommand command = new SQLiteCommand(createTableQuery, connection))
            {
                command.ExecuteNonQuery();
            }

            connection.Close();
        }

        Console.WriteLine("Table created successfully.");

        // 데이터 입력
        using (SQLiteConnection connection = new SQLiteConnection(connectionString))
        {
            connection.Open();

            string insertQuery = "INSERT INTO users (name, age) VALUES (@name, @age)";

            using (SQLiteCommand command = new SQLiteCommand(insertQuery, connection))
            {
                command.Parameters.AddWithValue("@name", "Alice");
                command.Parameters.AddWithValue("@age", 30);
                command.ExecuteNonQuery();
            }

            connection.Close();
        }

        Console.WriteLine("Data inserted successfully.");

        // 데이터 조회
        using (SQLiteConnection connection = new SQLiteConnection(connectionString))
        {
            connection.Open();

            string selectQuery = "SELECT * FROM users";

            using (SQLiteCommand command = new SQLiteCommand(selectQuery, connection))
            {
                using (SQLiteDataReader reader = command.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        Console.WriteLine("ID: " + reader["id"]);
                        Console.WriteLine("Name: " + reader["name"]);
                        Console.WriteLine("Age: " + reader["age"]);
                        Console.WriteLine();
                    }
                }
            }

            connection.Close();
        }
    }
}

이 코드는 SQLite 데이터베이스를 생성하고, 테이블을 생성한 후, 데이터를 입력하고 조회하는 예제입니다. SQLite 데이터베이스 파일(sample.db)이 실행 디렉토리에 생성됩니다.


(정보분배)TCP로 받은 증권데이타를 파싱해서 DataGridView에 표시해봅니다.



1. SDI 방식
2. MDI 방식

1. 데이타의 정합성체크(소수점등)에 활용될수 있다.
2. *.db 파일에 저장되므로, 메모리의 크기에 대한 걱정을 안해도 된다.(무한정 데이타룰 수신가능)


접속표준서(정보분배-UDP_TCP실시간)에 정의되어진 콤마가 있는항목을 따로 표시해 봅니다.
누적거래량은 콤마3으로 표시되어지므로, ".000" 부분을 잘라서 사용해야 합니다.
"."까지 포함되어졌으므로, 4자리를 잘라서 사용해야 할것 같습니다.(주의할점)

 

TCP로 받은 호가데이타를 파싱해서 DataGridView에 표시해봅니다.

통합본)

public class PublicNM
{

     public const int WM_KEYDOWN = 0x0100;

    // FindWindow를 정적 메서드로 선언합니다.
    public static IntPtr FindWindow(string lpClassName, string lpWindowName)
    {
        // 실제 FindWindow 구현은 생략
        return IntPtr.Zero; 
    }
}

사용예)

         // 정적 메서드에 접근할 때는 클래스 이름을 사용합니다.
         IntPtr hWnd = PublicNM.FindWindow("ClassName", "WindowName");

        protected override bool ProcessCmdKey(ref Message message, Keys keyData)
        {
            switch (message.Msg)
            {
                case PublicNM.WM_KEYDOWN:
                    break;
                default:
                    break;
            }
            return base.ProcessCmdKey(ref message, keyData);
        }

증권정보,ㅡㅡ,ㅡㅡ packet send/recv in windows os

exture_send_basic.c
0.00MB
dirent.h
0.01MB
exture_recv_basic.c
0.00MB

 

 

c#/windows.pre.foundation/원리금균등상환.금리비교시.대출상환포함
c#/windows.pre.foundation/원리금균등상환.금리비교시.대출상환포함

Install Win.Form)



FiancialNM.msi
2.05MB


Install Win.Pre.Foundation)

FiancialCalc.msi
1.38MB



0. 메모장에서 코딩후 빌드 & 런

1. Win Form과 Win Presentaion Foundation과의 비교화면)

 

 


2. 대출상환정보가 없을경우)

3. 대출상환정보가 있을경우)

 

4. 대출상환정보가 있을경우>데이타선택시에, 해당월 일별 데이타 리스트출력

( ChatGPT )c# List 와 배열의 차이점

C#에서 List와 배열(Array)의 주요 차이점은 다음과 같습니다:

  1. 크기 조절 가능 여부:
    • 배열(Array): 배열은 선언된 크기를 초과하거나 축소할 수 없습니다. 한 번 생성되면 크기가 고정됩니다.
    • List: List는 동적으로 크기가 조절될 수 있습니다. 요소를 추가하거나 제거할 때 List 내부에서 자동으로 크기가 조절됩니다.
  2. 메서드와 기능:
    • 배열(Array): 배열은 .NET Framework에서 제공하는 여러 메서드와 기능이 있지만, 크기 조절이나 요소 추가/삭제에 제한이 있습니다. 예를 들어, Array 클래스는 Length 속성을 통해 배열의 길이를 확인할 수 있습니다.
    • List: List는 배열보다 다양한 메서드와 기능을 제공합니다. 예를 들어, 요소 추가/삭제를 위한 Add, Remove, Insert 메서드 등이 있습니다. 또한 LINQ 쿼리와 함께 사용할 수 있는 다양한 확장 메서드를 사용할 수 있습니다.
  3. 선언 및 초기화:
    • 배열(Array): 배열은 고정된 크기를 선언하고 초기화해야 합니다. 예를 들어, int[] numbers = new int[5];와 같이 선언하고 초기화합니다.
    • List: List는 크기를 선언하지 않고 생성할 수 있으며, 초기 요소를 포함할 수도 있습니다. 예를 들어, List<int> numbers = new List<int>();로 선언하고 초기화합니다.
  4. 타입 안정성:
    • 배열(Array): 배열은 특정 데이터 형식으로 타입 안전성을 보장합니다. 즉, 선언된 데이터 형식 이외의 데이터를 저장할 수 없습니다.
    • List: List는 제네릭 형식으로 제공되며, 특정 데이터 형식에 대한 타입 안정성을 보장합니다.

이러한 차이점으로 인해 상황에 따라 배열이나 List 중 하나를 선택하여 사용할 수 있습니다. 배열은 크기가 고정된 정적 데이터에 적합하고, List는 동적인 데이터 구조에 적합합니다.

ChatGPT)
C#에서 public string SpecificPay { get; set; }와 public string SpecificPay;의 차이는 다음과 같습니다:

1. public string SpecificPay { get; set; }

public string SpecificPay { get; set; }: 이것은 C#의 속성(Property)입니다. 속성은 클래스의 멤버로, 해당 속성에 대한 값을 캡슐화하고, 접근을 제어하며, 필요한 경우 추가 로직을 적용할 수 있습니다. 이 특정 속성은 읽기 및 쓰기가 가능합니다(getter와 setter 모두가 존재함).

2. public string SpecificPay;

public string SpecificPay;: 이것은 클래스의 필드(Field)입니다. 필드는 해당 클래스의 인스턴스마다 각각의 값을 저장합니다. 이것은 간단한 데이터 저장 용도로 사용되며, 특별한 접근 로직이나 제한이 없습니다. 직접적으로 필드에 접근하여 값을 설정하거나 가져올 수 있습니다.


따라서, 첫 번째 선언은 읽기 및 쓰기가 가능한 속성을 정의하고, 두 번째 선언은 클래스의 필드를 정의합니다.

 

 

TCP 증권데이타 수신및 송신의 원리를 위의 그림을 보면서 설명한다.

1. TCP증권데이타는 한국거래소에서 계약되어진 회원사에 일방적으로 전송한다.
2. 회원사는 A,B,C라고 3개로 예를 든다.
3. 회원사 A,B,C는 수신받는 엔진을 보일러로 표시하고 보일러로 설명해 본다.
4. 회원사는 A,B,C는 집의 규모가 틀리다. 물론 보일러도 처리용량에 차이가 있다. 좋은 보일러, 혹은 그외.
5. TCP증권데이타를 물로 예를들면, 물을 공급해서 A,B,C가 보일러로 처리하는 도중에 보일러에 문제가 생기면한국거래소에서는 물공급을 중단한다. 그리고 해당 회원사에 통보한다.
6. 계속 입수되는 물을 보일러에서 잘 처리되도록 회원사는 A,B,C는 보일러를 각각 잘 관리해야 한다.

Practice)
20,000 바이트를 보낸다면, 각각 회원사는 A,B,C는 할당되어진 버퍼만큼 받을수 있다.
A - 10,000 + 10,000 = 20,000 두번에 걸쳐서 받는다.
B - 5,000 + 5,000 + 5,000 + 5,000 = 20,000 네번에 걸쳐서 받는다.
C - 20,000 한번에 받는다.

20,000 바이트에는 보내는 데이타종류가 15개가 들어있다고 치자.(체결,호가등등)
A - 10,000 + 10,000 = 20,000 두번에 걸쳐서 받이서 데이타종류가 15개를 추출해야 한다.
B - 5,000 + 5,000 + 5,000 + 5,000 = 20,000 네번에 걸쳐서 받이서 데이타종류가 15개를 추출해야 한다.
C - 20,000 한번에 받이서 데이타종류가 15개를 추출해야 한다.

결국 회원사는 A,B,C는 관으로 표현하자면, 넓은 관을 통해서 받던지, 좁은 관을 통해서 받던지, 결국은 받아서 데이타종류가 15개를 추출해야 한다.

결과화면)


(CHAT.GPT)
WPF에서 메인 윈도우에서 클래스를 호출하고, 해당 클래스에서 다이얼로그를 메인 윈도우의 가운데에 위치하도록 호출하려면, 다음과 같은 절차를 따르면 됩니다.

  1. 메인 윈도우에서 클래스 호출
  2. 클래스에서 다이얼로그 호출 시 메인 윈도우를 기준으로 위치 설정



1. MainWindow.xaml.cs

메인 윈도우에서 클래스를 호출하는 버튼 클릭 이벤트를 설정합니다.

using System.Windows;

namespace WpfApp
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void OpenDialogFromClassButton_Click(object sender, RoutedEventArgs e)
        {
            DialogHelper helper = new DialogHelper();
            helper.ShowDialog(this);
        }
    }
}

2. DialogHelper.cs

다이얼로그를 표시할 클래스입니다.

using System.Windows;

namespace WpfApp
{
    public class DialogHelper
    {
        public void ShowDialog(Window owner)
        {
            DialogWindow dialog = new DialogWindow
            {
                Owner = owner,
                WindowStartupLocation = WindowStartupLocation.CenterOwner
            };
            dialog.ShowDialog();
        }
    }
}

3. DialogWindow.xaml

다이얼로그 윈도우의 XAML 파일입니다.

<Window x:Class="WpfApp.DialogWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="DialogWindow"
        Height="200"
        Width="400">
    <Grid>
        <!-- 다이얼로그의 UI 요소들 -->
    </Grid>
</Window>

4. DialogWindow.xaml.cs

다이얼로그 윈도우의 코드 비하인드 파일입니다.

using System.Windows;

namespace WpfApp
{
    public partial class DialogWindow : Window
    {
        public DialogWindow()
        {
            InitializeComponent();
        }
    }
}

5. MainWindow.xaml

메인 윈도우의 XAML 파일에서 클래스를 호출하여 다이얼로그를 여는 버튼을 추가합니다.

<Window x:Class="WpfApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow"
        Height="450"
        Width="800">
    <Grid>
        <Button Content="Open Dialog from Class" Click="OpenDialogFromClassButton_Click" HorizontalAlignment="Center" VerticalAlignment="Center"/>
    </Grid>
</Window>

요약

이 구조에서는 DialogHelper 클래스가 메인 윈도우의 인스턴스를 인자로 받아 다이얼로그를 중앙에 위치하도록 설정합니다. 이렇게 하면 다이얼로그 윈도우가 항상 메인 윈도우의 중앙에 표시됩니다.






아래검색파일에서 Value="14"를 Value="10"으로 변경하고자, 구글링하려고 했으나, (CHAT.GPT)를 이용하니, 바로 리눅스명령어 알려줌
아래검색파일에서 Value="14"를 Value="10"으로 변경하고자, 구글링하려고 했으나, (CHAT.GPT)를 이용하니, 바로 리눅스명령어 알려줌


xterm@DESKTOP-VVAIF4D MINGW64 ~/Downloads/tmp/WindowsPresenFoundation/WINPAYCalcM
$ find ./ -name "*.xaml" | xargs grep FontSize | grep Value=
./BasicInfoDialog.xaml:            <Setter Property="FontSize" Value="14"/>
./BasicInfoDialog.xaml:            <Setter Property="FontSize" Value="14"/>
./BasicInfoDialog.xaml:            <Setter Property="FontSize" Value="14"/>
./FileListNM.xaml:        <Setter Property="FontSize" Value="14"/>
./FileListNM.xaml:        <Setter Property="FontSize" Value="14"/>
./FileListNM.xaml:        <Setter Property="FontSize" Value="14"/>
./MainWindow.xaml:            <Setter Property="FontSize" Value="10"/>
./SpecificDialog.xaml:        <Setter Property="FontSize" Value="14"/>
./SpecificDialog.xaml:        <Setter Property="FontSize" Value="14"/>
./SpecificDialog.xaml:        <Setter Property="FontSize" Value="14"/>

xterm@DESKTOP-VVAIF4D MINGW64 ~/Downloads/tmp/WindowsPresenFoundation/WINPAYCalcM
$ sed -i 's/Value="10"/Value="11"/g' ./BasicInfoDialog.xaml ./FileListNM.xaml ./SpecificDialog.xaml

xterm@DESKTOP-VVAIF4D MINGW64 ~/Downloads/tmp/WindowsPresenFoundation/WINPAYCalcM
$ find ./ -name "*.xaml" | xargs grep FontSize | grep Value=
./BasicInfoDialog.xaml:            <Setter Property="FontSize" Value="10"/>
./BasicInfoDialog.xaml:            <Setter Property="FontSize" Value="10"/>
./BasicInfoDialog.xaml:            <Setter Property="FontSize" Value="10"/>
./FileListNM.xaml:        <Setter Property="FontSize" Value="10"/>
./FileListNM.xaml:        <Setter Property="FontSize" Value="10"/>
./FileListNM.xaml:        <Setter Property="FontSize" Value="10"/>
./MainWindow.xaml:            <Setter Property="FontSize" Value="10"/>
./SpecificDialog.xaml:        <Setter Property="FontSize" Value="10"/>
./SpecificDialog.xaml:        <Setter Property="FontSize" Value="10"/>
./SpecificDialog.xaml:        <Setter Property="FontSize" Value="10"/>

xterm@DESKTOP-VVAIF4D MINGW64 ~/Downloads/tmp/WindowsPresenFoundation/WINPAYCalcM
$

 

접속표준서(TCP) 공통정보(송신채널_정보구분)

 

접속표준서(TCP) 공통정보(송신채널_정보구분)
1.
- 그룹별로 3,4개의 포트로 구성됨.
- 수신사는(예를 들어 네이버, 팍스넷, 싱크풀등등) 데이타를 수신하는 서버프로그램을 작성해야 한다.


2.
- 보통 포트별로 한개의 서버가 1:1로 통신하는 방식을 취한다.
- 한번 맺은 세션은 끊어지지 않는다. 즉 하나의 포트를 담당하는 수신서버는 Accept를 한번 하는 꼴이다.
- 한번 맺은 세션은 끊어지지 않는다. Accept후에 데이타를 수신해서 데이타를 파싱하는 로직이 들어가면 된다.
- 한번 맺은 세션은 끊어지지 않는다. Accept후에 데이타를 수신하는 과정에서, 패킷은 헤더와 테일은 존재하지 않는다.
- 한번 맺은 세션은 끊어지지 않는다. Accept후에 데이타를 수신하는 과정에서, 데이타를 구분짓는 구분자 0xff만 존재
- 구체적으로 10,000바이트를 받았다면 여러개의 정보데이타를 끊어서 저장해야한다.
- 정보데이타의 길이정보를 이용해서 수신받는 데이타를 분리한다면, 제도변경시에 좋지 않는 방법이다.
- Ack가 없는 관계의 TCP 수신,송신방법이다.
- 송신하는쪽도 일방적으로 데이타를 보낸다. 수신하는쪽이 잘 받았는지 못받았는지는 신경쓰지 않는다.
(마치 UDP처럼)
- 수신하는쪽도 송신하는쪽에 Ack신호를 보내지 않는다. 무조건 데이타를 받기만한다.
- 만약에 수신쪽의 서버나 네트웍이 불안해서 송신데이타를 보내는쪽에서 프로그램이 죽는다면, ㅠㅠㅠㅠ(?)
- 만약에 수신쪽의 서버나 네트웍이 불안해서 송신데이타를 보내는쪽에서 프로그램이 죽는다면, ㅠㅠㅠㅠ(?)
- 만약에 수신쪽의 서버나 네트웍이 불안해서 송신데이타를 보내는쪽에서 프로그램이 죽는다면, ㅠㅠㅠㅠ(?)
(절대 그렇게 설계를 해서는 안되지만, 장애로 분리되겠다.)

- UDP는 송신쪽에서 보내기만 할뿐, 수신쪽과 상관없기떄문에, 송신쪽 프로그램이 죽는경우는 발생하지 않는다.
(즉 수신쪽의 프로그램의 정상여부를 알수가 없다. 하지만 TCP는 알수 있기떄문에 통보해줄수 있다.)


3.
- 구분해서 분리되어진 정보데이타패킷은 저장하는 방법으로 여러가지가 있을것이다.(메모리,디비,메모리디비,파일등등)
- netstat -an | grep Listen 으로 명령어를 실행하면 수신 포트갯수만큼 나올것이다.
- netstat -an | grep ESTABLISHED 으로 명령어를 실행하면 수신 포트갯수만큼 나올것이다.


A301S        G140KR7395400005001491150011499628200000000005000000050000000000003000000049950000000503000000004970000000169022000000000844538870.0001000000000000000000000       0502000000005000 0xff
A301S        G140KR7018250001000603150011505463200000001050000000230500000000002000000222000000002370000000021450000000348867000000007940610250.0001000000000000000000000       2325000000023050 0xff
A301S        G140KR7042670000000710150011509142500000000280000000091000000000022000000093800000000940000000009070000003129543000000028712498060.0001000000000000000000000       0911000000009100 0xff
A301S        G140KR7010130003000492150011516616500000011000000005450000000000001000005560000000055600000000542000000000040379000000022063218000.0001000000000000000000005       4600000000545000 0xff
A301S        G140KR7074610007000767150011516845500000001580000000142100000000070000000162500000001630000000014170000011525661000000171499537320.0002000000000000000000000       1421000000014200 0xff
A301S        G140KR7042670000000710150011519030500000000280000000091000000000007000000093800000000940000000009070000003129550000000028712561760.0001000000000000000000000       0911000000009100 0xff
A301S        G140KR7015260003000574150011521442200000000060000000024800000000001000000024250000000260000000002310000002374822000000005909336090.0001000000000000000000000       0249000000002480 0xff
A301S        G140KR7005070008000344150011521734500000000900000001828000000000001000001840000000018600000000176000000000445860000000080994328200.0002000000000000000000001       8280000000182700 0xff
A301S        G140KR7042670000000710150011529108500000000280000000091000000000006000000093800000000940000000009070000003129556000000028712616360.0001000000000000000000000       0911000000009100 0xff
A301S        G140KR7285130001001229150011534317500000000600000000781000000000028000000781000000007870000000076800000000074168000000005762897800.0001000000000000000000000       7820000000078100 0xff
A301S        G140KR7018670000000607150011534410500000000500000001208000000000001000001217000000012170000000118800000000007102000000000849499600.0002000000000000000000001       2080000000120700 0xff
A301S        G140KR7042670000000710150011539763500000000280000000091000000000007000000093800000000940000000009070000003129563000000028712680060.0001000000000000000000000       0911000000009100 0xff
A301S        G140KR7069260008000748150011545434200000000550000000235000000000037000000230500000002415000000022800000000472597000000011151020750.0001000000000000000000000       2355000000023450 0xff
A301S        G140KR7285130001001229150011545707500000000600000000781000000000016000000781000000007870000000076800000000074184000000005764147400.0001000000000000000000000       7820000000078100 0xff
A301S        G140KR7323410001001332150011549230500000000150000000236500000000001000000238500000002400000000023500000001015103000000024082797850.0002000000000000000000000       2365000000023600 0xff
A301S        G140KR7042670000000710150011549573500000000280000000091000000000032000000093800000000940000000009070000003129595000000028712971260.0001000000000000000000000       0911000000009100 0xff
A301S        G140KR7025820002000645150011558337200000000190000000054500000000001000000054100000000553000000005060000007489311000000039879368230.0001000000000000000000000       0546000000005450 0xff
A301S        G140KR7042670000000710150011560718500000000280000000091000000000015000000093800000000940000000009070000003129610000000028713107760.0001000000000000000000000       0911000000009100 0xff
A301S        G140KR7042670000000710150011572588500000000280000000091000000000020000000093800000000940000000009070000003129630000000028713289760.0001000000000000000000000       0911000000009100 0xff

 

1. 기존 Windows Form방식)
- 기존 Form방식에서 사용되어진 Form 화면디자인을 WPF방식에는 이용할수 없다는점이 아쉽다.
- Form과 WPF를 동시에 코딩을 원한다면, 화면과 로직을 분리하는 설계를 잘해야한다.
- 즉 Form Class에 로직코드에 해당되는 부분은 클래스파일을 따로 만들어서 작성하도록 한다.

2. Windows.Presentation.Foundation(WPF) 방식
- 디자인파트
- 코드파트로 나눌수 있다는점에서 웹프로그래밍과 일맥상통하다.(xaml/cs)

FiancialCalc.msi
0.71MB


<Window x:Class="WINPAYCalcM.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WINPAYCalcM"
        mc:Ignorable="d"
        Title="원리금균등상환계산기(대출상환포함)" Height="1000" Width="1400"
KeyDown="Window_KeyDown">
    <Window.Resources>
        <Style TargetType="DataGrid">
            <Setter Property="ColumnHeaderHeight" Value="40"/>
            <Setter Property="RowHeight" Value="30"/>
            <Setter Property="FontSize" Value="14"/>
            <Setter Property="HorizontalGridLinesBrush" Value="LightGray"/>
            <Setter Property="VerticalGridLinesBrush" Value="LightGray"/>
            <Setter Property="AlternatingRowBackground" Value="LightCyan"/>
            <Setter Property="Background" Value="White"/>
        </Style>
        <Style TargetType="DataGridColumnHeader">
            <Setter Property="Background" Value="SlateGray"/>
            <Setter Property="Foreground" Value="White"/>
            <Setter Property="FontWeight" Value="Bold"/>
            <Setter Property="HorizontalContentAlignment" Value="Center"/>
        </Style>
        <Style TargetType="DataGridRow">
            <EventSetter Event="MouseEnter" Handler="DataGridRow_MouseEnter"/>
            <EventSetter Event="MouseLeave" Handler="DataGridRow_MouseLeave"/>
            <Setter Property="Background" Value="White"/>
        </Style>
    </Window.Resources>
    <DockPanel>
        <Menu DockPanel.Dock="Top">
            <MenuItem Header="_Information">
                <MenuItem Header="기초정보입력" Click="BasicInfoMenuItem_Click"/>
                <MenuItem Header="상환정보입력(자동)" Click="RepayMenuItem_Click"/>
                <MenuItem Header="상환정보입력(수동)" Click="RepayManualMenuItem_Click"/>
                <Separator/>
                <MenuItem Header="_Exit" Click="ExitMenuItem_Click"/>
            </MenuItem>
            <MenuItem Header="_File">
                <MenuItem Header="_Save" Click="SaveFileMenuItem_Click"/>
                <MenuItem Header="_Open" Click="OpenFileMenuItem_Click"/>
            </MenuItem>
            <MenuItem Header="_Help">
                <MenuItem Header="_About" Click="AboutMenuItem_Click"/>
            </MenuItem>
        </Menu>
        <!-- 다른 컨텐츠 -->
        <Grid>
            <DataGrid x:Name="dataGrid" AutoGenerateColumns="False" IsReadOnly="False" CanUserAddRows="True"
                  CanUserDeleteRows="True" CanUserResizeColumns="True" CanUserSortColumns="True" Margin="10">
                <DataGrid.Columns>
                    <DataGridTextColumn Header="번호" Binding="{Binding index}" Width="*"/>
                    <DataGridTextColumn Header="상환방법" Binding="{Binding repaymethod}" Width="*"/>
                    <DataGridTextColumn Header="날짜" Binding="{Binding date}" Width="*"/>
                    <DataGridTextColumn Header="이자금액" Binding="{Binding rate_real_to_bank}" Width="*"/>
                    <DataGridTextColumn Header="원금금액" Binding="{Binding reapy_real_to_bank}" Width="*"/>
                    <DataGridTextColumn Header="이자상환합계" Binding="{Binding rate_real_to_bank_sum}" Width="*"/>
                    <DataGridTextColumn Header="현재원금" Binding="{Binding remain_original_pay}" Width="*"/>
    <DataGridTextColumn Header="원리금균등상환금액(일정)" Binding="{Binding original_rent_pay}" Width="*"/>
                    <DataGridTextColumn Header="이자+원금합계" Binding="{Binding calc_pricipal_plus_interest_sum}" Width="*"/>
                    <DataGridTextColumn Header="상환금액합계(원금+매달일정상환)" Binding="{Binding remain_repay_except_interest_sum}" Width="*"/>
                </DataGrid.Columns>

            </DataGrid>
        </Grid>
    </DockPanel>
</Window>

FTP로 파일을 전송 시 아스키(ASCII) 모드와 바이너리(Binary) 모드 두 가지가 있습니다. 
아스키 모드로 전송하는 경우 수신 컴퓨터에서 파일을 읽지 못하는 문제가 발생할 수 있습니다. 
ftp로 전송했는데 파일에 문제가 있다면 혹시 아스키 모드로 보냈는지 확인해보세요.

FTP 전송 모드
바이너리 모드로 보내야 하는 경우
바이너리 모드는 파일을 0과 1로 구성된 원시 데이터로 전송하는 모드입니다. 
가공되지 않은 원본 데이터이기 때문에 발송자가 보내는 파일과 수신자가 받는 파일이 정확히 같습니다.

반드시 바이너리 모드로 전송해야만 하는 파일들이 존재합니다. 
이미지 파일(.jpg, bmp, png), 사운드 파일(.mp3, .avi, .wma), 비디오 파일(.flv, .mkv, .mov, .mp4), 아카이브 파일(.zip, .rar, .tar), 그 외 실행 및 문서 파일(.exe, .doc, .xls, .pdf) 등이 그렇습니다. 사실 이렇게 나열할 필요도 없고 텍스트 형식이 아니면 그냥 바이너리로 보내야 한다고 보시면 됩니다.

아스키 모드 사용 이유
아스키 모드는 파일을 텍스트로 전송할 때 사용되는 모드입니다. 
ASCII는 American Standard Code for Information Interchange의 약자입니다. 정보 교환을 위해 만든 미국의 표준 코드입니다. .txt, html, .php, .cgi, .js, .txt, .css 등 확장자를 보낼 때 아스키 모드가 권고되기도 합니다.
당연하게도 텍스트 파일 역시 바이너리로 전송해도 됩니다. 
다만 아스키 모드로 전송 시 CR(Carriage Return)이 제거된다는 장점(?)이 있습니다. 
이게 무슨 말이냐 하면, 운영체제 간 행바꿈을 표현하는 기호의 차이를 보정해준다는 것입니다.

굳이 아스키 모드 안 써도 되는 이유
그럼에도 아스키 모드는 현재로서 영향력을 잃어가는 추세입니다. 
왜냐하면 바이너리 모드로도 별 문제가 없기 때문입니다. 
위에서 언급한 형식의 문제는 이미 상용 프로그램들이 스스로 보정을 해주기 때문입니다.
게다가 아스키 모드는 한국어, 일본어, 중국어 등의 문자를 처리하지 못합니다. 
텍스트 파일이라 할지라도 한글로 쓴 문서는 어차피 바이너리로 보내야 합니다.
이런 흐름을 반영하듯 sftp에선 모든 데이터는 바이너리 모드로 전송됩니다. 아스키 모드는 없습니다. 

비고)
윈도우는 띄어쓰기를 하면 캐리지 리턴(CR)과 라인피드(LF)가 모두 표기됩니다. 
두 용어는 과거 타자기에서 유래된 용어인데, 그게 중요한 건 아니고 결과적으로 아래의 예시 같은 차이가 발생한다는 것만 이해하시면 됩니다.
윈도우의 경우 줄을 바꾸면 \r\n
이렇게 \r\n 둘이 표시됩니다.
리눅스, 맥, FreeBSD, AIX 등 유닉스 시스템의 경우 줄을 바꾸면 \n
이렇게 \n만 표시됩니다.
그러니 윈도우에서 작성한 텍스트 파일을 리눅스에 보낼 때 아스키 모드는 CR(\n)을 제거해 차이를 보정해주기 때문에 이점이 있습니다.

클로바 AI에게 물어본결과)
FTP(File Transfer Protocol)는 파일 전송을 위한 프로토콜로, ASCII와 Binary 모드를 구분하여 사용합니다.
ASCII 모드: 텍스트 파일 전송에 사용되며, 각 문자를 7비트의 ASCII 코드로 변환하여 전송합니다. 전송 과정에서 데이터의 손실이나 변형이 일어날 가능성이 적습니다.
Binary 모드: 이미지, 동영상, 압축 파일 등의 바이너리 파일 전송에 사용되며, 파일의 원래 형태를 그대로 유지하여 전송합니다.
모드의 선택은 전송할 파일의 종류와 특성에 따라 달라집니다. 이를 고려하여 적절한 모드를 선택해야 합니다.

 

TCP의 흐름 제어/Ack/Console/Multi File Send/Recv/Thread/Windows/
TCP의 흐름 제어/Ack/Console/Multi File Send/Recv/Thread/Windows/
TCP의 흐름 제어/Ack/Console/Multi File Send/Recv/Thread/Windows/

 

SEND(Console)

send.cs
0.01MB

 

RECV(Console)

recv.cs
0.01MB

TCP(Transmission Control Protocol)은 원활한 통신을 위해 전송하는 데이터 흐름을 제어하고 네트워크의 혼잡 상태를 파악해서 대처하는 기능을 프로토콜 자체에 포함하고 있다.
만약 TCP가 이런 기능들을 제공해주지 않는다면 개발자가 일일히 데이터를 어떤 단위로 보낼 것인지 정의해야하고, 패킷이 유실되면 어떤 예외처리를 해야하는 지까지 신경써야하기 때문에 TCP가 제공해주는 이러한 기능들 덕분에 우리는 온전히 상위 레이어의 동작에만 집중할 수 있는 것이다.
보통 TCP의 전송 제어 방법은 전송되는 데이터의 양을 조절하는 흐름 제어, 통신 도중에 데이터가 유실되거나 잘못된 데이터가 수신되었을 경우 대처하는 방법인 오류 제어, 네트워크 혼잡에 대처하는 혼잡 제어로 나누어진다.
물론 TCP 같은 전송 계층의 프로토콜을 어플리케이션 레이어에서 활동하는 개발자가 건드릴 일은 많이 없다. 그러나 혹시라도 이 부분에서 뭔가 문제가 발생했을 경우, TCP가 어떤 식으로 작동하는지 모른다면 고치는 건 둘째치고 원인 파악조차 하지 못하는 슬픈 상황이 발생할 수 있으므로 여러모로 알아두는 것이 좋다고 생각한다. (더불어 야근도 따라올 것이다)
그런 의미에서 이번 포스팅에서는 TCP의 흐름 제어 기법들과 오류 제어 기법들에 대한 이야기를 한번 해보려고 한다.

TCP의 흐름 제어

송신 측과 수신 측이 서로 데이터를 주고 받을 때, 여러가지 요인에 따라 이 두 친구들의 처리 속도가 달라질 수 있다. 이때 데이터를 받는 수신 측의 처리 속도가 송신 측보다 빠른 경우는 사실 별 문제가 없다.
주는 족족 빠르게 처리해주니 딱히 문제될 것이 없는 것이다. 그러나 수신 측의 처리 속도보다 송신 측이 더 빠른 경우 문제가 생긴다.
송신 측과 수신 측은 모두 데이터를 저장할 수 있는 버퍼를 가지고 있다. 이때 수신 측이 자신의 버퍼 안에 있는 데이터를 처리하는 속도보다 송신 측이 데이터를 전송하는 속도가 더 빠르다면, 당연히 수신 측의 버퍼는 언젠가 꽉 차버릴 것이기 때문이다.
수신 측의 버퍼가 꽉 찬 상태에서 도착한 데이터는 더 이상 담아둘 공간이 없기 때문에 폐기 처분된다. 물론 이런 상황에서는 송신 측이 다시 데이터를 보내주기는 하겠지만, 데이터 전송이라는게 네트워크 환경에 따라 변수가 워낙 많은 작업이기 때문에 사실 이 작업을 줄일 수 있으면 줄이는 것이 가장 좋다.
그래서 송신 측은 수신 측의 데이터 처리 속도를 파악하고 자신이 얼마나 빠르게, 많은 데이터를 전송할 지 결정해야한다. 이것이 바로 TCP의 흐름 제어인 것이다.
수신 측은 자신이 처리할 수 있는 데이터의 양을 의미하는 윈도우 크기(Window Size)를 자신의 응답 헤더에 담아서 송신 측에게 전해주게 되고, 송신 측은 상대방에게 데이터를 보낼 때 이 윈도우 크기와 네트워크의 현재 상황을 참고해서 알맞은 양의 데이터를 보냄으로써 전체적인 데이터의 흐름을 제어하게 된다.

1. Stop and Wait
Stop and Wait 방식은 이름 그대로 상대방에게 데이터를 보낸 후 잘 받았다는 응답이 올 때까지 기다리는 모든 방식을 통칭하는 말이다. 이때 데이터를 받는 수신 측은 잘 받았어!와 못 받았어... 등의 대답을 해주게 되는데, 수신 측이 어떤 대답을 해주냐에 따라 사용할 수 있는 오류 제어 방법이 나눠지기도 한다.
Stop and Wait로 흐름 제어를 할 경우의 대원칙은 단순히 상대방이 응답을 하면 데이터를 보낸다이기 때문에 구현 자체도 간단하고 개발자가 어플리케이션의 작동 원리를 파악하기도 쉬운 편이다.
기본적인 ARQ(Automatic Repeat Request)를 구현한다고 생각해보면, 수신 측의 윈도우 크기를 1 byte로 설정하고 처리 가능 = 1, 처리 불가능 = 0과 같은 식으로 대충 구현해도 돌아가기는 하기 때문이다.
하지만 서로 처리 가능, 처리 불가능 정도의 의미만 주고받는 방식은 간단한만큼 비효율적이라고 할 수도 있다. 왜냐하면 송신 측은 자신이 직접 데이터를 보내봐야 이 데이터를 수신 측이 처리할 수 있는지 알 수 있기 때문이다. 쉽게 말해서 이런 기초적인 Stop and Wait 방식은 그냥 될 때까지 주구장창 보내는 방식이라고 봐도 무방하다.
그런 이유로 Stop and Wait 방식을 사용하여 흐름 제어를 할 경우에는, 이런 비효율성을 커버하기 위해 이런 단순한 구현이 아닌 여러가지 오류 제어 방식을 함께 도입해서 사용한다.

2. Sliding Window

방금 알아본 바와 같이 Stop and Wait를 사용하여 흐름 제어를 하게 되면 비효율적인 부분이 있기 때문에, 오늘날의 TCP는 특별한 경우가 아닌 이상 대부분 슬라이딩 윈도우(Sliding Window) 방식을 사용한다.
슬라이딩 윈도우는 수신 측이 한 번에 처리할 수 있는 데이터를 정해놓고 그때그때 수신 측의 데이터 처리 상황을 송신 측에 알려줘서 데이터의 흐름을 제어하는 방식이다.
Stop and Wait과 여러 가지 차이점이 있겠지만, 
사실 가장 큰 차이점은 송신 측이 수신 측이 처리할 수 있는 데이터의 양을 알고 있다는 점이다. 
이 정보를 알고 있기 때문에 굳이 수신 측이 처리 가능이라는 대답을 일일히 해주지 않아도 데이터를 보내기 전에 이게 처리될 지 어떨지 어느 정도 예측이 가능하다는 말이다.

 

 

패킷의 흐름과 오류를 제어하는 TCP | Evans Library (evan-moon.github.io)

 

패킷의 흐름과 오류를 제어하는 TCP

은 원활한 통신을 위해 전송하는 데이터 흐름을 제어하고 네트워크의 혼잡 상태를 파악해서 대처하는 기능을 프로토콜 자체에 포함하고 있다.

evan-moon.github.io

 

 

 

TCP의 흐름 제어/Ack/Stop and Wait>File Send/Recv/Windows/Guage/Thread/
TCP의 흐름 제어/Ack/Stop and Wait>File Send/Recv/Windows/Guage/Thread/
TCP의 흐름 제어/Ack/Stop and Wait>File Send/Recv/Windows/Guage/Thread/

Send하는 파트에서 Thread 활용시에 다중 데이타를 송신/수신할수 있다.
Send하는 파트에서 Thread 활용시에 다중 데이타를 송신/수신할수 있다.
Send하는 파트에서 Thread 활용시에 다중 데이타를 송신/수신할수 있다.

TCP(Transmission Control Protocol)은 원활한 통신을 위해 전송하는 데이터 흐름을 제어하고 네트워크의 혼잡 상태를 파악해서 대처하는 기능을 프로토콜 자체에 포함하고 있다.
만약 TCP가 이런 기능들을 제공해주지 않는다면 개발자가 일일히 데이터를 어떤 단위로 보낼 것인지 정의해야하고, 패킷이 유실되면 어떤 예외처리를 해야하는 지까지 신경써야하기 때문에 TCP가 제공해주는 이러한 기능들 덕분에 우리는 온전히 상위 레이어의 동작에만 집중할 수 있는 것이다.
보통 TCP의 전송 제어 방법은 전송되는 데이터의 양을 조절하는 흐름 제어, 통신 도중에 데이터가 유실되거나 잘못된 데이터가 수신되었을 경우 대처하는 방법인 오류 제어, 네트워크 혼잡에 대처하는 혼잡 제어로 나누어진다.
물론 TCP 같은 전송 계층의 프로토콜을 어플리케이션 레이어에서 활동하는 개발자가 건드릴 일은 많이 없다. 그러나 혹시라도 이 부분에서 뭔가 문제가 발생했을 경우, TCP가 어떤 식으로 작동하는지 모른다면 고치는 건 둘째치고 원인 파악조차 하지 못하는 슬픈 상황이 발생할 수 있으므로 여러모로 알아두는 것이 좋다고 생각한다. (더불어 야근도 따라올 것이다)
그런 의미에서 이번 포스팅에서는 TCP의 흐름 제어 기법들과 오류 제어 기법들에 대한 이야기를 한번 해보려고 한다.

TCP의 흐름 제어

송신 측과 수신 측이 서로 데이터를 주고 받을 때, 여러가지 요인에 따라 이 두 친구들의 처리 속도가 달라질 수 있다. 이때 데이터를 받는 수신 측의 처리 속도가 송신 측보다 빠른 경우는 사실 별 문제가 없다.
주는 족족 빠르게 처리해주니 딱히 문제될 것이 없는 것이다. 그러나 수신 측의 처리 속도보다 송신 측이 더 빠른 경우 문제가 생긴다.
송신 측과 수신 측은 모두 데이터를 저장할 수 있는 버퍼를 가지고 있다. 이때 수신 측이 자신의 버퍼 안에 있는 데이터를 처리하는 속도보다 송신 측이 데이터를 전송하는 속도가 더 빠르다면, 당연히 수신 측의 버퍼는 언젠가 꽉 차버릴 것이기 때문이다.
수신 측의 버퍼가 꽉 찬 상태에서 도착한 데이터는 더 이상 담아둘 공간이 없기 때문에 폐기 처분된다. 물론 이런 상황에서는 송신 측이 다시 데이터를 보내주기는 하겠지만, 데이터 전송이라는게 네트워크 환경에 따라 변수가 워낙 많은 작업이기 때문에 사실 이 작업을 줄일 수 있으면 줄이는 것이 가장 좋다.
그래서 송신 측은 수신 측의 데이터 처리 속도를 파악하고 자신이 얼마나 빠르게, 많은 데이터를 전송할 지 결정해야한다. 이것이 바로 TCP의 흐름 제어인 것이다.
수신 측은 자신이 처리할 수 있는 데이터의 양을 의미하는 윈도우 크기(Window Size)를 자신의 응답 헤더에 담아서 송신 측에게 전해주게 되고, 송신 측은 상대방에게 데이터를 보낼 때 이 윈도우 크기와 네트워크의 현재 상황을 참고해서 알맞은 양의 데이터를 보냄으로써 전체적인 데이터의 흐름을 제어하게 된다.

1. Stop and Wait
Stop and Wait 방식은 이름 그대로 상대방에게 데이터를 보낸 후 잘 받았다는 응답이 올 때까지 기다리는 모든 방식을 통칭하는 말이다. 이때 데이터를 받는 수신 측은 잘 받았어!와 못 받았어... 등의 대답을 해주게 되는데, 수신 측이 어떤 대답을 해주냐에 따라 사용할 수 있는 오류 제어 방법이 나눠지기도 한다.
Stop and Wait로 흐름 제어를 할 경우의 대원칙은 단순히 상대방이 응답을 하면 데이터를 보낸다이기 때문에 구현 자체도 간단하고 개발자가 어플리케이션의 작동 원리를 파악하기도 쉬운 편이다.
기본적인 ARQ(Automatic Repeat Request)를 구현한다고 생각해보면, 수신 측의 윈도우 크기를 1 byte로 설정하고 처리 가능 = 1, 처리 불가능 = 0과 같은 식으로 대충 구현해도 돌아가기는 하기 때문이다.
하지만 서로 처리 가능, 처리 불가능 정도의 의미만 주고받는 방식은 간단한만큼 비효율적이라고 할 수도 있다. 왜냐하면 송신 측은 자신이 직접 데이터를 보내봐야 이 데이터를 수신 측이 처리할 수 있는지 알 수 있기 때문이다. 쉽게 말해서 이런 기초적인 Stop and Wait 방식은 그냥 될 때까지 주구장창 보내는 방식이라고 봐도 무방하다.
그런 이유로 Stop and Wait 방식을 사용하여 흐름 제어를 할 경우에는, 이런 비효율성을 커버하기 위해 이런 단순한 구현이 아닌 여러가지 오류 제어 방식을 함께 도입해서 사용한다.

 



>>>>보내는쪽에서의 개략소스)
>>>>보내는쪽에서의 개략소스)
>>>>보내는쪽에서의 개략소스)
>>>>보내는쪽에서의 개략소스)

while (true)
{
    while(true)
    {
        bytesSent = sender.Send(fileByte);
        bytesSent = sender.Receive(recvByte);

        string flagTxt = Encoding.UTF8.GetString(recvByte, 0, bytesSent);

        if (flagTxt == "no") continue;
        else if (flagTxt == "go") break;
    }
}

>>>>받는쪽에서 개략소스)
>>>>받는쪽에서 개략소스)
>>>>받는쪽에서 개략소스)
>>>>받는쪽에서 개략소스)

while (true)
{
    int sz = handler.Receive(bytes);

    if (sz == -1) break;
    else if (sz != PACKSZ) //제대로 받지를 못했다고 판단했을때에
    {
if (sz + cur < filesz) //실제로 마지막데이타도 아니라면
{
    handler.Send("no");
    continue;
}
    }

    cur += sz;

    handler.Send("go");

    if (cur > filesz) break;
    else if (filesz == cur) break;
}

 

TCP의 흐름 제어/Stop and Wait>File Recv/Windows/Guage/
TCP의 흐름 제어/Stop and Wait>File Recv/Windows/Guage/

Program.cs
0.00MB
ACKRcvFrm.cs
0.01MB
ACKRcvFrm.Designer.cs
0.06MB
AGauge.cs
0.06MB



TCP의 흐름 제어/ Stop and Wait>File Send/Windows/
TCP의 흐름 제어/ Stop and Wait>File Send/Windows/

 

ACKSndFrm.cs
0.01MB
ACKSndFrm.Designer.cs
0.00MB
Program.cs
0.00MB

TCP의 흐름 제어/No-Ack/Sliding Window/File Send/Recv/Windows/Guage/Thread/
TCP의 흐름 제어/No-Ack/Sliding Window/File Send/Recv/Windows/Guage/Thread/
TCP의 흐름 제어/No-Ack/Sliding Window/File Send/Recv/Windows/Guage/Thread/

1) Using Send Thread
Send하는 파트에서 Thread 활용시에 다중 데이타를 송신/수신할수 있다.
Send하는 파트에서 Thread 활용시에 다중 데이타를 송신/수신할수 있다.
2) Using Send Basic class
Send하는 파트에서 Thread 활용을 안해도 다중 데이타를 송신/수신할수 있다.
Send하는 파트에서 Thread 활용을 안해도 다중 데이타를 송신/수신할수 있다.

TCP의 흐름 제어/Sliding Window

2. Sliding Window
방금 알아본 바와 같이 Stop and Wait를 사용하여 흐름 제어를 하게 되면 비효율적인 부분이 있기 때문에, 오늘날의 TCP는 특별한 경우가 아닌 이상 대부분 슬라이딩 윈도우(Sliding Window) 방식을 사용한다.
슬라이딩 윈도우는 수신 측이 한 번에 처리할 수 있는 데이터를 정해놓고 그때그때 수신 측의 데이터 처리 상황을 송신 측에 알려줘서 데이터의 흐름을 제어하는 방식이다.
Stop and Wait과 여러 가지 차이점이 있겠지만, 
사실 가장 큰 차이점은 송신 측이 수신 측이 처리할 수 있는 데이터의 양을 알고 있다는 점이다. 
이 정보를 알고 있기 때문에 굳이 수신 측이 처리 가능이라는 대답을 일일히 해주지 않아도 데이터를 보내기 전에 이게 처리될 지 어떨지 어느 정도 예측이 가능하다는 말이다.

File Send/Recv/Guage12/Multi Socket Thread/
File Send/Recv/Guage12/Multi Socket Thread/

File Sender
When Enter Key Pressed,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
1. file size sender
2. file name sender
3. file data sender

 

Program.cs
0.00MB
Form1.cs
0.01MB
Form1.Designer.cs
0.00MB



File Receiver
1. file size receive
2. file name receive( -> Rename: YYYYMMDDHHMISS_0000_filename)
3. file read receive(Guage display)


Program.cs
0.00MB
AGauge.cs
0.06MB
Form1.cs
0.01MB
Form1.Designer.cs
0.06MB

TCP의 흐름 제어/Sliding Window

2. Sliding Window
방금 알아본 바와 같이 Stop and Wait를 사용하여 흐름 제어를 하게 되면 비효율적인 부분이 있기 때문에, 오늘날의 TCP는 특별한 경우가 아닌 이상 대부분 슬라이딩 윈도우(Sliding Window) 방식을 사용한다.
슬라이딩 윈도우는 수신 측이 한 번에 처리할 수 있는 데이터를 정해놓고 그때그때 수신 측의 데이터 처리 상황을 송신 측에 알려줘서 데이터의 흐름을 제어하는 방식이다.
Stop and Wait과 여러 가지 차이점이 있겠지만, 
사실 가장 큰 차이점은 송신 측이 수신 측이 처리할 수 있는 데이터의 양을 알고 있다는 점이다. 
이 정보를 알고 있기 때문에 굳이 수신 측이 처리 가능이라는 대답을 일일히 해주지 않아도 데이터를 보내기 전에 이게 처리될 지 어떨지 어느 정도 예측이 가능하다는 말이다.

File Recv/Send-Thread/ProgressBar(feat. Thread)

 

UAPDBMQ0010.Designer.cs
0.00MB
UAPDBMQ0010.cs
0.01MB
windows_frm_file_send.cs
0.00MB
windows_file_send.c
0.00MB

+ Recent posts