2015. 5. 30. 14:22
C# with TCP/IP
프로그램 설명
서버-클라이언트로 구성 된 파일 전송 예제입니다.
한 번에 전송되는 양은 8k 이며, 양방향 통신을 합니다.
1. 기본 클래스와 UI 파일입니다.
기본 클래스 : SocketAcceptedEventArgs, Listener, PacketWriter, PacketReader
UI 파일 : main.cs (서버와 클라이언트가 공통된 UI 를 사용함)
2. QueueType, TransferQueue class 를 정의합니다.
enum QueueType define : 다운로드인지, 업로드인지 구분하는 enum
public enum QueueType : byte { Download, Upload }
class TransferQueue define : 전송되는 queue 에 저장하는 class
public class TransferQueue { public static TransferQueue CreateUploadQueue(TransferClient client, string fileName) { try { var queue = new TransferQueue(); queue.Filename = Path.GetFileName(fileName); queue.Client = client; queue.Type = QueueType.Upload; queue.FS = new FileStream(fileName, FileMode.Open); queue.Thread = new Thread(new ParameterizedThreadStart(transferProc)); queue.Thread.IsBackground = true; queue.ID = Program.Rand.Next(); queue.Length = queue.FS.Length; return queue; } catch { return null; } } public static TransferQueue CreateDownloadQueue(TransferClient client, int id, string saveName, long length) { try { var queue = new TransferQueue(); queue.Filename = Path.GetFileName(saveName); queue.Client = client; queue.Type = QueueType.Download; queue.FS = new FileStream(saveName, FileMode.Create); queue.FS.SetLength(length); queue.Length = length; queue.ID = id; return queue; } catch { return null; } } private const int FILE_BUFFER_SIZE = 8175; private static byte[] file_buffer = new byte[FILE_BUFFER_SIZE]; private ManualResetEvent pauseEvent; public int ID; public int Progress, LastProgress; public long Transferred; public long Index; public long Length; public bool Running; public bool Paused; public string Filename; public QueueType Type; public TransferClient Client; public Thread Thread; public FileStream FS; private TransferQueue() { pauseEvent = new ManualResetEvent(true); Running = true; } public void Start() { Running = true; Thread.Start(this); } public void Stop() { Running = false; } public void Pause() { if(!Paused) { pauseEvent.Reset(); } else { pauseEvent.Set(); } Paused = !Paused; } public void Close() { try { Client.Transfers.Remove(ID); } catch { } Running = false; FS.Close(); pauseEvent.Dispose(); Client = null; } public void Write(byte[] bytes, long index) { lock(this) { FS.Position = index; FS.Write(bytes, 0, bytes.Length); Transferred += bytes.Length; } } private static void transferProc(object o) { TransferQueue queue = (TransferQueue)o; while(queue.Running && queue.Index < queue.Length) { queue.pauseEvent.WaitOne(); if (!queue.Running) break; lock(file_buffer) { queue.FS.Position = queue.Index; int read = queue.FS.Read(file_buffer, 0, file_buffer.Length); PacketWriter pw = new PacketWriter(); pw.Write((byte)Headers.Chunk); pw.Write(queue.ID); pw.Write(queue.Index); pw.Write(read); pw.Write(file_buffer, 0, read); queue.Transferred += read; queue.Index += read; queue.Client.Send(pw.GetBytes()); queue.Progress = (int)((queue.Transferred * 100) / queue.Length); if(queue.LastProgress < queue.Progress) { queue.LastProgress = queue.Progress; queue.Client.callProgressChanged(queue); } Thread.Sleep(1); } } queue.Close(); } }
3. queue 작동 방법을 정의합니다.
public enum Headers : byte { Queue, Start, Stop, pause, Chunk }
4. TransferClient class 정의 합니다.
transferClient calss define : 클라이언트 전송 class
public class TransferClient { private Socket _baseSocket; private byte[] _buffer = new byte[8192]; private ConnectCallback _connectCallback; private Dictionary<int, TransferQueue> _transfers = new Dictionary<int, TransferQueue>(); public Dictionary<int, TransferQueue> Transfers { get { return _transfers; } } public bool Closed { get; private set; } public string OutputFolder { get; set; } public IPEndPoint EndPoint { get; private set; } public event TransferEventHandler Queued; public event TransferEventHandler ProgressChanged; public event TransferEventHandler Stopped; public event TransferEventHandler Complete; public event EventHandler Disconnected; public TransferClient() { _baseSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); } public TransferClient(Socket sock) { _baseSocket = sock; EndPoint = (IPEndPoint)_baseSocket.RemoteEndPoint; } public void Connect(string hostName, int port, ConnectCallback callback) { _connectCallback = callback; _baseSocket.BeginConnect(hostName, port, connectionCallback, null); } private void connectionCallback(IAsyncResult ar) { string error = null; try { _baseSocket.EndConnect(ar); EndPoint = (IPEndPoint)_baseSocket.RemoteEndPoint; } catch(Exception ex) { error = ex.Message.ToString(); } _connectCallback(this, error); } public void Run() { try { _baseSocket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.Peek, receiveCallback, null); } catch { Close(); } } public void QueueTransfer(string fileName) { try { TransferQueue queue = TransferQueue.CreateUploadQueue(this, fileName); _transfers.Add(queue.ID, queue); PacketWriter pw = new PacketWriter(); pw.Write((byte)Headers.Queue); pw.Write(queue.ID); pw.Write(queue.Filename); pw.Write(queue.Length); Send(pw.GetBytes()); if(Queued != null) { Queued(this, queue); } } catch { } } public void StartTransfer(TransferQueue queue) { PacketWriter pw = new PacketWriter(); pw.Write((byte)Headers.Start); pw.Write(queue.ID); Send(pw.GetBytes()); } public void StopTransfer(TransferQueue queue) { if(queue.Type == QueueType.Upload) { queue.Stop(); } PacketWriter pw = new PacketWriter(); pw.Write((byte)Headers.Stop); pw.Write(queue.ID); Send(pw.GetBytes()); queue.Close(); } public void PauseTransfer(TransferQueue queue) { if(queue.Type == QueueType.Upload) { queue.Pause(); return; } PacketWriter pw = new PacketWriter(); pw.Write((byte)Headers.pause); pw.Write(queue.ID); Send(pw.GetBytes()); } public int GetOverallProgress() { int overall = 0; foreach(KeyValuePair<int, TransferQueue> pair in _transfers) { overall += pair.Value.Progress; } System.Diagnostics.Trace.WriteLine(string.Format("1. overall = {0}", overall)); if (overall > 0) { overall = (overall * 100) / (_transfers.Count * 100); } System.Diagnostics.Trace.WriteLine(string.Format("2. overall = {0}", overall)); if (overall > 100) overall = 100; return overall; } public void Send(byte[] data) { if (Closed) return; lock(this) { try { _baseSocket.Send(BitConverter.GetBytes(data.Length), 0, 4, SocketFlags.None); _baseSocket.Send(data, 0, data.Length, SocketFlags.None); } catch { Close(); } } } public void Close() { Closed = true; _baseSocket.Close(); if (_transfers != null) { _transfers.Clear(); _transfers = null; } _buffer = null; OutputFolder = null; if (Disconnected != null) Disconnected(this, EventArgs.Empty); } private void process() { PacketReader pr = new PacketReader(_buffer); Headers header = (Headers)pr.ReadByte(); switch(header) { case Headers.Queue: { int id = pr.ReadInt32(); string fileName = pr.ReadString(); long length = pr.ReadInt64(); TransferQueue queue = TransferQueue.CreateDownloadQueue(this, id, Path.Combine(OutputFolder, Path.GetFileName(fileName)), length); _transfers.Add(id, queue); if (Queued != null) { Queued(this, queue); } } break; case Headers.Start: { int id = pr.ReadInt32(); if(_transfers.ContainsKey(id)) { _transfers[id].Start(); } } break; case Headers.Stop: { int id = pr.ReadInt32(); if (_transfers.ContainsKey(id)) { TransferQueue queue = _transfers[id]; queue.Stop(); queue.Close(); if(Stopped != null) { Stopped(this, queue); } _transfers.Remove(id); } } break; case Headers.pause: { int id = pr.ReadInt32(); if(_transfers.ContainsKey(id)) { _transfers[id].Pause(); } } break; case Headers.Chunk: { int id = pr.ReadInt32(); long index = pr.ReadInt64(); int size = pr.ReadInt32(); byte[] buffer = pr.ReadBytes(size); TransferQueue queue = _transfers[id]; queue.Write(buffer, index); queue.Progress = (int)((queue.Transferred * 100) / queue.Length); if(queue.LastProgress < queue.Progress) { queue.LastProgress = queue.Progress; if(ProgressChanged != null) { ProgressChanged(this, queue); } if(queue.Progress == 100) { queue.Close(); if(Complete != null) { Complete(this, queue); } } } } break; } pr.Dispose(); } private void receiveCallback(IAsyncResult ar) { try { int found = _baseSocket.EndReceive(ar); if(found >= 4) { _baseSocket.Receive(_buffer, 0, 4, SocketFlags.None); int size = BitConverter.ToInt32(_buffer, 0); int read = _baseSocket.Receive(_buffer, 0, size, SocketFlags.None); while(read < size) { read += _baseSocket.Receive(_buffer, read, size - read, SocketFlags.None); } process(); } Run(); } catch { Close(); } } internal void callProgressChanged(TransferQueue queue) { if (ProgressChanged != null) ProgressChanged(this, queue); } }
5. UI method 를 정의 합니다.
실행 화면
소스 파일 :
'C# with TCP/IP' 카테고리의 다른 글
[Program C#] 패킷을 이용한 데이타 전송 (0) | 2015.05.25 |
---|---|
[Program C#] Check all client connection. (0) | 2015.05.20 |
[Program C#]이미지 파일과 텍스트 전송 - 2 (1) | 2015.04.02 |
[Program C#]이미지 파일과 텍스트 전송 - 1 (0) | 2015.03.26 |
[Program C#] SslStream 을 이용한 통신 방법 (1) | 2015.03.20 |