이번에는 원격 제어를 허용한 호스트의 전체 화면을 제어하는 호스트에게 주기적으로 전송하고 수신하는 부분을 작성합시다.
먼저 이미지를 전송하는 ImageClient 클래스를 작성합시다.
public class ImageClient {
전송에 사용할 소켓을 멤버 필드로 선언하세요.
Socket sock;
생성자에서는 상대 IP와 포트 번호를 입력 인자로 받습니다.
public ImageClient(string ip, int port) {
입력 인자로 받은 정보로 IPEndPoint 개체를 생성합니다.
IPAddress ipaddr = IPAddress.Parse(ip); IPEndPoint ep = new IPEndPoint(ipaddr, port);
그리고 TCP 소켓을 생성하고 상대 호스트 EndPoint에 연결 요청합니다.
sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); sock.Connect(ep); }
이미지를 전송하는 메서드를 정의합시다.
public bool SendImage(Image img) {
만약 소켓이 null이면 메서드를 종료합니다.
if (sock == null) { return false; }
이미지를 선형적으로 보내기 위해 메모리 스트림 개체를 생성합니다.
MemoryStream ms = new MemoryStream();
이미지를 메모리 스트림에 저장합니다.
img.Save(ms, ImageFormat.Jpeg);
그리고 메모리 스트림에 이미지를 저장한 버퍼를 얻어옵니다.
byte[] data = ms.GetBuffer(); try {
소켓으로 전송할 이미지 버퍼의 길이를 byte 배열 개체로 만들어서 전송합니다.
byte[] lbuf = BitConverter.GetBytes(data.Length); sock.Send(lbuf);
그리고 이미지를 전송합니다.
int trans = 0; while (trans < data.Length) { trans += sock.Send(data,trans,data.Length-trans,SocketFlags.None); } sock.Close(); return true;
전송이 끝나면 소켓을 닫습니다.
sock.Close(); return true; }
예외가 발생하면 응용을 끝냅니다.
catch{ Application.Exit(); return false; } }
비동기로 이미지를 전송하는 메서드도 제공합시다. 이 부분은 대리자를 이용할게요.
public delegate bool SendImageDele(Image img); public void SendImageAsync(Image img, AsyncCallback callback) {
SendImage 메서드를 인자로 대리자를 생성하여 대리자를 비동기로 수행합니다.
SendImageDele dele = new SendImageDele(SendImage); dele.BeginInvoke(img, callback, this); }
이미지 전송 클라이언트를 닫는 메서드도 제공합시다.
public void Close() { sock.Close(); sock = null; } }
이미지 수신 서버를 구현합시다. 이미지 수신 서버에서는 이미지를 수신할 때마다 폼에 이를 알려주어야 합니다. 이 부분을 위해 이벤트 처리를 할 것입니다. 먼저 이미지를 수신하였을 때 이벤트를 처리하기 위해 대리자와 이벤트 인자 클래스를 정의합시다. RecvImageEventArgs 이름의 클래스를 추가하세요.
public class RecvImageEventArgs : EventArgs {
이벤트 인자로 상대측 IPEndPoint를 가져오기 할 수 있는 속성을 제공합시다.
public IPEndPoint IPEndPoint { get; private set; }
상대측 IP 주소 개체와 IP 주소 문자열과 포트 정보를 가져오기 할 수 있는 속성도 제공합시다.
public IPAddress IPAddress { get{ return IPEndPoint.Address; } } public string IPAddressStr { get{ return IPAddress.ToString(); } } public int Port { get{ return IPEndPoint.Port; } }
이미지 개체와 이미지 크기에 관한 정보도 가져오기 할 수 있는 속성을 제공합시다.
public Image Image { get; private set; } public Size Size { get{ return Image.Size; } } public int Width { get{ return Image.Width; } } public int Height { get{ return Image.Height; } }
생성자에서는 상대측 IPEndPoint 정보와 이미지 개체를 입력 인자로 받아 속성을 설정합니다.
internal RecvImageEventArgs(IPEndPoint remote_iep, Image image) { IPEndPoint = remote_iep; Image = image; }
ToString 메서드를 재정의하여 상대측 IP 정보와 이미지의 폭과 높이를 문자열로 형성하여 제공합시다.
public override string ToString() { return string.Format("IP:{0} width:{1} height:{1}", IPAddressStr, Width, Height); } }
이미지를 수신할 때 처리를 위한 대리자를 정의합시다.
public delegate void RecvImageEventHandler(object sender, RecvImageEventArgs e);
이제 이미지 서버 클래스를 정의합시다.
public class ImageServer {
먼저 Listen 소켓과 연결 요청을 대기하고 수용하는 부분을 위한 스레드를 멤버로 선언합시다.
Socket lis_sock; Thread accept_thread = null;
이미지를 수신할 때 이벤트 처리를 위해 RecvImageEventHandler 형식의 이벤트를 선언합시다.
public event RecvImageEventHandler RecvedImage = null;
생성자에서는 IP와 포트 정보를 받습니다.
public ImageServer(string ip, int port) {
먼저 전달받은 정보로 IPEndPoint 개체를 생성합니다.
IPAddress ipaddr = IPAddress.Parse(ip); IPEndPoint ep = new IPEndPoint(ipaddr, port);
그리고 TCP 소켓을 생성하여 네트워크 인터페이스와 결합하고 백로그 큐를 설정합니다.
lis_sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); lis_sock.Bind(ep); lis_sock.Listen(1);
클라이언트 연결 요청을 대기하고 수락하는 스레드를 생성하고 시작합니다.
ThreadStart ts = new ThreadStart(AcceptLoop); accept_thread = new Thread(ts); accept_thread.Start(); }
void AcceptLoop() { try {
클라이언트 연결 요청을 대기하고 수락하는 부분은 무한 반복합니다.
while (true) {
Listen 소켓의 Accept 메서드를 호출합니다.
Socket do_sock = lis_sock.Accept();
이미지를 수신합니다. 이 부분은 별도의 메서드로 정의합시다.
Receive(do_sock); } } catch{ Close(); } }
void Receive(Socket dosock) {
먼저 수신할 이미지 길이를 수신합니다.
byte[] lbuf = new byte[4]; dosock.Receive(lbuf); int len = BitConverter.ToInt32(lbuf, 0);
그리고 이미지를 수신합니다.
byte[] buffer = new byte[len]; int trans=0; while (trans < len) { trans += dosock.Receive(buffer,trans,len-trans,SocketFlags.None); }
만약 이미지 수신 이벤트 핸들러가 있으면 이벤트 인자를 생성하여 이벤트를 전송합니다. 이벤트 인자를 생성하기 위해 수신한 byte 배열을 이미지로 변환하는 부분은 별도의 메서드를 만들기로 합시다.
if (RecvedImage != null) { IPEndPoint iep = dosock.RemoteEndPoint as IPEndPoint; RecvImageEventArgs e = new RecvImageEventArgs(iep, ConvertBitmap(buffer)); RecvedImage(this, e); } dosock.Close(); }
public Bitmap ConvertBitmap(byte[] data) {
byte 배열을 이미지로 변환하기 위해 메모리 스트림 개체를 생성합니다.
MemoryStream ms = new MemoryStream();
메모리 스트림 개체에 수신한 이미지가 있는 버퍼의 내용을 기록합니다.
ms.Write(data, 0, (int)data.Length);
메모리 스트림 개체를 입력 인자로 Bitmap 개체를 생성하여 반환합니다.
Bitmap bitmap = new Bitmap(ms); return bitmap; }
이미지 서버를 닫는 메서드도 제공합시다.
public void Close() {
스레드와 Listen 소켓을 해제하는 작업을 수행합니다.
if (accept_thread != null){ accept_thread = null; } if (lis_sock != null){ lis_sock.Close(); lis_sock = null; } }
using System; using System.Net.Sockets; using System.Net; using System.IO; using System.Drawing.Imaging; using System.Windows.Forms; using System.Drawing; namespace 원격제어기 { public class ImageClient { Socket sock; public ImageClient(string ip, int port) { IPAddress ipaddr = IPAddress.Parse(ip); IPEndPoint ep = new IPEndPoint(ipaddr, port); sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); sock.Connect(ep); } public bool SendImage(Image img) { if (sock == null) { return false; } MemoryStream ms = new MemoryStream(); img.Save(ms, ImageFormat.Jpeg); byte[] data = ms.GetBuffer(); try { int trans = 0; byte[] lbuf = BitConverter.GetBytes(data.Length); sock.Send(lbuf); while (trans < data.Length) { trans += sock.Send(data,trans,data.Length-trans,SocketFlags.None); } sock.Close(); sock = null; return true; } catch { Application.Exit(); return false; } } public delegate bool SendImageDele(Image img); public void SendImageAsync(Image img, AsyncCallback callback) { SendImageDele dele = new SendImageDele(SendImage); dele.BeginInvoke(img, callback, this); } public void Close() { if (sock != null) { sock.Close(); sock = null; } } } }
[소스 9.4] ImageClient.cs
using System; using System.Net; using System.Drawing; namespace 원격제어기 { public class RecvImageEventArgs : EventArgs { public IPEndPoint IPEndPoint { get; private set; } public IPAddress IPAddress { get { return IPEndPoint.Address; } } public string IPAddressStr { get { return IPAddress.ToString(); } } public int Port { get { return IPEndPoint.Port; } } public Image Image { get; private set; } public Size Size { get { return Image.Size; } } public int Width { get { return Image.Width; } } public int Height { get { return Image.Height; } } internal RecvImageEventArgs(IPEndPoint remote_iep, Image image) { IPEndPoint = remote_iep; Image = image; } public override string ToString() { return string.Format("IP:{0} width:{1} height:{1}", IPAddressStr, Width, Height); } } public delegate void RecvImageEventHandler(object sender, RecvImageEventArgs e); }
[소스 9.5] RecvImageEventArgs.cs
using System.Net.Sockets; using System.Threading; using System.Net; using System.Drawing; using System.IO; using System; namespace 원격제어기 { public class ImageServer { Socket lis_sock; Thread accept_thread = null; public event RecvImageEventHandler RecvedImage = null; public ImageServer(string ip, int port) { IPAddress ipaddr = IPAddress.Parse(ip); IPEndPoint ep = new IPEndPoint(ipaddr, port); lis_sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); lis_sock.Bind(ep); lis_sock.Listen(100); ThreadStart ts = new ThreadStart(AcceptLoop); accept_thread = new Thread(ts); accept_thread.Start(); } void AcceptLoop() { try { while (lis_sock != null) { Socket do_sock = lis_sock.Accept(); Receive(do_sock); } } catch{ Close(); } } void Receive(Socket dosock) { byte[] lbuf = new byte[4]; dosock.Receive(lbuf); int len = BitConverter.ToInt32(lbuf, 0); byte[] buffer = new byte[len]; int trans=0; while (trans < len) { trans += dosock.Receive(buffer,trans,len-trans,SocketFlags.None); } if (RecvedImage != null) { IPEndPoint iep = dosock.RemoteEndPoint as IPEndPoint; RecvImageEventArgs e = new RecvImageEventArgs(iep, ConvertBitmap(buffer)); RecvedImage(this, e); } dosock.Close(); } public Bitmap ConvertBitmap(byte[] data) { MemoryStream ms = new MemoryStream(); ms.Write(data, 0, (int)data.Length); Bitmap bitmap = new Bitmap(ms); return bitmap; } public void Close() { if (accept_thread != null){ accept_thread = null; } if (lis_sock != null){ lis_sock.Close(); lis_sock = null; } } } }
[소스 9.6] ImageServer.cs