내가만든/라인들2016. 2. 28. 16:25

C++ -> C#

처음에는 C++로 구현했다가 콘솔의 한계.. 로 인해 UI를 위해 C#으로 변경

마샬링이 어려워서 그냥 JSON형식으로 전달. 서버와 마찬가지로 전체 소스는 SVN에!

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using PacketDLL;
using ChatClient_CSharp.Library;
using System.Net.Json;
using System.Windows.Forms;

namespace ChatClient_CSharp
{
    delegate void DText(string strMessage);

    public class ClientInfo
    {
        private IPAddress m_IpAddress;
        private int m_nPort;
        private TcpClient m_ClientSocket;
       
        /* 스트림 전송 관련 */
        private NetworkStream m_NetWorkStream;
        private StreamReader m_strReader;
        private StreamWriter m_strWriter;

        private MemoryStream m_MemStream;
        private BinaryFormatter m_BinaryFomatter;

        private string m_strNickName;

        public ClientInfo()
        {
            m_IpAddress = IPAddress.Parse("127.0.0.1");
            m_nPort = 50001;
            m_strNickName = "";
        }

        public IPAddress IpAddress
        {
            get { return this.m_IpAddress; }
            set { this.m_IpAddress = value; }
        }
        public int Port       
        {
            get { return this.m_nPort; }
            set { this.m_nPort = value; }
        }

        public TcpClient MyClientSocket
        {
            get { return this.m_ClientSocket; }
            set { this.m_ClientSocket = value; }
        }

        public NetworkStream MyNetWorkStream
        {
            get { return this.m_NetWorkStream; }
            set { this.m_NetWorkStream = value; }
        }
        public StreamReader MyStreamReader
        {
            get { return this.m_strReader; }
            set { this.m_strReader = value; }
        }
        public StreamWriter MyStreamWriter
        {
            get { return this.m_strWriter; }
            set { this.m_strWriter = value; }
        }
        public string NickName
        {
            get { return this.m_strNickName; }
            set { this.m_strNickName = value; }
        }
        public MemoryStream MyMemoryStream
        {
            get { return this.m_MemStream; }
            set { this.m_MemStream = value; }
        }
        public BinaryFormatter MyBinaryFomatter
        {
            get { return this.m_BinaryFomatter; }
            set { this.m_BinaryFomatter = value; }
        }
    }
    public class Client : Library.Singleton.MySingleton<Client>
    {
        public const int BUFFER_SIZE = 4096;

        private AsyncCallback asReceiveThread;
        private AsyncCallback asSendThread;
        private System.Windows.Forms.TextBox textBox_Chat;
        private ClientInfo ClientInfo;
        private DText call;

        public ClientInfo CInfo
        {
            get { return this.ClientInfo; }
        }

        public Client()
        {
            Init();
            //ConnectToServer();
        }

        private void Init()
        {
            ClientInfo = new ClientInfo();
            call       = new DText(Print_Text);
        }

        public bool ConnectToServer()
        {
            ClientInfo.MyClientSocket = new TcpClient();
            IPEndPoint EP = new IPEndPoint(ClientInfo.IpAddress, ClientInfo.Port);

            int nTryCnt = 0;
            while (true)
            {
                try
                {
                    ClientInfo.MyClientSocket.Connect(EP);
                    break;
                }
                catch
                {
                    if (10 < nTryCnt)
                    {
                        return false;
                    }
                    nTryCnt++;
                }
            }

            ClientInfo.MyNetWorkStream = ClientInfo.MyClientSocket.GetStream();
            ClientInfo.MyClientSocket.ReceiveBufferSize = BUFFER_SIZE;
            ClientInfo.MyClientSocket.SendBufferSize = BUFFER_SIZE;

            this.CreateReceiveThread();

            return true;
        }

        public void CreateReceiveThread()
        {
            Thread ThreadHandle = new Thread(new ThreadStart(Receive_Thread));
            ThreadHandle.Start();

            ClientInfo.MyStreamReader = new StreamReader(ClientInfo.MyNetWorkStream);
            ClientInfo.MyStreamWriter = new StreamWriter(ClientInfo.MyNetWorkStream);
        }
       
        public void SendMessage(string strMessage)
        {
            string strName = "["+ MyForm.MainForm.GetNameBox().Text +"] ";

            JsonObjectCollection JsonCollection = new JsonObjectCollection();
            JsonCollection.Add(new JsonStringValue("Nick", strName));
            JsonCollection.Add(new JsonStringValue("Chat", strMessage));
            string strSendMessage = JsonCollection.ToString();

            byte[] ByteMessage = System.Text.Encoding.Default.GetBytes(strSendMessage.ToCharArray());
            ClientInfo.MyNetWorkStream.Write(ByteMessage, 0, ByteMessage.Length);
            ClientInfo.MyNetWorkStream.Flush();
        }

        private void Receive_Thread()
        {
            byte[] byteMessage = new byte[BUFFER_SIZE];
            BinaryFormatter BF = new BinaryFormatter();
            NetworkStream stream = ClientInfo.MyNetWorkStream;
           
            while (true)
            {
                try
                {
                    ClientInfo.MyNetWorkStream.Read(byteMessage, 0, byteMessage.Length);
                    MemoryStream MemStream = new MemoryStream(byteMessage);
                   
                    JsonTextParser JsonParser = new JsonTextParser();                   
                    JsonObject     JsonObj    = JsonParser.Parse(Encoding.Default.GetString(byteMessage));
                    JsonObjectCollection JsonCol = (JsonObjectCollection)JsonObj;

                    string Nick = (string)JsonCol["Nick"].GetValue();
                    string Chat = (string)JsonCol["Chat"].GetValue();
                    string StrChat = Nick + Chat;
                    Print_Text( StrChat );
                }
                catch
                {                  
                    break;
                }
            }
        }

        public void SetNickName( string strName )
        {
            if( null == strName || "" == strName )
            {
                return;
            }
            this.ClientInfo.NickName = strName;
        }
        public void Print_Text(string strMessage)
        {
            if (MyForm.MainForm.GetTextBox_Chat().InvokeRequired)
            {
                DText call = new DText(Print_Text);
                MyForm.MainForm.GetTextBox_Chat().Invoke(call, strMessage);
            }
            else
            {
                MyForm.MainForm.GetTextBox_Chat().AppendText(strMessage + "\r\n");
                MyForm.MainForm.FocusToType();
            }           
        }

        public bool IsClientConnected()
        {
            return ClientInfo.MyClientSocket.Connected;
        }

        public void DisconnectFromServer()
        {
            ClientInfo.MyStreamReader.Close();
            ClientInfo.MyStreamWriter.Close();
            ClientInfo.MyNetWorkStream.Close();
            ClientInfo.MyClientSocket.Close();
        }       
    }   
}

Posted by 비엔나햄
내가만든/라인들2016. 2. 28. 16:23

C++/IOCP

메인 코드만 저장. 나머지 내용은 SVN에 있음.

#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <WinSock2.h>
#include <windows.h>
#include <process.h>

#define MESSAGE_SIZE 100
#define MAX_CLIENT 3

using namespace std;
struct E_DEFINE
{
 enum ENUM
 {
  MAX_USER = 3,
 };
};
// 전역 변수
SOCKET stClientSocket[MAX_CLIENT]; // 접속중인 클라이언트 소켓
int g_nClientCnt = 0;    // 접속중인 클라이언트의 수
HANDLE g_hIocp = 0;     // IOCP 핸들 값

// 소켓 정보 구조체 OVERLAPPED로 캐스팅 되어서 넘어간다
typedef struct SocketInfo
 : public OVERLAPPED
{
 WSAOVERLAPPED m_OverLapped;
 SOCKET m_sSocket;
 WSABUF m_WsaDataBuf;
 int m_nReadLen;
 int m_nWriteLen; 
 char m_cBuff[MESSAGE_SIZE];
 SOCKADDR_IN m_siSocketAddr;
}SOCKETINFO, *PSOCKETINFO;

void SendMSG(char * message, int len, SOCKETINFO * pSocketInfo ); // 접속중인 유저들에게 패킷 broadcast 함수
DWORD WINAPI Thread_Func( LPVOID parm );       // IOCP 입력 결과에 의해서 사용될 함수
void ClientDisconnect( SOCKETINFO * pSocketInfo );     // 클라이언트 접속 종료 처리 함수
void ConsoleLog( char * strLOG );         // 콘솔 로그 출력 함수

void main()
{
 ConsoleLog("Server Start");

 WSADATA wsaData;
 SOCKET hServSock;
 SOCKADDR_IN servAddr, clntAddr;
 SOCKETINFO * pSocketInfo;

 char message[] = "Hello World";
 char RecvBuff[100];
 char szPort[] = "50001";

 HANDLE hThread;
 DWORD hThreadID;
 
 if( 0 != WSAStartup(MAKEWORD( 2, 2 ), &wsaData ) )
 {
  ConsoleLog( "StartUp ERROR!" );
 }

 hServSock = socket( PF_INET, SOCK_STREAM, 0 );
 if( INVALID_SOCKET == hServSock )
 {
  ConsoleLog( "Socket Open ERROR!" );
 }

 ZeroMemory( &servAddr, sizeof(servAddr) );
 servAddr.sin_family = AF_INET;
 servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
 servAddr.sin_port = htons(atoi(szPort));

 if( SOCKET_ERROR == bind(hServSock, (SOCKADDR*)&servAddr, sizeof(servAddr)))
 {
  ConsoleLog( "Bind ERROR!" );
 }

 if( SOCKET_ERROR == listen( hServSock, 5 ))
 {
  ConsoleLog( "Listen ERROR!" );
 }

 g_hIocp = CreateIoCompletionPort( INVALID_HANDLE_VALUE, NULL, 0, 0 );

 SYSTEM_INFO stSysInfo;
 GetSystemInfo( &stSysInfo );
 const int dwProcessors = stSysInfo.dwNumberOfProcessors * 2 + 2;
 for( int i = 0; i < dwProcessors; i++ )
 {
  hThread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)Thread_Func, 0, 0, &hThreadID );
 }

 while(TRUE)
 { 
  SOCKET hClientSock;
  SOCKADDR_IN hSockAddr;

  int nRead = MESSAGE_SIZE;
  int nFlags = 0;
  int nSockAddrLen = sizeof( hSockAddr );

  hClientSock = accept(hServSock, (SOCKADDR*)&hSockAddr, &nSockAddrLen );
  if( INVALID_SOCKET == hClientSock )
  {
   ConsoleLog( "Accept ERROR!" );
  }

  pSocketInfo = new SOCKETINFO;
  pSocketInfo->m_nReadLen  = 0;
  pSocketInfo->m_nWriteLen = 0;
  pSocketInfo->m_sSocket  = hClientSock;
  pSocketInfo->m_cBuff[0]  = '\0';
  memcpy( &(pSocketInfo->m_siSocketAddr), &hSockAddr, nSockAddrLen );
  
  ZeroMemory( &(pSocketInfo->m_OverLapped), sizeof(pSocketInfo->m_OverLapped) );
  pSocketInfo->m_WsaDataBuf.len = MESSAGE_SIZE;
  pSocketInfo->m_WsaDataBuf.buf = pSocketInfo->m_cBuff;

  g_hIocp = CreateIoCompletionPort( (HANDLE)hClientSock , g_hIocp, (unsigned long)pSocketInfo, 0 );
  if( nullptr == g_hIocp )
  {
   ConsoleLog( "CreateIoCompletion ERROR!" );
   delete pSocketInfo;
   continue;
  }

  if( SOCKET_ERROR == WSARecv( pSocketInfo->m_sSocket, &(pSocketInfo->m_WsaDataBuf), 1, (LPDWORD)(&pSocketInfo->m_nReadLen), (LPDWORD)(&nFlags), &(pSocketInfo->m_OverLapped), NULL ) );
  {
   int nRet = WSAGetLastError();
   if( WSAGetLastError() != WSA_IO_PENDING )
   {
    ConsoleLog( "WSARecv ERROR!" );
    delete pSocketInfo;
    continue;
   }
  }

  OVERLAPPED stOverLapped;
  WSABUF dataBuf;
  dataBuf.buf = message;
  dataBuf.len = strlen(message);
  ZeroMemory( &stOverLapped, sizeof(OVERLAPPED) );
  DWORD dwBufferProgress;
  if( WSASend( hClientSock, &dataBuf, 1, (LPDWORD)&dwBufferProgress, 0, NULL, NULL )  == SOCKET_ERROR )
  {
   if( WSAGetLastError() != WSA_IO_PENDING )
   {
    ConsoleLog( "WSASend ERROR" );
   }
  }  

  // 클라이언트 빈자리 검사 및 인원 제한
  int nEmptyClientSlot = 0;
  for( int nEmptyClientSlot = 0; nEmptyClientSlot<MAX_CLIENT; nEmptyClientSlot++ )
  {
   if( 0 == stClientSocket[nEmptyClientSlot] )
   {
    stClientSocket[g_nClientCnt] = hClientSock;
    char strClinetInfo[100]  = "";  
    sprintf( strClinetInfo, "Client %d accept || IP : %s", g_nClientCnt, inet_ntoa(hSockAddr.sin_addr) );
    ConsoleLog( strClinetInfo );
    g_nClientCnt++;
    break;
   }
  }  
 }

 // Socket Close
 closesocket(hServSock);
 WSACleanup();

 return;
}

// 접속중인 전체 유저에게 Send하는 함수
void SendMSG(char * message, int len, SOCKETINFO * pSocketInfo )
{
 DWORD dwBufferProgress;
 int nRetVal ;
 int nError;
 for( int i = 0; i < g_nClientCnt; i++ )
 { 
  if( 0 == stClientSocket[i] )
  {
   continue;
  }
  nRetVal = WSASend( stClientSocket[i], &pSocketInfo->m_WsaDataBuf, 1, &dwBufferProgress, 0, NULL, NULL );
  nError = WSAGetLastError();
  //send( stClientSocket[i], pSocketInfo->m_WsaDataBuf.buf, len, 0);
 }
}

DWORD WINAPI Thread_Func(LPVOID parm)
{
 HANDLE hCompletionPort = (HANDLE)parm;

 DWORD dwTransFerred = 0;
 int nFlag = 0;
 int nKey = 0;

 int nRet = 0;
 int nErrorNum = 0;

 SOCKETINFO * pSocketInfo;
 OVERLAPPED *pOverLapped;

 while(TRUE)
 {
  GetQueuedCompletionStatus( g_hIocp, (LPDWORD)&dwTransFerred, (LPDWORD)&nKey, (LPOVERLAPPED*)(&pOverLapped), INFINITE );
  pSocketInfo = (SOCKETINFO*)nKey;

  if( 0 == dwTransFerred )
  {
   ClientDisconnect( pSocketInfo );
   closesocket( pSocketInfo->m_sSocket );
   delete pSocketInfo;
   continue;
  }

  if( nullptr == pSocketInfo )
  {
   continue;
  }

  if( 0 != dwTransFerred )
  {
   SendMSG(pSocketInfo->m_cBuff, dwTransFerred, pSocketInfo );
  }

  ZeroMemory( &(pSocketInfo->m_OverLapped), sizeof( OVERLAPPED ));
  pSocketInfo->m_WsaDataBuf.len = MESSAGE_SIZE;
  pSocketInfo->m_WsaDataBuf.buf = pSocketInfo->m_cBuff;
  nFlag = 0;


  nRet = WSARecv( pSocketInfo->m_sSocket, &(pSocketInfo->m_WsaDataBuf), 1, (LPDWORD)(&dwTransFerred), (LPDWORD)(&nFlag), &(pSocketInfo->m_OverLapped), NULL ); 
  nErrorNum = WSAGetLastError();
 }
 return 0;
}

void ClientDisconnect( SOCKETINFO * pSocketInfo )
{
 if( nullptr == pSocketInfo )
 {
  return;
 }

 char strClinetInfo[100]  = "";
 for( int i = 0; i < MAX_CLIENT; i++ )
 {
  if( pSocketInfo->m_sSocket == stClientSocket[i] )
  {
   sprintf( strClinetInfo, "Client %d Disconnect || IP : %s", i, inet_ntoa(pSocketInfo->m_siSocketAddr.sin_addr) );

   stClientSocket[i] = 0;
   g_nClientCnt--;
   if( g_nClientCnt < 0 )
   {
    g_nClientCnt = 0;
   }
   break;
  }
 } 
 
 ConsoleLog( strClinetInfo );
}
void ConsoleLog( char * strLOG = nullptr )
{  
 SYSTEMTIME stSystemTime;
 GetLocalTime( &stSystemTime );
 char strTime[200] = "";

 if( nullptr == strLOG )
 {
  strLOG = "";
 }

 sprintf_s( strTime, "[%d%02d%02d] %s",
  stSystemTime.wYear,
  stSystemTime.wMonth,
  stSystemTime.wDay,
  strLOG);

 cout<< strTime << endl;
}

Posted by 비엔나햄