달력

42024  이전 다음

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

C#의 GZipStream을 이용해 패킷을 압축해서 보내고 받고 테스트를 하니 잘된다.

그래서 unity에 적용!!!!! 하자 마자 오류 ...

유니티가 GZipStream과의 호환성 문제가 있나보다.

 

이것 저것 찾다가 sharpZipLib 라는걸 추천하길래 써봤다.

 

현재 서버의 구조는

 

1. 버퍼 생성

2. 버퍼의 데이터를 압축

3. 데이터를 암호화

4. 전송

 

1. 수신

2. 복호화

3. 압축 해제

4. 버퍼를 읽어 들임.

 

순서다.

 

압축 코드는 초 간단..

암호화와 압축이 옵션이라 호출 하는 쪽에서 버퍼를 공유하기 위해 out byte[] ret 으로 받고 있지만  그냥 애초에 리턴으 byte[]로 받는게 보통이다..

 

        private void Compress(byte[] data, out byte[] ret)
        {
            using(MemoryStream ms = new MemoryStream(data))  //  패킷 데이터를 넣어 메모리 스트림 생성 (input)
            {
                using(MemoryStream zip = new MemoryStream())  // 리턴받을 메모리 스트림 생성(output)
                {
                    BZip2.Compress(ms, zip, false, 3);  // input 스트림의 데이터를 output 스트림으로 압축하여 저장
                    ret = zip.ToArray();
                }
            }
        }

        public void Decompress(byte[] data, out byte[] ret)
        {
            using(MemoryStream ms = new MemoryStream(data))  // 패킷 데이터를 넣어 메모리 스트림 생성
            {
                using(MemoryStream zip = new MemoryStream()) // 리턴받을 메모리 스트림 생성
                {
                    BZip2.Decompress(ms, zip, false);  // 압축 해제 하여 output 스트림에 저장
                    ret = zip.ToArray();
                }
            }
        }

 

이렇게 데이터를 보내고 받으니 unity 에서도 잘 된다.

패킷 사이즈는 총 12000 byte 정도가 4000 byte 정도까지 줄어들었다.

 

 

Posted by SadDev
|

internal static ManualResetEvent recvDone = new ManualResetEvent(false);

....

public void Receive(Session session)
        {

session.Socket.BeginReceive(session.RecvMsg.stream, session.RecvMsg.ReadSize, (session.RecvMsg.stream.Length - session.RecvMsg.ReadSize), SocketFlags.None, new AsyncCallback(ReceiveCallback), session);

recvDone.WaitOne();

}

private void ReceiveCallback(IAsyncResult ar)
        {

recvDone.Set();

....
                this.Receive(session);

}

 

이와 같은 코드로 비동기 소켓 서버를 구현하였다.

 

그런데...

동시에 여러개의 패킷이 마구 날아오는 경우 최초 3~4개는 바로 바로 받는데 이후부터는 0.5초당 1개씩 패킷이 처리된다..

고민을 하다가  ManualResetEvent 를 사용안하고  IAsyncResult 의 WaitOne을 써보기로 했다.

 

Receive 함수의 비동기 리시브를

IAsyncResult ar = session.Socket.BeginReceive(session.RecvMsg.stream, session.RecvMsg.ReadSize, (session.RecvMsg.stream.Length - session.RecvMsg.ReadSize), SocketFlags.None, new AsyncCallback(ReceiveCallback), session);
                ar.AsyncWaitHandle.WaitOne();

이렇게 바꾸니.. 바로 바로 리시브 처리가 된다 ..

 

아우... msdn예제에서는 ResetEvent를 사용하던데.. IAsyncResult 로 바꾸니 잘되는 이유가 무엇인지 좀 찾아봐야겠다.

Posted by SadDev
|

MDI를 이용하여 프로그램을 짜다가 현재 활성화 된 자식폼의 함수를 호출하는 부분에 대한 문제가 발생했다.

MDI부모창에서 Child1 이라는 자식폼을 만든경우


child1 에서는 

public void 함수명()


Child1 child = (Child1)this.this.ActiveMdiChild;

child.함수명();


이라고 했으면 호출 되는게 당연하다.

MDI 에서 파일 저장하기를 하려고 하다가 문제가 발생했다.

자식폼이 하나가 아닌 것이다.. 자식폼이 여러개라면 ??

Child1인지 Child2 인지 모르기 때문에 child2 를 저장하는데 Child1을 선언했다면 오류가 날것이다.

그래서 인터넷을 뒤지다가 여러가지 정보를 모아서 성공!!!


우선 클래스 파일을 하나 만든다

MdiChildForm.cs 라고 하자.

public class MdiChildForm: Form   // fForm을 상속받자 (using System.Windows.Forms;)

    {

        public virtual void saveFile()   자식폼에서 호출될 함수 이름...  virtual로 선언.

        {

        }

    }


자식 폼에는 아래와 같이 함수를 추가 한다.


pubcli override void saveFile()

{

 .....내용 ....

}



MDI부모폼에서는 아래와 같이 사용한다.

private void Save_ToolStripMenuItem_Click(object sender, EventArgs e)

        {


            MdiChildFormchild = (MdiChildForm)this.ActiveMdiChild;

            child.saveFile();

        }


이로써 자식폼이 여러개일지라도 해당 자식폼의 함수를 호출 할 수 있게 되었다.




Posted by SadDev
|

서버와 클라이언트간의 데이터 전송을 하다보면 여러가지 일이 생긴다.

그중 보안적인 문제가 있는데 이를 위해 암호화를 시도!!

그 과정에서 만든 소스를 올려본다.


using System;

using System.IO;

using System.Security.Cryptography;


namespace DataCrypt

{

    public class Crypt

    {

        //Rijndael 암호화 

        private RijndaelManaged rmCrypt;

        private byte[] KEY;

        private byte[] IV;


        public Crypt()

        {

            rmCrypt = new RijndaelManaged();

        }


        /// <summary>

        /// 암호화 키

        /// </summary>

        public byte[] Key

        {

            get { return KEY; }

        }


        /// <summary>

        /// 암호화 벡터 값

        /// </summary>

        public byte[] Iv

        {

            get { return IV; }

        }

        

        /// <summary>

        /// 버퍼 데이터를 암호화 

        /// </summary>

        /// <param name="buffer">암호화할 데이터</param>

        /// <returns></returns>

        public byte[] Encrypt(byte[] buffer)

        {

            rmCrypt.GenerateKey();

            rmCrypt.GenerateIV();

            KEY = rmCrypt.Key;

            IV = rmCrypt.IV;

            return EncryptBytes(buffer);

        }


        /// <summary>

        /// 버퍼 데이터를 복호화

        /// </summary>

        /// <param name="buffer">복호화 할 데이터</param>

        /// <param name="key">암호화 키</param>

        /// <param name="iv">암호화 벡터 값</param>

        /// <returns></returns>

        public byte[] Decrypt(byte[] buffer, byte[] key, byte[] iv)

        {

            rmCrypt.Key = key;

            rmCrypt.IV = iv;

            return DecryptBytes(buffer);

        }

       

        /// <summary>

        /// 암호화 함수

        /// </summary>

        /// <param name="data">암호화 할 데이터</param>

        /// <returns></returns>

        private byte[] EncryptBytes(byte[] data)

        {

            if (data == null || data.Length == 0)

            {

                return data;

            }


            if (rmCrypt == null)

            {

                throw new ArgumentNullException("rmCrypt");

            }


            using (MemoryStream stream = new MemoryStream()) 

            using (ICryptoTransform encryptor = rmCrypt.CreateEncryptor())

            using (CryptoStream cryptStream = new CryptoStream(stream, encryptor, CryptoStreamMode.Write))

            {

                cryptStream.Write(data, 0, data.Length);

                cryptStream.FlushFinalBlock();

                return stream.ToArray();

            }

        }


        /// <summary>

        /// 복호화 함수

        /// </summary>

        /// <param name="data">복호화 할 데이터</param>

        /// <returns></returns>

        private byte[] DecryptBytes(byte[] data)

        {

            if (data == null || data.Length == 0)

            {

                return data;

            }


            if (rmCrypt == null)

            {

                throw new ArgumentNullException("rmCrypt");

            }


            using (MemoryStream stream = new MemoryStream())

            using (ICryptoTransform decryptor = rmCrypt.CreateDecryptor())

            using (CryptoStream cryptStream = new CryptoStream(stream, decryptor, CryptoStreamMode.Write))

            {

                cryptStream.Write(data, 0, data.Length);

                cryptStream.FlushFinalBlock();

                return stream.ToArray();

            }

        }        

    }

}



사용법은 간단하게

Crypt crypt = new Crypt();

byte[] buffer = crypt.Encrypt(tmpBuffer);

식으로 하면 암호화 된 데이터가 넘어온다..

복호화의 경우는  key와  iv가 필요하다.

byte[] buffer = crypt.Decrypt(tmpBuffer, key, iv);




Posted by SadDev
|

c 에서 사용하는 pointer 구조처럼 C#의 BinaryWriter에 사용하는법

MemoryStream ms = new MemoryStream();

BinaryWriter bw = new BinaryWriter(ms);

 

int a = 400;

bw.write(&a);

라는 형태로 메모리  pointer를 C#에서는 사용할수가 없다.

 

그래서 알아보니 bw.seek() 을 쓰면 된다고 해서 써봤다..

 

int a = 400;

int b = 500;

int c = 600;

int d = 999;

int e = 700;

bw.write(a);

long pointer = bw.BaseStream.position;

bw.write(b);

bw.write(c);

bw.seek((int)pointer, SeekOrigin.Begin);

bw.write(d);

bw.seek(0, SeekOrigin.End);

bw.write(e);

 

결과

400

999  //b가 500 이지만 pointer 위치를 다시 덮어 씌워서 d 를 넣었음로 999가 들어가게된다.

600

700

 

우선 이렇게 해서 해결!

Posted by SadDev
|
php 로 EXIF의 keyword 값(이미지 정보중 태그 값) 을 수정하려다가 포기하고 C#으로 만들어 봤다...

몇가지 사이트에서 정보를 얻어서 했지만 자꾸 문제가 발생...
byte 단위로 데이터 읽어가면서 테스트 해서 겨우 성공..

  protected void Page_Load(object sender, EventArgs e)
        {
 //* 특정 이미지 파일을 읽어와서 이미지의 EXIF 정보 중 40094(keyword) 를 화면에 출력함. *//
            System.Drawing.Image theImage = new Bitmap(@"D:\\photo\\DSC02787.JPG");   

            System.Drawing.Imaging.PropertyItem[] propItems = theImage.PropertyItems;
            foreach (System.Drawing.Imaging.PropertyItem items in propItems)
            {
                System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
//                string value = encoding.GetString(items.Value);
                if (items.Id == 40094)
                {   
                    Response.Write(items.Type + " : " + items.Id + " = " + items.Value.ToString() + "(len : " + items.Len + ")");
                   // for (int i = 0; i < items.Value.Length; i++) Response.Write(items.Value[i] + "<br>");
                }
            }
            theImage.Dispose();
// 특정 이미지에 keyword 값을 수정하여 이미지를 새로 저장
            WriteNewDescriptionInImage(@"d:\\photo\\DSC02787.JPG", "지혜님짱!!;이쁜이;사랑스러워");
        }

        private static ImageCodecInfo GetEncodeInfo(string mimeType)
        {
            int j;
            ImageCodecInfo[] encoders;
            encoders = ImageCodecInfo.GetImageEncoders();
            for (j = 0; j < encoders.Length; j++)
            {
                if (encoders[j].MimeType == mimeType)
                    return encoders[j];
            }
            return null;
        }

        private void WriteNewDescriptionInImage(string FileName, string NewKeywords)
        {
            System.Drawing.Image pic;
            PropertyItem[] PropertyItems;
            byte[] bKeywords = new Byte[NewKeywords.Length];

            string FileNameTemp;
            System.Drawing.Imaging.Encoder Enc = System.Drawing.Imaging.Encoder.Transformation;
            EncoderParameters EncParmas = new EncoderParameters(1);
            EncoderParameter EncParam;
            ImageCodecInfo CodenInfo = GetEncodeInfo("image/jpeg");
// 여기가 중요함 keyword 의 경우 unicode로 읽어야 함... comment 의 경우  ASCII나 UTF8로 읽어야 함. 
            bKeywords = Encoding.Unicode.GetBytes(NewKeywords);

            pic = System.Drawing.Image.FromFile(FileName);
            PropertyItems = pic.PropertyItems;
            foreach (PropertyItem item in PropertyItems)
            {
                if (item.Id == 40094)
                {
                    item.Value = bKeywords;
                    item.Len = bKeywords.Length;
                    pic.SetPropertyItem(item);
                }
            }

            FileNameTemp = FileName + ".temp";  // 파일명 + .temp로 새로 저장...
            EncParam = new EncoderParameter(Enc, (long)EncoderValue.TransformRotate90);
            EncParmas.Param[0] = EncParam;

            pic.Save(FileNameTemp, CodenInfo, EncParmas);
            pic.Dispose();
            pic = null;
            GC.Collect();
        }

'프로그래밍 > C#' 카테고리의 다른 글

[C#]byte[] 데이터 암호화  (0) 2012.08.31
BinaryWriter pointer 사용(seek)  (0) 2012.08.06
[C#] DataGridViewRow 복사  (0) 2010.07.05
[C#] MessageBox의 기능  (1) 2010.07.05
[C#] DataGridView  (0) 2010.07.02
Posted by SadDev
|

C#에서 DataGridViewRow 를 복사하려는데 오류가 자꾸 발생한다.

분명 설명에는 DataGridView.Rows.Add(DataGridViewRow )가 있다..
그런데 왜 오류가 날까....

이유는 Row에 값을 넣는 방법이 문제 였다 ㅡㅡ;;

대충 해결 방법

DataGridView dgv = new DataGridView();
...  (dgv에 특정 데이터를 집어 넣은 상태);

DataGridView dGrid  = new DataGridView();
DataGridViewRow dgvRow = new DataGridViewRow();
dgvRow = CloneWithValues(dgv.Rows[i]);

dGrid.Rows.Add(dgvRow); 
public DataGridViewRow CloneWithValues(DataGridViewRow row);
{
      DataGridViewRow clonedRow = (DataGridViewRow)row.Clone();
      for (Int32 index = 0; index < row.Cells.Count; index++)
      {
        clonedRow.Cells[index].Value = row.Cells[index].Value;
      }
      return clonedRow;
}

함수로 만들었지만.. 문제는 Cell에 값을 넣는게 문제 였다..
Row를 만들고 셀에 값을 잘 넣어주면 해결 되더라능 ㅡㅡ;

Posted by SadDev
|

C#에서 MessageBox는 javaScript의 alert 혹은 confirm 과 같다.

우선 MessageBox.Show() 의 사용법은 여러가지가 있지만.. 사실 써본적도 없다.
기본적인 두가지만 쓸뿐 --;;;

그 첫번째 .. alert과 동일한  MessageBox

1. MessageBox.Show("텍스트");

private void button_Click(object sender, EventArgs e)
{
      MessageBox.Show("오류 발생 !!");
}




2. MessageBox.Show(" 내용", "설명", 버튼 종류)

private void button2_Click(object sender, EventArgs e)
{
      if (MessageBox.Show("취소를 누르시면 입력하신 내용이 모두 초기화 됩니다.\r계속 하시겠습니까?", "입력 취소", MessageBoxButtons.YesNo) == DialogResult.Yes)
      {
        this.DialogResult = DialogResult.Abort;
      }
}



간단히 alert과 confirm의 C# 버젼으로 기억하면 된다.


이때 줄바꿈 문자는 \r 을 사용한다.. \n 은 안됨 ;ㅁ;


우선 MessageBoxButtons는 여러가지가 있는데  "Yes"는 예 버튼 "No" 는 아니오 버튼 "OK"는 확인 버튼 등이 있다.  이중 YesNo 는 예 , 아니오 버튼.
그리고 Dialogresult  는 확인 창의 결과 값인데..  Yes 이면 .. 즉 예를눌렀을때... 를 의미한다..

Posted by SadDev
|

ListView 를 써서 DataGridView의 작업을 하려 했던 내가 한심하다 ;ㅁ;
Listview의 Detail 모드에서 데이터를 작업해보려 하니 이게 아니지 싶어서 고민을 해봤다.

그러다 보니.. DataTable이 생각났고 DataTable을 쓰려고 했더니 이넘이 또 WindowForm 에서는 안된다 ㅡㅡ;;

그래서 비슷한 기능을 가진녀석이 있을거라는 생각에 ToolBox를 열심히 뒤져보니 역시나...

DataGridView를 찾아냈다.

사용법은 간단하다.

using System.Windows.Forms;

namespace WindowsFormsApplication1
{
  public partial class Form1 : Form
  {
    public Form1()
    {
      InitializeComponent();
      Init();
    }

    public void Init()
    {
      DataGridView dgrid = new DataGridView();  //  DataGridView 생성
      dgrid.Width = 600;
      dgrid.Height = 300;
      this.Controls.Add(dgrid);   

      dgrid.ColumnCount = 3;    // 컬럼의 수 지정
      dgrid.ColumnHeadersVisible = true;

      dgrid.Columns[0].Name = "ID";   // 컬럼 1의 이름
      dgrid.Columns[1].Name = "Name";   // 컬럼 2의 이름
      dgrid.Columns[2].Name = "Age";   // 컬럼 3의 이름

      dgrid.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCellsExceptHeader);   // 컬럼의 크기를 자동으로 조절

     // 데이터는 아래처럼 string[] 배열에 담아서 그냥 Rows.Add(string 배열) 해주면 편하다.
      for (int i = 0; i < 5; i++)
      {
        string[] rows = new string[]{i.ToString(), "aaanmjrystyktsyk,ts,dtuk", ((i+1)*10 ).ToString()};
        dgrid.Rows.Add(rows);
      }

    }
  }
}

Posted by SadDev
|
.NET 에 보면 ListView 라는 것이 있다.
평소 ListBox 를 사용하다가 여러개의 컬럼이 들어가는 리스트가 필요해 ListView를 사용할 계기가 생겼다.
이것 저것 알아보고 ListView를 사용..

일반적으로 우리가 알고 있는 윈도우탐색기를 생각하면 된다.
아래는 우리가 알고 있는 일반적인 탐색기의 모습이다.
보기 -> 아이콘(작은아이콘) 상태에서는 아래 처럼 아이콘과 파일(폴더)의 이름이 표시된다.



아래는 보기 -> 자세히를 선택한 상태다.
자세히 보기 상태는 보통 한눈에 다 볼수 있도록 정보가 표시된다.



보통  ListView 기능은 위 두가지 기능에 많이 사용되며 특히 두번째와 같은 형식으로 많이 쓴다.

ListView 를 이용하면 우리가 DB 작업에서 흔히 사용하는 쿼리분석기의 결과 창처럼 표시도 가능하다.

아래는 각각 쿼리분석기에서 실행한 결과 창과 ListView를 이용하여 만들어본 샘플의 결과이다.

<쿼리 분석기의 결과 창>
<Sample 프로그램의 결과창>


처음 ListView 컨트롤을 봤을 때는 단순히 "ListBox와 비슷하겠지" 라는 생각만으로 너무 간단히 봤다..
하지만 뭔가 다른게 많았다..
이런 다른점들을 몇개 정리 해 보려한다.


1. ColumnHeader
 - ColumnHeader는 위의 샘플에서 보면 UserID / Password 와 같이 컬럼의 헤더 부분에 들어가는 영역을 말한다.
 컬럼헤더의 사용법
ColumnHeader cHeader = new ColumnHeader();   // 헤더 생성
cHeader.Text = "UserID";     // 헤더에 들어갈 텍스트
cHeader.Width = 150;   // 헤더의 크기
ListView1.Columns.Add(cHeader);  //  ListView에 헤더컬럼 추가



와 같이 사용한다.

2.  ListViewItem
 - ListView의 경우 ListViewItem 컨트롤을 사용한다. ListView에 들어가 있는 각각의 항목들에 관한 내용이다.
ListViewItem 의 추가 방법
ListViewItem lItem = new ListViewItem("test001");  // 첫번째 컬럼에 들어갈 내용을 ListViewItem 생성시 입력한다.
lItem.SubItem.Add("비밀번호1"); // 두번째 컬럼에 들어갈 내용부터는 SubItem 을 사용하여 추가한다.
lItem.SubItem.Add("이름1") ;  // 마찬가지로 앞으로 주욱~~ SubItem 으로 추가하면 옆으로 계속 내용이 늘어난다.
ListView1.Item.Add(lItem);  // ListView에 Item 추가


위처럼 사용한다.

3. 내용 복사
 - ListView 에 입력된 내용을 ClipBoard(Ctrl+C)로 복사 한다.

우선 ListView에 특정 이벤트를 추가하던가. 아니면 별도의 버튼으로 이벤트를 만든다. 예제에서는 별도의 버튼의 클릭이벤트를 사용하겠습니다.
 출처는 구글에서 검색했는데 까먹었네요.
private void btnCopy_Click(object sender, EventArgs e)  // 버튼 클릭 이벤트
    {
      ListView lv = lvList;   // ListView 에  원본 ListView 를 복사
      StringBuilder buffer = new StringBuilder();  

      for (int i = 0; i < lv.Columns.Count; i++)   // 컬럼 카운트 만큰 루프를 돌면서 헤더 항목 복사
      {
        buffer.Append(lv.Columns[i].Text);
        buffer.Append("\t");
      }

      buffer.Append("\n");

      for (int i = 0; i < lv.Items.Count; i++)   //아이템 카운트 만큼 루프를 돌면서 항목 복사
      {
        for (int j = 0; j < lv.Columns.Count; j++)
        {
          buffer.Append(lv.Items[i].SubItems[j].Text);
          buffer.Append("\t");
        }

        buffer.Append("\n");
      }

      Clipboard.SetText(buffer.ToString());   // 클립보드에 내용 복사
      MessageBox.Show("ClipBoard에 복사 되었습니다. Ctrl+V로 붙여넣기 하여 사용할 수 있습니다.");
    }


위와 같이 작업 하여 엑셀이나 메모장 등에 붙여 넣기 하여 사용할 수 있습니다.

4. 헤더 클릭 시 정렬 하기.
 - 이것도 아직 수정이 필요한 사항...
 클릭하면 오름차순, 다시 누르면 내림차순이 아니라. 그냥 무조건 오름 차순으로 정렬되는 단점이 있지만 그래도 이게 어딘가 ;.... 라는 생각으로 ..
private void lvList_ColumnClick(object sender, ColumnClickEventArgs e)
    {
      lvList.ListViewItemSorter = new ListViewItemComparer(e.Column);
    }

    // Implements the manual sorting of items by columns.
    class ListViewItemComparer : IComparer   // ICompare는  using System.Collections; 를 잊지 말아야 함.
    {
      private int col;
      public ListViewItemComparer()
      {
        col = 0;
      }
      public ListViewItemComparer(int column)
      {
        col = column;
      }
      public int Compare(object x, object y)
      {
        return String.Compare(((ListViewItem)x).SubItems[col].Text, ((ListViewItem)y).SubItems[col].Text);
      }
    }


저 같은 초보 분들에게는 조금이라도 도움이 되길 바라며..

조만간 LargeIcon 모드도 사용하게 되면 사용 후 정리해서 올려보겠습니다.
Posted by SadDev
|