WCF - TCP Sample
서버
WCF LIB
namespace WCFServerLib
{
public interface IClientCallback
{
[OperationContract(IsOneWay = true)]
void SendMessageToClient(string message);
}
}
namespace WCFServerLib
{
[ServiceContract(CallbackContract = typeof(IClientCallback))]
public interface IServerService
{
[OperationContract(IsOneWay = true)]
void RegistClinet(string id);
[OperationContract]
void TestUserData(RequestEcho reqEcho);
[OperationContract]
string TestSayHello();
}
public struct RequestEcho
{
public string Message;
}
}
namespace WCFServerLib
{
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class ServerService : IServerService
{
ServerLogic MainLogic;
ConcurrentDictionary<string, IClientCallback> ClientMap = new ConcurrentDictionary<string, IClientCallback>();
public ServerService(ServerLogic mainLogic)
{
MainLogic = mainLogic;
MainLogic.ForceDisconnect = this.ForceDisconnect;
MainLogic.SendPacketClient = this.SendToClient;
MainLogic.SendPacketAllClient = this.BrodCast;
}
public void RegistClinet(string id)
{
var ctx = OperationContext.Current;
var client = ctx.GetCallbackChannel<IClientCallback>();
IClientChannel channel = client as IClientChannel;
if (ClientMap.TryAdd(id, client))
{
MainLogic.AddClinet(id, channel);
// 네트워크 통신이 안될 때 호출
//OperationContext.Current.Channel.Faulted += new EventHandler(Channel_Faulted);
// 클라이언트의 접속이 우아하게 종료되었을 때 호출
OperationContext.Current.Channel.Closed += new EventHandler(Channel_Closed);
}
else
{
//
}
}
public void TestUserData(RequestEcho reqEcho)
{
}
public string TestSayHello()
{
return "Hello WCF World!";
}
public bool SendToClient(string id, string message)
{
IClientCallback client = null;
if (ClientMap.TryGetValue(id, out client) == false)
{
return false;
}
client.SendMessageToClient(message);
return true;
}
void Channel_Closed(object sender, EventArgs e)
{
Logout((IContextChannel)sender);
}
void Logout(IContextChannel channel)
{
string sessionId = null;
if (channel != null)
{
sessionId = channel.SessionId;
var userID = MainLogic.RemoveClient(sessionId);
if (userID != null)
{
IClientCallback client = null;
ClientMap.TryRemove(userID, out client);
}
}
}
bool ForceDisconnect(string id)
{
IClientCallback client = null;
if (ClientMap.TryGetValue(id, out client) == false)
{
return false;
}
IClientChannel channel = client as IClientChannel;
((ICommunicationObject)channel).Close();
return true;
}
int BrodCast(string message)
{
int count = 0;
foreach (var client in ClientMap.Values)
{
var channel = client as IClientChannel;
if (channel.State != CommunicationState.Opened)
{
continue;
}
client.SendMessageToClient(message);
++count;
}
return count;
}
}
}
ServerLogic 클래스
namespace ServerLogicLib
{
public class ServerLogic
{
ConcurrentDictionary<string, Client> ClientMap = new ConcurrentDictionary<string, Client>();
ConcurrentDictionary<string, string> SessionIDMap = new ConcurrentDictionary<string, string>();
public Func<string, bool> ForceDisconnect;
public Func<string, string, bool> SendPacketClient;
public Func<string, int> SendPacketAllClient;
public void Init()
{
InnerMessageHostProgram.Init();
}
public void AddClinet(string id, IClientChannel clientChannel)
{
var client = new Client();
ClientMap.TryAdd(id, client);
SessionIDMap.TryAdd(clientChannel.SessionId, id);
InnerMessageHostProgram.RegistNewUser(id);
DevLog.Write(string.Format("[AddClinet] ID:{0}, SessionID:{1}", id, clientChannel.SessionId), LOG_LEVEL.INFO);
}
public string RemoveClient(string sessionID)
{
string id = null;
if (SessionIDMap.TryRemove(sessionID, out id) == false)
{
return id;
}
Client tempClient = null;
ClientMap.TryRemove(id, out tempClient);
InnerMessageHostProgram.UnRegistUser(id);
DevLog.Write(string.Format("[RemoveClinet] ID:{0}, SessionID:{1}", id, sessionID), LOG_LEVEL.INFO);
return id;
}
}
}
호스트 프로그램
ServiceHost Host = null;
WCFServerLib.ServerService HostService = null;
ServerLogic MainLogic = new ServerLogic();
private void MainForm_Load(object sender, EventArgs e)
{
InitWCF();
}
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
Host.Close();
}
void InitWCF()
{
MainLogic = new ServerLogic();
MainLogic.Init();
HostService = new WCFServerLib.ServerService(MainLogic);
// host 생성, address 지정
Host = new ServiceHost(HostService, new Uri("net.tcp://localhost/WCF/ServerService"));
// 종점 설정
Host.AddServiceEndpoint(
typeof(WCFServerLib.IServerService),
new NetTcpBinding(),
"");
// 호스트 open
Host.Open();
}
클라이언트
WCF의 IClientCallback 구현
namespace TestClient
{
class ClientCallBackHandler : WCFServerLib.IClientCallback
{
public void SendMessageToClient(string message)
{
DevLog.Write(message);
}
}
}
호스트 프로그램
public partial class ClientForm : Form
{
WCFServerLib.IServerService ServerProxy = null;
private void ClientForm_Load(object sender, EventArgs e)
{
InitWCF();
}
private void ClientForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (ServerProxy != null)
{
var channel = (System.ServiceModel.Channels.IChannel)ServerProxy;
if(channel.State == CommunicationState.Opened)
{
channel.Close();
}
}
}
void InitWCF()
{
try
{
var callback = new ClientCallBackHandler();
var factory = new DuplexChannelFactory<WCFServerLib.IServerService>(callback);
factory.Endpoint.Binding = new NetTcpBinding();
factory.Endpoint.Contract.ContractType = typeof(WCFServerLib.IServerService);
factory.Endpoint.Address = new EndpointAddress(textBoxServer.Text);
ServerProxy = factory.CreateChannel();
}
catch (Exception ex)
{
DevLog.Write(ex.Message);
}
}
// 서버에 등록하기
private void button2_Click(object sender, EventArgs e)
{
if (ServerProxy == null)
{
return;
}
ServerProxy.RegistClinet(textBoxUserID.Text);
}
// 서버에 메시지 보내기
private void button1_Click(object sender, EventArgs e)
{
if (ServerProxy == null)
{
return;
}
var result = ServerProxy.TestSayHello();
}
private void button3_Click(object sender, EventArgs e)
{
if (ServerProxy == null)
{
return;
}
var request = new WCFServerLib.RequestEcho
{
Message = string.Format("[{0}] 클라이언트에서 보냈음^^", DateTime.Now)
};
ServerProxy.TestUserData(request);
}
}
이 글은 2019-01-13에 작성되었습니다.