[Python]
Python과 C#을 이용한 소켓통신

2020. 11. 10. 08:33


728x90

해당 포스트는 링크를 스크랩한 글이며 원작자는 명월님임을 안내드립니다.

아래 코드를 개인적으로 많이 사용하게 될것같아, 스크랩하였습니다.

Python Server <-> C# Client

#Server.py

# 소켓을 사용하기 위해서는 socket을 import해야 한다.	
import socket, threading;	
 	
# binder함수는 서버에서 accept가 되면 생성되는 socket 인스턴스를 통해 client로 부터 데이터를 받으면 echo형태로 재송신하는 메소드이다.	
def binder(client_socket, addr):	
  # 커넥션이 되면 접속 주소가 나온다.	
  print('Connected by', addr);	
  try:	
    # 접속 상태에서는 클라이언트로 부터 받을 데이터를 무한 대기한다.	
    # 만약 접속이 끊기게 된다면 except가 발생해서 접속이 끊기게 된다.	
    while True:	
      # socket의 recv함수는 연결된 소켓으로부터 데이터를 받을 대기하는 함수입니다. 최초 4바이트를 대기합니다.	
      data = client_socket.recv(4);	
      # 최초 4바이트는 전송할 데이터의 크기이다. 그 크기는 big 엔디언으로 byte에서 int형식으로 변환한다.	
      # C#의 BitConverter는 big엔디언으로 처리된다.	
      length = int.from_bytes(data, "big");	
      # 다시 데이터를 수신한다.	
      data = client_socket.recv(length);	
      # 수신된 데이터를 str형식으로 decode한다.	
      msg = data.decode();	
      # 수신된 메시지를 콘솔에 출력한다.	
      print('Received from', addr, msg);	
 	
      # 수신된 메시지 앞에 「echo:」 라는 메시지를 붙힌다.	
      msg = "echo : " + msg;	
      # 바이너리(byte)형식으로 변환한다.	
      data = msg.encode();	
      # 바이너리의 데이터 사이즈를 구한다.	
      length = len(data);	
      # 데이터 사이즈를 big 엔디언 형식으로 byte로 변환한 다음 전송한다.(※이게 버그인지 big을 써도 little엔디언으로 전송된다.)	
      client_socket.sendall(length.to_bytes(4, byteorder='big'));	
      # 데이터를 클라이언트로 전송한다.	
      client_socket.sendall(data);	
  except:	
    # 접속이 끊기면 except가 발생한다.	
    print("except : " , addr);	
  finally:	
    # 접속이 끊기면 socket 리소스를 닫는다.	
    client_socket.close();	
 	
# 소켓을 만든다.	
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM);	
# 소켓 레벨과 데이터 형태를 설정한다.	
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1);	
# 서버는 복수 ip를 사용하는 pc의 경우는 ip를 지정하고 그렇지 않으면 None이 아닌 ''로 설정한다.	
# 포트는 pc내에서 비어있는 포트를 사용한다. cmd에서 netstat -an | find "LISTEN"으로 확인할 수 있다.	
server_socket.bind(('', 9999));	
# server 설정이 완료되면 listen를 시작한다.	
server_socket.listen();	
 	
try:	
  # 서버는 여러 클라이언트를 상대하기 때문에 무한 루프를 사용한다.	
  while True:	
    # client로 접속이 발생하면 accept가 발생한다.	
    # 그럼 client 소켓과 addr(주소)를 튜플로 받는다.	
    client_socket, addr = server_socket.accept();	
    th = threading.Thread(target=binder, args = (client_socket,addr));	
    # 쓰레드를 이용해서 client 접속 대기를 만들고 다시 accept로 넘어가서 다른 client를 대기한다.	
    th.start();	
except:	
  print("server");	
finally:	
   # 에러가 발생하면 서버 소켓을 닫는다.	
  server_socket.close();

#Client.cs

using System;	
using System.Collections.Generic;	
using System.Linq;	
using System.Text;	
using System.Threading.Tasks;	
using System.Net;	
using System.Net.Sockets;	
 	
namespace SocketTest	
{	
  class Program	
  {	
    static void Main(string[] args)	
    {	
      // 소켓을 생성한다.	
      using (Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))	
      {	
        // Connect 함수로 로컬(127.0.0.1)의 포트 번호 9999로 대기 중인 socket에 접속한다.	
        client.Connect(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9999));	
        // 보낼 메시지를 UTF8타입의 byte 배열로 변환한다.	
        var data = Encoding.UTF8.GetBytes("this message is sent from C# client.");	
        	
        // big엔디언으로 데이터 길이를 변환하고 서버로 보낼 데이터의 길이를 보낸다. (4byte)	
        client.Send(BitConverter.GetBytes(data.Length));	
        // 데이터를 전송한다.	
        client.Send(data);	
 	
        // 데이터의 길이를 수신하기 위한 배열을 생성한다. (4byte)	
        data = new byte[4];	
        // 데이터의 길이를 수신한다.	
        client.Receive(data, data.Length, SocketFlags.None);	
        // server에서 big엔디언으로 전송을 했는데도 little 엔디언으로 온다. big엔디언과 little엔디언은 배열의 순서가 반대이므로 reverse한다.	
        Array.Reverse(data);	
        // 데이터의 길이만큼 byte 배열을 생성한다.	
        data = new byte[BitConverter.ToInt32(data, 0)];	
        // 데이터를 수신한다.	
        client.Receive(data, data.Length, SocketFlags.None);	
        // 수신된 데이터를 UTF8인코딩으로 string 타입으로 변환 후에 콘솔에 출력한다.	
        Console.WriteLine(Encoding.UTF8.GetString(data));	
      }	
 	
      Console.WriteLine("Press any key...");	
      Console.ReadLine();	
    }	
  }	
}

C# Client <-> Python Server

#Server.cs

using System;	
using System.Collections.Generic;	
using System.Linq;	
using System.Text;	
using System.Threading.Tasks;	
using System.Net;	
using System.Net.Sockets;	
using System.Threading;	
 	
namespace SocketTest	
{	
  class Program	
  {	
    static void Main(string[] args)	
    {	
      // server 소켓을 생성한다.	
      using (var server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))	
      {	
        // ip는 로컬이고 포트는 9999로 listen 대기한다.	
        server.Bind(new IPEndPoint(IPAddress.Any, 9999));	
        server.Listen(20);	
 	
        Console.WriteLine("Server Start... Listen port 9999...");	
 	
        try	
        {	
          while (true)	
          {	
            // 다중 접속을 허용하기 위해 Threadpool를 이용한 멀티 쓰레드 환경을 만들었다.	
            ThreadPool.QueueUserWorkItem(c =>	
            {	
              Socket client = (Socket)c;	
              try	
              {	
                // 무한 루프로 메시지를 대기한다.	
                while (true)	
                {	
                  // 처음에 데이터 길이를 받기 위한 4byte를 선언한다.	
                  var data = new byte[4];	
                  // python에서 little 엔디언으로 값이 온다. big엔디언과 little엔디언은 배열의 순서가 반대이므로 reverse한다.	
                  client.Receive(data, 4, SocketFlags.None);	
                  Array.Reverse(data);	
                  // 데이터의 길이만큼 byte 배열을 생성한다.	
                  data = new byte[BitConverter.ToInt32(data, 0)];	
                  // 데이터를 수신한다.	
                  client.Receive(data, data.Length, SocketFlags.None);	
                  	
                  // byte를 UTF8인코딩으로 string 형식으로 변환한다.	
                  var msg = Encoding.UTF8.GetString(data);	
                  // 데이터를 콘솔에 출력한다.	
                  Console.WriteLine(msg);	
                  // 메시지에 echo를 문자를 붙힌다.	
                  msg = "C# server echo : " + msg;	
                  // 데이터를 UTF8인코딩으로 byte형식으로 변환한다.	
                  data = Encoding.UTF8.GetBytes(msg);	
                  // 데이터 길이를 클라이언트로 전송한다.	
                  client.Send(BitConverter.GetBytes(data.Length));	
                  // 데이터를 전송한다.	
                  client.Send(data, data.Length, SocketFlags.None);	
                }	
 	
              }	
              catch (Exception)	
              {	
                // Exception이 발생하면 (예기치 못한 접속 종료) client socket을 닫는다.	
                client.Close();	
              }	
            // server로 client가 접속이 되면 ThreadPool에 Thread가 생성됩니다.	
            }, server.Accept());	
          }	
        }	
        catch (Exception e)	
        {	
          Console.WriteLine(e);	
        }	
      }	
      Console.WriteLine("Press any key...");	
      Console.ReadLine();	
    }	
  }	
}

#Client.py

# 소켓을 사용하기 위해서는 socket을 import해야 한다.	
import socket	
# 로컬은 127.0.0.1의 ip로 접속한다.	
HOST = '127.0.0.1'	
# port는 위 서버에서 설정한 9999로 접속을 한다.	
PORT = 9999	
# 소켓을 만든다.	
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)	
# connect함수로 접속을 한다.	
client_socket.connect((HOST, PORT))	
 	
# 10번의 루프로 send receive를 한다.	
for i in range(1,10):	
  # 메시지는 hello로 보낸다.	
  msg = 'hello';	
  # 메시지를 바이너리(byte)형식으로 변환한다.	
  data = msg.encode();	
  # 메시지 길이를 구한다.	
  length = len(data);	
  # server로 big 엔디언 형식으로 데이터 길이를 전송한다.	
  client_socket.sendall(length.to_bytes(4, byteorder="big"));	
  # 데이터를 전송한다.	
  client_socket.sendall(data);	
 	
  # server로 부터 전송받을 데이터 길이를 받는다.	
  data = client_socket.recv(4);	
  # 데이터 길이는 big 엔디언 형식으로 int를 변환한다. (※이게 버그인지 big을 써도 little엔디언으로 전송된다.)	
  length = int.from_bytes(data, "big");	
  # 데이터 길이를 받는다.	
  data = client_socket.recv(length);	
  # 데이터를 수신한다.	
  msg = data.decode();	
  # 데이터를 출력한다.	
  print('Received from : ', msg);	
 	
client_socket.close();

 

1 2 3 4 5 6 7 8 9 ··· 95