C#_ TCP Client (MVVM)

바위게·2024년 1월 12일

WPF

목록 보기
1/8

ViewModel

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//
using System.Threading;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Windows.Input;
using System.Collections.ObjectModel;
using TCP_Client.Model;
using WPF_Default;
using System.Windows;
using System.Windows.Forms;


namespace TCP_Client.ViewModel
{
    class ClientViewModel : Notifier
    {
        NetworkStream networkStream;

        private MessageModel selectedItem;
        private string ip;
        private string port;
        private string content;
        private int exportFileNum = 0;
        private uint syncNum = 1234567890;
        private bool isCheck = false;
        private bool canConnect = true;

        Thread thread;
        public ICommand startCommand { get; set; }
        public ICommand sendCommand { get; set; }
        public ICommand deleteContextCommand { get; set; }
        public ICommand exportContextCommand { get; set; }
        public ICommand openMessageCommand { get; set; }
        public ICommand systemExitCommand { get; set; }

        private ObservableCollection<MessageModel> messageCollection;
        private string connectText;


        public ClientViewModel()
        {
            messageCollection = new ObservableCollection<MessageModel>();
            startCommand = new Command(Start, canexecuteMethod);
            sendCommand = new Command(Send, canexecuteMethod);
            deleteContextCommand = new Command(DeleteContextMenu, canexecuteMethod);
            exportContextCommand = new Command(ExportContextMenu, canexecuteMethod);
            openMessageCommand = new Command(OpenMessageCommand, canexecuteMethod);
            systemExitCommand = new Command(SystemExit, canexecuteMethod);

            IP = "192.168.2.59";
            Port = "3000";
            ConnectText = "Connect";

        }


        #region ButtonMethode
        private void Start(object obj)
        {
            if (canConnect)
            {
                thread = new Thread(Connect);
                thread.IsBackground = true;
                thread.Start();
                canConnect = false;
            }
            else
            {
                thread.Abort();
                canConnect = true;
                MessageCollection.Add(new MessageModel() { Content = "연결  종료...", IP = this.IP, Time = DateTime.Now.ToString("F") });

            }
            if (ConnectText == "Connect")
                ConnectText = "Dis connect";
            else
                ConnectText = "Connect";

        }

        private void Connect()
        {            //클라이언트 생성
                     //         if (canConnect)
            {
                TcpClient tcpClient = new TcpClient();

                //IP와 Port 입력
                //연결
                try
                {
                    IPEndPoint iPEnd = new IPEndPoint(IPAddress.Parse(IP), int.Parse(Port));
                    Console.WriteLine("Try");
                    tcpClient.Connect(iPEnd);
                    canConnect = false;
                }
                catch (Exception e)
                {
                    System.Windows.MessageBox.Show("IP 주소를 확인하시오.", "연결 실패 \n" + e);
                    ConnectText = "ReConnect";
                    return;
                }
                //다른 Thread이기때문에 Invoke로 실행
                App.Current.Dispatcher.Invoke(() =>
                {
                    MessageCollection.Add(new MessageModel() { Content = "연결 성공...", IP = this.IP, Time = DateTime.Now.ToString("F") });
                });

                networkStream = tcpClient.GetStream();


                //streamWriter.AutoFlush = true;

                //연결이 되어있는동안
                while (tcpClient.Connected)
                {
                    Console.WriteLine("메시지");
                    byte[] headerArr = new byte[8];
                    if (networkStream.CanRead)
                        networkStream.Read(headerArr, 0, 8);
                    MessageModel messageModel = new MessageModel();
                    //head
                    if (messageModel.CompareHeader(networkStream, headerArr, syncNum))
                        GenerateMessage(messageModel.Content);
                }

            }

        }

        public void GenerateMessage(string text)
        {


            Console.WriteLine(text);
            App.Current.Dispatcher.Invoke(() =>
            {
                MessageCollection.Add(new MessageModel() { Content = text, IP = this.IP, Time = DateTime.Now.ToString("F") });
            });
        }
        /// <summary>
        /// 메시지 보내기
        /// </summary>
        /// <param name="obj"></param>
        private void Send(object obj)
        {
            if (canConnect)
                return;

            Console.WriteLine("전송");
            byte[] header = new byte[8];
            byte[] sync = new byte[4];
            byte[] type = new byte[2];
            byte[] length = new byte[2];

            sync = BitConverter.GetBytes((uint)syncNum);
            /*           if (BitConverter.IsLittleEndian)
                           Array.Reverse(sync)*/
            ;

            //Type
            if (IsCheck)
                type[1] = 1;
            else
                type[1] = 0;

            //Length
            length[1] = (byte)Content.Length;

            MessageModel messageModel = new MessageModel() { SyncPattern = sync, Type = type, Length = length, Content = this.Content, IP = this.IP, Time = DateTime.Now.ToString("F") };
            MessageCollection.Add(messageModel);
            messageModel.GeneratePacket();
            networkStream.Write(messageModel.Packet, 0, messageModel.Packet.Length);
            Console.WriteLine(messageModel.Packet);
        }
        private bool canexecuteMethod(object arg)
        {
            return true;
        }

        /// <summary>
        /// 이전메시지 불러오기
        /// </summary>
        /// <param name="obj"></param>
        private void OpenMessageCommand(object obj)
        {
            string path;
            string saveLine = "";
            OpenFileDialog openBrowserDialog = new OpenFileDialog();
            openBrowserDialog.Filter = "대화 (*.csv)|*.csv";
            if (openBrowserDialog.ShowDialog() == DialogResult.OK)
            {
                path = openBrowserDialog.FileName;
                StreamReader streamReader = new StreamReader(path, Encoding.GetEncoding("ks_c_5601-1987"));

                while (saveLine != null)
                {
                    saveLine = streamReader.ReadLine();
                    if (saveLine == null)
                        break;
                    GenerateMessage(saveLine, streamReader.ReadLine(), streamReader.ReadLine());
                    Console.WriteLine("데이터 읽어오는중...");
                }
            }
        }

        private void ExportContextMenu(object obj)
        {
            string path = "";
            FolderBrowserDialog folderBrowserDialog = new FolderBrowserDialog();
            if (folderBrowserDialog.ShowDialog() == DialogResult.OK)
            {
                path = folderBrowserDialog.SelectedPath;
                StreamWriter file = new StreamWriter(path + "/ExportData" + exportFileNum + ".csv", true, Encoding.GetEncoding("euc-kr"));
                Console.WriteLine(path);
                file.WriteLine(selectedItem.Time);
                file.WriteLine(selectedItem.IP);
                file.WriteLine(selectedItem.Content);
                file.Flush();
                file.Close();
            }
        }

        private void DeleteContextMenu(object obj)
        {
            Console.WriteLine("DeleteContextMenu Clicked");
            messageCollection.Remove(selectedItem);
        }

        public void SystemExit(object obj)
        {
            //Close();
            Environment.Exit(0);
        }

        #endregion


        #region Getter&Setter
        public ObservableCollection<MessageModel> MessageCollection
        {
            get { return messageCollection; }
            set { messageCollection = value; }
        }

        public string IP
        {
            get { return ip; }
            set { ip = value; OnPropertyChanged("IP"); }
        }
        public string Port
        {
            get { return port; }
            set { port = value; OnPropertyChanged("Port"); }
        }
        public string Content
        {
            get { return content; }
            set { content = value; OnPropertyChanged("Content"); }

        }
        public MessageModel SelectedItem
        {
            get { return selectedItem; }
            set { selectedItem = value; OnPropertyChanged("SelectedItemNum"); Console.WriteLine(selectedItem); }
        }

        public string ConnectText
        {
            get { return connectText; }
            set { connectText = value; OnPropertyChanged("ConnectText"); }
        }

        public bool IsCheck
        {
            get { return isCheck; }
            set { isCheck = value; OnPropertyChanged("IsCheck"); }
        }


        #endregion

        public void GenerateMessage(string date, string ip, string text)
        {
            MessageCollection.Add(new MessageModel() { Content = text, IP = ip, Time = date });
        }

    }
}

View (xaml)

<Window x:Class="TCP_Client.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:TCP_Client.ViewModel" 
        mc:Ignorable="d"
        WindowStyle="None" AllowsTransparency="False"
        MouseDown="Window_MouseDown"
        Title="Client" Height="500" Width="550"
        Background="#FF002970"
        
        >

    <Window.DataContext>
        <local:ClientViewModel/>
    </Window.DataContext>

    <Window.Resources>
        <Style x:Key="ec" TargetType="Button">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Button">
                        <Grid>
                            <Ellipse Fill="#FFF90000"/>
                            <Ellipse Fill="#FFF90000"                                
                                     Margin="0,0,10,10"/>

                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Setter Property="VerticalAlignment" Value="Center"/>
            <Setter Property="HorizontalAlignment" Value="Center"/>
        </Style>
    </Window.Resources>


    <Grid>

        <Grid.RowDefinitions>
            <RowDefinition Height="30"/>
            <RowDefinition Height="42"/>
            <RowDefinition Height="380*"/>
            <RowDefinition Height="47*"/>
        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>

        <!--BAR-->
        <Grid Grid.Column="0"  Background="#FF06003E">
            <TextBlock Text="Client" FontSize="20" VerticalAlignment="Center" HorizontalAlignment="Center" FontFamily="Lucida Bright" FontWeight="Bold" Foreground="Gold" />
        </Grid>
        <Grid Grid.Column="1" Grid.ColumnSpan="4" Background="#FF06003E">
            <StackPanel  Orientation="Horizontal" HorizontalAlignment="Right" >
                <Button Content="-" Width="20" Height="20" Click="Button_Click" Background="Gold" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="10,0,10,0" />
                <Button Content="X" Width="20" Height="20" Click="Button_Click" Background="Gold" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="10,0,10,0" Command="{Binding systemExitCommand}"  />

            </StackPanel>
        </Grid>

        <!--TOP-->
        <Grid  Grid.Column="0" Grid.Row="1" VerticalAlignment="Center" >
            <TextBlock Text="IP" FontSize="20" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="Gold" FontFamily="Lucida Bright" />
        </Grid>
        <Grid Grid.Column="1" Grid.Row="1">
            <TextBox Text="{Binding IP}" IsReadOnly="True" Height="20" VerticalAlignment="Center" Background="#FF4F6B9B"/>
        </Grid>
        <Grid Grid.Column="2" Grid.Row="1">
            <TextBlock Text="Port" FontSize="20" TextAlignment="Center" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="Gold" FontFamily="Lucida Bright" />
        </Grid>
        <Grid Grid.Column="3" Grid.Row="1">
            <TextBox  Text="{Binding Port}" Height="20" Background="#FF4F6B9B"/>
        </Grid>
        <Grid Grid.Column="4" Grid.Row="1">
            <Button Command="{Binding startCommand}" Background="AliceBlue" Content="{Binding ConnectText}" HorizontalAlignment="Center" Height="20"  />
        </Grid>

        <!--Content-->
        <Grid Grid.Row=" 2" Grid.ColumnSpan="5">
            <DataGrid  SelectedItem="{Binding SelectedItem}"  ItemsSource="{Binding MessageCollection}" AutoGenerateColumns="False" CanUserAddRows="False" Margin="10" Background="#FFA19FAE" >
                <DataGrid.Columns>
                    <DataGridTextColumn Header="Time" Binding="{Binding Time}" IsReadOnly="True"/>
                    <DataGridTextColumn Header="Client" Binding="{Binding IP}" IsReadOnly="True"/>
                    <DataGridTextColumn Header="Message" Binding="{Binding Content}" IsReadOnly="True" Width="*" />
                </DataGrid.Columns>
                <DataGrid.ContextMenu >
                    <ContextMenu Name="GridContext" >
                        <MenuItem Header="Delete" Command="{Binding deleteContextCommand}"/>
                        <MenuItem Header="Export" Command="{Binding exportContextCommand}"/>
                        <MenuItem Header="Import" Command="{Binding openMessageCommand}"/>
                    </ContextMenu>
                </DataGrid.ContextMenu>
            </DataGrid>
        </Grid>


        <!--Bottom-->
        <Grid Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="4">
            <TextBox Text="{Binding Content}"  HorizontalAlignment="Stretch" Height="30" Margin="10" Background="#FF4F6B9B"/>
        </Grid>
        <Grid Grid.Row="3" Grid.Column="4">
            <StackPanel Orientation="Horizontal" Margin="-10,0,0,0">
                <CheckBox IsChecked="{Binding IsCheck}" Content="Error" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,16,5,16" Foreground="Red"  />
                <Button Command="{Binding sendCommand}" Content="Send" Height="30" />
            </StackPanel>
        </Grid>

    </Grid>
</Window>

<!--<Grid Grid.Row="2" Grid.Column="5" VerticalAlignment="Bottom" >
            <Button Command="{Binding openMessageCommand}" Content="이전 대화 불러오기"  FontSize="10" Height="20" Margin="-22,0,0,0" />
        </Grid>-->

Model

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Windows;

namespace TCP_Client.Model
{
    public class MessageModel
    {
        private byte[] packet;

        private byte[] syncPatter;
        private byte[] type;
        private byte[] length;

        private byte[] footer;

        public byte[] Packet { get => packet; set => packet = value; }
        //Header
        public byte[] SyncPattern { get => syncPatter; set => syncPatter = value; }
        public byte[] Type { get => type; set => type = value; }
        public byte[] Length { get => length; set => length = value; }

        //Body
        public string Content { get; set; }
        public string Time { get; set; }
        public string IP { get; set; }

        //Footer
        public byte[] Footer { get => footer; set => footer = value; }



        public MessageModel()
        {
            // Byte Header 생성
            packet = new byte[8];
            syncPatter = new byte[4];
            type = new byte[2];
            length = new byte[2];

        }

        /// <summary>
        /// 패킷화
        /// </summary>
        /// <returns></returns>
        public byte[] GeneratePacket()
        {
            //byte[] header = length.Concat(type).Concat(syncPatter).ToArray();
            byte[] header = syncPatter.Concat(Type).Concat(Length).ToArray();
            byte[] body = Encoding.UTF8.GetBytes(Content);

            footer = new byte[header.Length];
            footer = Enumerable.Reverse(header).ToArray();
            Packet = header.Concat(body).Concat(footer).ToArray();

            return Packet;
        }

        public bool CompareHeader(NetworkStream network, byte[] header, uint syncNum)
        {
            byte[] syncArr = new byte[4];
            syncArr = header.Take(4).ToArray();
            uint sync;
            sync = BitConverter.ToUInt32(syncArr, 0);
            if (sync == syncNum)
            {
                //Type
                if (header[5] == 0)
                    Content = "정상: ";
                else
                    Content = "오류: ";

                //Length
                //첫번째 값이 길이
                int length = header[7];

                //Content
                //얻은 길이만큼 다시 읽는다
                byte[] contentArr = new byte[length];
                network.Read(contentArr, 0, length);

                //Footer
                byte[] footerArr = new byte[8];
                network.Read(footerArr, 0, 8);

                Content += Encoding.UTF8.GetString(contentArr);
                //GenerateMessage(Content);
                return true;
            }
            else
            {
                MessageBox.Show("SyncPattern이 일치하지않음..");
                return false;

            }
        }
    }
}
profile
게임하는만큼만 개발공부하자

0개의 댓글