이번에는 원격 제어를 허용한 호스트의 전체 화면을 제어하는 컨트롤러에게 주기적으로 화면을 전송해야 합니다. 이 부분을 담당하는 ImageClient를 작성합시다.
public class ImageClient {
전송에 사용할 소켓을 멤버 필드로 선언하세요.
Socket sock;
생성자에서는 이미지를 수신할 컨트롤러의 IP와 포트 번호를 입력 인자로 받습니다.
public ImageClient(string ip, int port) {
소켓을 생성하세요.
//소켓 생성 sock = new Socket(AddressFamily.InterNetwork, //네트워크 주소 체계 SocketType.Stream,//전송 방식 ProtocolType.Tcp);//프로토콜
이미지를 수신할 컨트롤로와 연결합니다.
IPAddress ipaddr = IPAddress.Parse(ip); IPEndPoint ep = new IPEndPoint(ipaddr, port); sock.Connect(ep);//연결 요청 }
이미지를 전송하는 메서드를 정의합시다.
public bool SendImage(Image img) { if (sock == null) //소켓이 없을 때 { return false; }
이미지를 선형적으로 보내기 위해 메모리 스트림 개체를 생성합니다.
MemoryStream ms = new MemoryStream();//메모리 스트림 개체 생성
이미지 정보를 JPEG 포멧으로 스트림 개체에 저장합니다.
//이미지 정보를 JPEG 포멧으로 메모리 스트림에 저장 img.Save(ms, ImageFormat.Jpeg);
그리고 메모리 스트림에 이미지를 저장한 버퍼를 얻어옵니다.
//메모리 스티림의 버퍼를 가져오기 byte[] data = ms.GetBuffer(); try { int trans = 0;
소켓으로 전송할 이미지 버퍼의 길이를 byte 배열 개체로 만드세요.
//버퍼의 크기를 구하여 바이트 배열로 변환 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) {
대리자 개체를 생성하여 비동기로 수행할 수 있게 BeginInvoke 메서드를 호출하세요.
//비동기로 이미지 보내는 대리자 생성 SendImageDele dele = new SendImageDele(SendImage); //비동기로 이미지 전송 dele.BeginInvoke(img, callback, this); }
이미지 전송 클라이언트를 닫는 메서드도 제공합시다.
public void Close() { if (sock != null) { sock.Close();//소켓 닫기 sock = null; } } }
다음은 이번 실습에서 작성한 소스 코드입니다.
//ImageClient.cs using System; using System.Net.Sockets; using System.Net; using System.IO; using System.Drawing.Imaging; using System.Windows.Forms; using System.Drawing; namespace 원격제어기 { /// <summary> /// 이미지 전송 클라이언트 /// </summary> public class ImageClient { Socket sock; /// <summary> /// 생성자 /// </summary> /// <param name="ip">컨트롤러의 IP 주소</param> /// <param name="port">컨트롤러의 포트</param> public ImageClient(string ip, int port) { //소켓 생성 sock = new Socket(AddressFamily.InterNetwork, //네트워크 주소 체계 SocketType.Stream,//전송 방식 ProtocolType.Tcp);//프로토콜 IPAddress ipaddr = IPAddress.Parse(ip); IPEndPoint ep = new IPEndPoint(ipaddr, port); sock.Connect(ep);//연결 요청 } /// <summary> /// 이미지 전송 메서드 /// </summary> /// <param name="img">전송할 이미지</param> /// <returns>전송 성공 여부</returns> public bool SendImage(Image img) { if (sock == null) //소켓이 없을 때 { return false; } MemoryStream ms = new MemoryStream();//메모리 스트림 개체 생성 img.Save(ms, ImageFormat.Jpeg);//이미지 개체를 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; } } /// <summary> /// 비동기로 이미지를 보내는 메서드의 대리자 /// </summary> /// <param name="img">전송할 이미지</param> /// <returns>이미지 전송 성공 여부</returns> public delegate bool SendImageDele(Image img);//비동기로 이미지를 보내는 메서드의 대리자 /// <summary> /// 이미지를 비동기로 전송하는 메서드 /// </summary> /// <param name="img">전송할 이미지</param> /// <param name="callback">이미지 전송을 완료할 때 처리할 콜백</param> public void SendImageAsync(Image img, AsyncCallback callback)//이미지를 비동기로 전송 { SendImageDele dele = new SendImageDele(SendImage);//비동기로 이미지 보내는 대리자 생성 dele.BeginInvoke(img, callback, this);//비동기로 이미지 전송 } /// <summary> /// 이미지 클라이언트 닫기 메서드 /// </summary> public void Close() { if (sock != null) { sock.Close();//소켓 닫기 sock = null; } } } }