WCF client can't connect to non WCF named pipe server - wcf

I have a WCF named pipe server, a non WCF named pipe server, a WCF named pipe client and a non wcf named pipe client.
The non WCF client can connect to both servers. The WCF client can only connect to the WCF server. When I try to connect it to the non WCF client I get this exception.
Unhandled Exception: System.ServiceModel.EndpointNotFoundException: There was no endpoint
listening at net.pipe://localhost/PipePlusFive that could accept the message. This is
often caused by an incorrect address or SOAP action. See InnerException,
if present, for more details. ---> System.IO.PipeException: The pipe endpoint
'net.pipe://localhost/PipePlusFive' could not be found on your local machine.
--- End of inner exception stack trace ---
According this the actual name of a pipe is a guid stored in a memory mapped file. I assume this is handled automagically for the WCF client and server. for the non WCF server I create the memory mapped file, write a guid to it, then create the pipe using that guid for the name. In the non WCF client I open the memory mapped file, read the pipe name from it, and then use that name to connect to the pipe. Then fact I can connect to both servers using the non WCF client without changing anything leads me to believe that I'm implementing this part correctly on both the server and the client.
Also when I start the non WCF server then start the WCF server the second crashes stating that it cannot listen on that pipe name because another endpoint is already listening.
I'm wondering why the WCF client can't find the non WCF server when the non WCF client can find both? is there something else WCF uses to find an end point besides what is described in the blog I linked to?
UPDATE:
Here is the code I'm using for the WCF client:
class Program
{
static void Main(string[] args)
{
ChannelFactory<IPlusFive> pipeFactory =
new ChannelFactory<IPlusFive>(new NetNamedPipeBinding(NetNamedPipeSecurityMode.None), new EndpointAddress("net.pipe://localhost/PipePlusFive"));
IPlusFive pipeProxy = pipeFactory.CreateChannel();
while (true)
{
string str = Console.ReadLine();
if (str.Equals("q"))
{ return; }
Console.WriteLine(pipeProxy.PlusFive(Int32.Parse(str)));
}
}
}
Here is the code I'm using for the WCF server:
class Program
{
static void Main(string[] args)
{
var inst = new PlusFiver();
using (ServiceHost host = new ServiceHost(inst,
new Uri[] { new Uri("net.pipe://localhost") }))
{
host.AddServiceEndpoint(typeof(IPlusFive), new NetNamedPipeBinding(NetNamedPipeSecurityMode.None), "PipePlusFive");
host.Open();
Console.WriteLine("Service is Available. Press enter to exit.");
Console.ReadLine();
host.Close();
}
}
}
Here is the code that I'm using for the non WCF server:
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Creating Memory Mapped file...");
string fileName = GenerateFileMapName(String.Empty);
Guid pipeName = Guid.NewGuid();
Console.WriteLine(" writing pipe name: " + pipeName.ToString("D"));
MemoryMappedFile mmf = null;
var messageList = new List<byte>();
try
{
mmf = WritePipeName(fileName, pipeName);
Console.WriteLine("Creating Named Pipe");
Console.WriteLine("Pipe Name: " + GetPipeNameFromMappedFile(fileName, mmf));
using (var pipe = new NamedPipeServerStream(pipeName.ToString("D"), PipeDirection.InOut))
{
Console.WriteLine("pipe created");
Console.WriteLine("Waiting for connection");
pipe.WaitForConnection();
Console.WriteLine("Received Connection");
Console.WriteLine("Waiting to receive data");
var bytes = new byte[7];
pipe.Read(bytes, 0, 7);
messageList.AddRange(bytes);
bytes = new byte[messageList[6]];
pipe.Read(bytes, 0, messageList[6]);
messageList.AddRange(bytes);
bytes = new byte[2];
pipe.Read(bytes, 0, 2);
messageList.AddRange(bytes);
messageList.Add((byte)pipe.ReadByte());
pipe.WriteByte(0x0b);
WriteList(messageList);
Console.WriteLine("Finished reading from pipe");
PrintBytes(bytes);
Console.WriteLine("Closing Connection");
pipe.Disconnect();
Console.WriteLine("Pipe disconnected");
//Console.Read();
}
}
finally
{
mmf.Dispose();
}
}
private static void WriteList(List<byte> messageList)
{
foreach (var b in messageList)
{
Console.Write(b.ToString("x2") + " ");
}
Console.WriteLine();
}
private static void PrintBytes(byte[] bytes)
{
foreach (var b in bytes)
{
Console.Write(b.ToString("x2") + " ");
}
Console.WriteLine();
}
private static string GenerateFileMapName(string uri)
{
return "net.pipe:EbmV0LnBpcGU6Ly8rLw==";
}
private static MemoryMappedFile WritePipeName(string fileName, Guid pipeName)
{
var mmf = MemoryMappedFile.CreateNew(fileName, pipeName.ToByteArray().Count());
Console.WriteLine("Memory Mapped File Created.");
using (var accessor = mmf.CreateViewAccessor(4, 45))
{
Console.WriteLine("Writing pipe name to file");
accessor.Write(0, ref pipeName);
Console.WriteLine("Finished writing pipe name to file");
}
return mmf;
}
private static string GetPipeNameFromMappedFile(string filename, MemoryMappedFile mmf)
{
Guid pipeName;
using (var accessor = mmf.CreateViewAccessor(4, 45))
{
accessor.Read<Guid>(0, out pipeName);
}
return pipeName.ToString("D");
}
}

WCF endpoint netNamedPipeBinding is designed to connect to WCF named pipe server. When WCF client established connection using this type of binding it makes the same preparations as specified in the article you mentioned. That post will clarify the rest details.

Related

What is the use case of BrokerService in ActiveMQ and how to use it correctly

I am new about ActiveMQ. I'm trying to study and check how it works by checking the example code provided by Apache at this link:-
http://activemq.apache.org/how-should-i-implement-request-response-with-jms.html
public class Server implements MessageListener {
private static int ackMode;
private static String messageQueueName;
private static String messageBrokerUrl;
private Session session;
private boolean transacted = false;
private MessageProducer replyProducer;
private MessageProtocol messageProtocol;
static {
messageBrokerUrl = "tcp://localhost:61616";
messageQueueName = "client.messages";
ackMode = Session.AUTO_ACKNOWLEDGE;
}
public Server() {
try {
//This message broker is embedded
BrokerService broker = new BrokerService();
broker.setPersistent(false);
broker.setUseJmx(false);
broker.addConnector(messageBrokerUrl);
broker.start();
} catch (Exception e) {
System.out.println("Exception: "+e.getMessage());
//Handle the exception appropriately
}
//Delegating the handling of messages to another class, instantiate it before setting up JMS so it
//is ready to handle messages
this.messageProtocol = new MessageProtocol();
this.setupMessageQueueConsumer();
}
private void setupMessageQueueConsumer() {
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(messageBrokerUrl);
Connection connection;
try {
connection = connectionFactory.createConnection();
connection.start();
this.session = connection.createSession(this.transacted, ackMode);
Destination adminQueue = this.session.createQueue(messageQueueName);
//Setup a message producer to respond to messages from clients, we will get the destination
//to send to from the JMSReplyTo header field from a Message
this.replyProducer = this.session.createProducer(null);
this.replyProducer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
//Set up a consumer to consume messages off of the admin queue
MessageConsumer consumer = this.session.createConsumer(adminQueue);
consumer.setMessageListener(this);
} catch (JMSException e) {
System.out.println("Exception: "+e.getMessage());
}
}
public void onMessage(Message message) {
try {
TextMessage response = this.session.createTextMessage();
if (message instanceof TextMessage) {
TextMessage txtMsg = (TextMessage) message;
String messageText = txtMsg.getText();
response.setText(this.messageProtocol.handleProtocolMessage(messageText));
}
//Set the correlation ID from the received message to be the correlation id of the response message
//this lets the client identify which message this is a response to if it has more than
//one outstanding message to the server
response.setJMSCorrelationID(message.getJMSCorrelationID());
//Send the response to the Destination specified by the JMSReplyTo field of the received message,
//this is presumably a temporary queue created by the client
this.replyProducer.send(message.getJMSReplyTo(), response);
} catch (JMSException e) {
System.out.println("Exception: "+e.getMessage());
}
}
public static void main(String[] args) {
new Server();
}
}
My confusion about the messageBrokerUrl = "tcp://localhost:61616"; You know ActiveMQ service is running on port 61616 by default. Why does this example chooses same port. If I try to run the code thows eception as:
Exception: Failed to bind to server socket: tcp://localhost:61616 due to: java.net.BindException: Address already in use: JVM_Bind
Perhaps if I change the port number, I can execute the code.
Please let me know why it is like this in the example and how to work with BrokerService.
The BrokerService in this example is trying to create an in memory ActiveMQ broker for use in the example. Given the error you are seeing I'd guess you already have an ActiveMQ broker running on the machine that is bound to port 61616 as that's the default port and thus the two are conflicting. You could either stop the external broker and run the example or modify the example to not run the embedded broker and just rely on your external broker instance.
Embedded brokers are great for unit testing or for creating examples that don't require the user to have a broker installed and running.

Netty: How to implement a telnet client handler which needs authentication

This is my first time ask question through this platform. I am sorry. I am not good in English. I will try my best to let you understand my questions.
I am totally beginner in Netty. I would like to implement a program to send commands to a telnet server and receive response message. I modified the sample telnet program to connect and get response from the serve when there is no authentication of serve.
The question is that
When the authentication processes are setup in server. (Require login name and password)
How to implement the client side program?
How can I receive the serve login request and response it?
Should I implement another handler to handle the authentication?
below shows how i send the commands to the server
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(new TelnetClientInitializer(sslCtx));
// Start the connection attempt.
ChannelFuture lastWriteFuture = null;
lastWriteFuture = b.connect(HOST, PORT).sync();
Channel ch = lastWriteFuture.channel();
lastWriteFuture = ch.writeAndFlush("ls" + "\r\n", ch.newPromise());
lastWriteFuture = ch.writeAndFlush("status" + "\r\n");
lastWriteFuture = ch.writeAndFlush("ls" + "\r\n");
lastWriteFuture = ch.writeAndFlush("exit" + "\r\n");
// Wait until the connection is closed.
lastWriteFuture.channel().closeFuture().sync();
} finally {
// Shut down the event loop to terminate all threads.
group.shutdownGracefully();
}
but what should i do before send the above commands to login into the serve?
The following picture shows what i want to do in the program
Thank you very much!!!
If we talk about TELNET as a protocol you should know that Telnet client from Netty examples does not support TELNET protocol. His name is just confusing and you can't connect to any standard telnet servers. You can read more about TELNET protocol here - THE TELNET PROTOCOL .
I see 2 ways:
write your implementation for TELNET on Netty
use another implementation for examples Apache Commons Net
Example for the first way - modified netty client, i tested him on Linux servers. He has several dirty hacks like a timer but he works.
Example for the second - Java – Writing An Automated Telnet Client:
import org.apache.commons.net.telnet.*;
import java.io.InputStream;
import java.io.PrintStream;
public class AutomatedTelnetClient {
private TelnetClient telnet = new TelnetClient();
private InputStream in;
private PrintStream out;
private String prompt = "~>";
public AutomatedTelnetClient(String server) {
try {
// Connect to the specified server
telnet.connect(server, 8023);
TerminalTypeOptionHandler ttopt = new TerminalTypeOptionHandler("VT100", false, false, true, false);
EchoOptionHandler echoopt = new EchoOptionHandler(true, false, true, false);
SuppressGAOptionHandler gaopt = new SuppressGAOptionHandler(true, true, true, true);
try {
telnet.addOptionHandler(ttopt);
telnet.addOptionHandler(echoopt);
telnet.addOptionHandler(gaopt);
} catch (InvalidTelnetOptionException e) {
System.err.println("Error registering option handlers: " + e.getMessage());
}
// Get input and output stream references
in = telnet.getInputStream();
out = new PrintStream(telnet.getOutputStream());
} catch (Exception e) {
e.printStackTrace();
}
}
// public void su(String password) {
// try {
// write(“su”);
// readUntil(“Password: “);
// write(password);
// prompt = “#”;
// readUntil(prompt + ” “);
// } catch (Exception e) {
// e.printStackTrace();
// }
// }
public String readUntil(String pattern) {
try {
char lastChar = pattern.charAt(pattern.length() - 1);
StringBuffer sb = new StringBuffer();
boolean found = false;
char ch = (char) in.read();
while (true) {
System.out.print(ch);
sb.append(ch);
if (ch == lastChar) {
if (sb.toString().endsWith(pattern)) {
return sb.toString();
}
}
ch = (char) in.read();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public void write(String value) {
try {
out.println(value);
out.flush();
System.out.println(value);
} catch (Exception e) {
e.printStackTrace();
}
}
public String sendCommand(String command) {
try {
write(command);
return readUntil(prompt + " ");
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public void disconnect() {
try {
telnet.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
String user = "test";
String password = "test";
AutomatedTelnetClient telnet = new AutomatedTelnetClient("localhost");
// Log the user on
telnet.readUntil("login:");
telnet.write(user);
telnet.readUntil("Password:");
telnet.write(password);
// Advance to a prompt
telnet.readUntil(telnet.prompt + " ");
telnet.sendCommand("ps -ef");
telnet.sendCommand("ls");
telnet.sendCommand("w");
telnet.disconnect();
}
}
Telnet has no real concept of a password packet, a password prompt is just like any normal text output. This means that you can just send the username and password when connection as separate lines, and the telnet server will use them correctly.
ch.writeAndFlush("administrator" + "\r\n");
ch.writeAndFlush("LetMeIn4!!" + "\r\n");
If you require connecting to server that don't always require the password, then you should read the output from the server, check if it contains "username", send the username, then keep reading if it contains "password" and send the password. This is prone to breaking as servers are not required to send those strings, and legit output may also contain those. This is the downside of the telnet protocol.
I hope this my article is helpful to someone.
Netty | Implement Telnet Automated Authentication
I had to use Telnet to control the sub-equipment while developing the space ground station software. Except for the authentication, Telnet is quite similar to regular TCP server communication. So, I implemented a Handler that automatically handles Telnet authentication to communicate with the Telnet server. When connecting to the Telnet server, the following introductory message, “Username: “, “Passwrod: “ messages are displayed in sequence, and user authentication is requested. Handler automatically handles the authentication process as if a human would input account information. Below is a brief description of the implementation.
c:\> telnet 192.168.0.1 12345
Power On Self Test (POST) Passed.
Integrated Control Unit (ICU) Build xxx (Build:xxxxxx) - Feb 7 2022, 17:57:16 (Network/TCP)
Date and Time: 2022-02-16 20:01:19 (GMT)
MAC Address : [00:xx:xx:xx:C6:8F]
Username: User
Password: 1234
>
Handler
TelnetAuthenticator Handler simply works as follows.
If the message contains the string “Username: “, send the username.
If the message contains the string “Password: “, the password is sent.
If the message contains the string “>” waiting for input, delete the authentication handler from the Pipeline. After authentication, TelnetAuthenticator Handler is unnecessary.
If the account is not registered on the Telnet server or the password does not match, the string “Username: “ or “Password: “ is repeatedly received. The authentication failure error is unrecoverable, notifying the user of a failed authentication process and forcing them to disconnect.
#Slf4j
#RequiredArgsConstructor
public class TelnetAuthenticator extends SimpleChannelInboundHandler<String> {
private final ChannelSpec channelSpec;
private boolean alreadyUserTried = false;
private boolean alreadyPasswordTried = false;
#Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) {
// If the message contains the string “Username: “, send the username.
if (msg.contains(channelSpec.getReqUserTag())) {
if (alreadyUserTried) {
processFail(ctx);
}
ctx.channel().writeAndFlush(channelSpec.getAccount().getUser() + channelSpec.getEndLine());
alreadyUserTried = true;
return;
}
// If the message contains the string “Password: “, the password is sent.
if (msg.contains(channelSpec.getReqPasswordTag())) {
if (alreadyPasswordTried) {
processFail(ctx);
}
ctx.channel().writeAndFlush(channelSpec.getAccount().getPassword() + channelSpec.getEndLine());
alreadyPasswordTried = true;
return;
}
// If the incoming message contains an input waiting message, the Pipeline deletes the current handler.
if (msg.contains(channelSpec.getStandByTag())) {
ctx.pipeline().remove(this.getClass());
}
}
private void processFail(ChannelHandlerContext ctx) {
ctx.fireUserEventTriggered(ErrorMessage.AUTHENTICATE_FAIL);
ctx.close();
}
}
Initialize ChannelPipeline
A ChannelPipeline configuration with a TelnetAuthenticator Handler can be: First, register InboundHandlers as follows.
First, add DelimiterBasedFrameDecoder with “Username: “, “Password: “, “>” strings as delimiters. The stripDelimiter option is set to false because all delimiters must be received to recognize the authentication process.
Add StringDecoder.
Add the implemented TelnetAuthenticator Handler.
Add other necessary business logic.
Simply add StringEncoder to Outbound. You can add other Handlers as needed.
public class PipelineInitializer extends ChannelInitializer<SocketChannel> {
private ChannelSpec channelSpec;
public void init(ChannelSpec channelSpec) {
this.channelSpec = channelSpec;
}
#Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline()
// Inbound
.addLast(new DelimiterBasedFrameDecoder(1024, false,
channelSpec.getDelimiter().reqUserTag(),
channelSpec.getDelimiter().reqPasswordTag(),
channelSpec.getDelimiter().standByTag()))
.addLast(new StringDecoder())
.addLast(new TelnetAuthenticator(channelSpec))
.addLast(new BusinessLogic())
// Outbound
.addLast(new StringEncoder());
}
}
ChannelSpec
ChannelSpec defines specifications required for communication with Telnet server. Manage server IP, port, account information, separator, etc.
#Getter
public class ChannelSpec {
private final String serverIp = "192.168.0.1";
private final int serverPort = 12345;
private final String endLine = "\r\n";
private final String standByTag = ">";
private final String reqUserTag = "Username: ";
private final String reqPasswordTag = "Password: ";
private final Account account = new Account("User", "1234");
private final Delimiter delimiter = new Delimiter();
public class Delimiter {
public ByteBuf standByTag() {
return toByteBuf(standByTag);
}
public ByteBuf reqUserTag() {
return toByteBuf(reqUserTag);
}
public ByteBuf reqPasswordTag() {
return toByteBuf(reqPasswordTag);
}
private ByteBuf toByteBuf(String input) {
ByteBuf delimiterBuf = Unpooled.buffer();
delimiterBuf.writeCharSequence(input, StandardCharsets.UTF_8);
return delimiterBuf;
}
}
}
#RequiredArgsConstructor
#Getter
public class Account {
private final String user;
private final String password;
}

TCP Server configuration in Mule - writing into client socket

I am trying to create a mule flow with a TCP inbound endpoint which is a TCP server that listens to a port. When a successful client connection is identified, before receiving any request from the client, I need to write a message into the socket (which lets the client know that I am listening), only after which the client sends me further requests. This is how I do it with a sample java program :
import java.net.*;
import java.io.*;
public class TCPServer
{
public static void main(String[] args) throws IOException
{
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(4445);
}
catch (IOException e)
{
System.err.println("Could not listen on port: 4445.");
System.exit(1);
}
Socket clientSocket = null;
System.out.println ("Waiting for connection.....");
try {
clientSocket = serverSocket.accept();
}
catch (IOException e)
{
System.err.println("Accept failed.");
System.exit(1);
}
System.out.println ("Connection successful");
System.out.println ("Sending output message - .....");
//Sending a message to the client to indicate that the server is active
PrintStream pingStream = new PrintStream(clientSocket.getOutputStream());
pingStream.print("Server listening");
pingStream.flush();
//Now start listening for messages
System.out.println ("Waiting for incoming message - .....");
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(),true);
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
String inputLine;
while ((inputLine = in.readLine()) != null)
{
System.out.println ("Server: " + inputLine);
out.println(inputLine);
if (inputLine.equals("Bye."))
break;
}
out.close();
in.close();
clientSocket.close();
serverSocket.close();
}
}
I have tried to use Mule's TCP inbound endpoint as a server, but I am not able to see how I can identify a successful connection from the client, inorder to trigger the outbound message. The flow gets triggered only when a message is sent across from the client. Is there a way I can extend the functionality of the Mule TCP connector and have a listener which could do the above requirement?
Based on the answer provided, this is how I implemented this -
public class TCPMuleOut extends TcpMessageReceiver {
boolean InitConnection = false;
Socket clientSocket = null;
public TCPMuleOut(Connector connector, FlowConstruct flowConstruct,
InboundEndpoint endpoint) throws CreateException {
super(connector, flowConstruct, endpoint);
}
protected Work createWork(Socket socket) throws IOException {
return new MyTcpWorker(socket, this);
}
protected class MyTcpWorker extends TcpMessageReceiver.TcpWorker {
public MyTcpWorker(Socket socket, AbstractMessageReceiver receiver)
throws IOException {
super(socket, receiver);
// TODO Auto-generated constructor stub
}
#Override
protected Object getNextMessage(Object resource) throws Exception {
if (InitConnection == false) {
clientSocket = this.socket;
logger.debug("Sending logon message");
PrintStream pingStream = new PrintStream(
clientSocket.getOutputStream());
pingStream.print("Log on message");
pingStream.flush();
InitConnection = true;
}
long keepAliveTimeout = ((TcpConnector) connector)
.getKeepAliveTimeout();
Object readMsg = null;
try {
// Create a monitor if expiry was set
if (keepAliveTimeout > 0) {
((TcpConnector) connector).getKeepAliveMonitor()
.addExpirable(keepAliveTimeout,
TimeUnit.MILLISECONDS, this);
}
readMsg = protocol.read(dataIn);
// There was some action so we can clear the monitor
((TcpConnector) connector).getKeepAliveMonitor()
.removeExpirable(this);
if (dataIn.isStreaming()) {
}
return readMsg;
} catch (SocketTimeoutException e) {
((TcpConnector) connector).getKeepAliveMonitor()
.removeExpirable(this);
System.out.println("Socket timeout");
} finally {
if (readMsg == null) {
// Protocols can return a null object, which means we're
// done
// reading messages for now and can mark the stream for
// closing later.
// Also, exceptions can be thrown, in which case we're done
// reading.
dataIn.close();
InitConnection = false;
logger.debug("Client closed");
}
}
return null;
}
}
}
And the TCP connector is as below:
<tcp:connector name="TCP" doc:name="TCP connector"
clientSoTimeout="100000" receiveBacklog="0" receiveBufferSize="0"
sendBufferSize="0" serverSoTimeout="100000" socketSoLinger="0"
validateConnections="true" keepAlive="true">
<receiver-threading-profile
maxThreadsActive="5" maxThreadsIdle="5" />
<reconnect-forever />
<service-overrides messageReceiver="TCPMuleOut" />
<tcp:direct-protocol payloadOnly="true" />
</tcp:connector>
What you're trying to do is a little difficult to accomplish but not impossible. The messages are received by the org.mule.transport.tcp.TcpMessageReceiver class, and this class always consumes the data in the input stream to create the message that injects in the flow.
However, you could extend that receiver and instruct the TCP module to use yours by adding a service-overrides tag in your flow's tcp connector (documented here) and replacing the messageReceiver element.
In your extended receiver you should change the TcpWorker.getNextMessage method in order to send the ack message before read from the input stream.
HTH, Marcos.

WCF ChannelFactory and channels - caching, reusing, closing and recovery

I have the following planned architecture for my WCF client library:
using ChannelFactory instead of svcutil generated proxies because
I need more control and also I want to keep the client in a separate
assembly and avoid regenerating when my WCF service changes
need to apply a behavior with a message inspector to my WCF
endpoint, so each channel is able to send its
own authentication token
my client library will be used from a MVC front-end, so I'll have to think about possible threading issues
I'm using .NET 4.5 (maybe it has some helpers or new approaches to implement WCF clients in some better way?)
I have read many articles about various separate bits but I'm still confused about how to put it all together the right way. I have the following questions:
as I understand, it is recommended to cache ChannelFactory in a static variable and then get channels out of it, right?
is endpoint behavior specific to the entire ChannelFactory or I can apply my authentication behavior for each channel separately? If the behavior is specific to the entire factory, this means that I cannot keep any state information in my endpoint behavior objects because the same auth token will get reused for every channel, but obviously I want each channel to have its own auth token for the current user. This means, that I'll have to calculate the token inside of my endpoint behavior (I can keep it in HttpContext, and my message inspector behavior will just add it to the outgoing messages).
my client class is disposable (implements IDispose). How do I dispose the channel correctly, knowing that it might be in any possible state (not opened, opened, failed ...)? Do I just dispose it? Do I abort it and then dispose? Do I close it (but it might be not opened yet at all) and then dispose?
what do I do if I get some fault when working with the channel? Is only the channel broken or entire ChannelFactory is broken?
I guess, a line of code speaks more than a thousand words, so here is my idea in code form. I have marked all my questions above with "???" in the code.
public class MyServiceClient : IDisposable
{
// channel factory cache
private static ChannelFactory<IMyService> _factory;
private static object _lock = new object();
private IMyService _client = null;
private bool _isDisposed = false;
/// <summary>
/// Creates a channel for the service
/// </summary>
public MyServiceClient()
{
lock (_lock)
{
if (_factory == null)
{
// ... set up custom bindings here and get some config values
var endpoint = new EndpointAddress(myServiceUrl);
_factory = new ChannelFactory<IMyService>(binding, endpoint);
// ???? do I add my auth behavior for entire ChannelFactory
// or I can apply it for individual channels when I create them?
}
}
_client = _factory.CreateChannel();
}
public string MyMethod()
{
RequireClientInWorkingState();
try
{
return _client.MyMethod();
}
catch
{
RecoverFromChannelFailure();
throw;
}
}
private void RequireClientInWorkingState()
{
if (_isDisposed)
throw new InvalidOperationException("This client was disposed. Create a new one.");
// ??? is it enough to check for CommunicationState.Opened && Created?
if (state != CommunicationState.Created && state != CommunicationState.Opened)
throw new InvalidOperationException("The client channel is not ready to work. Create a new one.");
}
private void RecoverFromChannelFailure()
{
// ??? is it the best way to check if there was a problem with the channel?
if (((IChannel)_client).State != CommunicationState.Opened)
{
// ??? is it safe to call Abort? won't it throw?
((IChannel)_client).Abort();
}
// ??? and what about ChannelFactory?
// will it still be able to create channels or it also might be broken and must be thrown away?
// In that case, how do I clean up ChannelFactory correctly before creating a new one?
}
#region IDisposable
public void Dispose()
{
// ??? is it how to free the channel correctly?
// I've heard, broken channels might throw when closing
// ??? what if it is not opened yet?
// ??? what if it is in fault state?
try
{
((IChannel)_client).Close();
}
catch
{
((IChannel)_client).Abort();
}
((IDisposable)_client).Dispose();
_client = null;
_isDisposed = true;
}
#endregion
}
I guess better late then never... and looks like author has it working, this might help future WCF users.
1) ChannelFactory arranges the channel which includes all behaviors for the channel. Creating the channel via CreateChannel method "activates" the channel. Channel factories can be cached.
2) You shape the channel factory with bindings and behaviors. This shape is shared with everyone who creates this channel. As you noted in your comment you can attach message inspectors but more common case is to use Header to send custom state information to the service. You can attach headers via OperationContext.Current
using (var op = new OperationContextScope((IContextChannel)proxy))
{
var header = new MessageHeader<string>("Some State");
var hout = header.GetUntypedHeader("message", "urn:someNamespace");
OperationContext.Current.OutgoingMessageHeaders.Add(hout);
}
3) This is my general way of disposing the client channel and factory (this method is part of my ProxyBase class)
public virtual void Dispose()
{
CloseChannel();
CloseFactory();
}
protected void CloseChannel()
{
if (((IChannel)_client).State == CommunicationState.Opened)
{
try
{
((IChannel)_client).Close();
}
catch (TimeoutException /* timeout */)
{
// Handle the timeout exception
((IChannel)innerChannel).Abort();
}
catch (CommunicationException /* communicationException */)
{
// Handle the communication exception
((IChannel)_client).Abort();
}
}
}
protected void CloseFactory()
{
if (Factory.State == CommunicationState.Opened)
{
try
{
Factory.Close();
}
catch (TimeoutException /* timeout */)
{
// Handle the timeout exception
Factory.Abort();
}
catch (CommunicationException /* communicationException */)
{
// Handle the communication exception
Factory.Abort();
}
}
}
4) WCF will fault the channel not the factory. You can implement a re-connect logic but that would require that you create and derive your clients from some custom ProxyBase e.g.
protected I Channel
{
get
{
lock (_channelLock)
{
if (! object.Equals(innerChannel, default(I)))
{
ICommunicationObject channelObject = innerChannel as ICommunicationObject;
if ((channelObject.State == CommunicationState.Faulted) || (channelObject.State == CommunicationState.Closed))
{
// Channel is faulted or closing for some reason, attempt to recreate channel
innerChannel = default(I);
}
}
if (object.Equals(innerChannel, default(I)))
{
Debug.Assert(Factory != null);
innerChannel = Factory.CreateChannel();
((ICommunicationObject)innerChannel).Faulted += new EventHandler(Channel_Faulted);
}
}
return innerChannel;
}
}
5) Do not re-use channels. Open, do something, close is the normal usage pattern.
6) Create common proxy base class and derive all your clients from it. This can be helpful, like re-connecting, using pre-invoke/post invoke logic, consuming events from factory (e.g. Faulted, Opening)
7) Create your own CustomChannelFactory this gives you further control how factory behaves e.g. Set default timeouts, enforce various binding settings (MaxMessageSizes) etc.
public static void SetTimeouts(Binding binding, TimeSpan? timeout = null, TimeSpan? debugTimeout = null)
{
if (timeout == null)
{
timeout = new TimeSpan(0, 0, 1, 0);
}
if (debugTimeout == null)
{
debugTimeout = new TimeSpan(0, 0, 10, 0);
}
if (Debugger.IsAttached)
{
binding.ReceiveTimeout = debugTimeout.Value;
binding.SendTimeout = debugTimeout.Value;
}
else
{
binding.ReceiveTimeout = timeout.Value;
binding.SendTimeout = timeout.Value;
}
}

How to create client proxy without svcutil or add service reference in wcf?

How can I create a client proxy without svcutil.exe or add service reference in wcf?
I want to create a client proxy at compile time.
If you have access to the service contract (the IService interface) in a separate DLL, you can add a reference to that service contract DLL and then do something like:
NetTcpBinding binding = new NetTcpBinding();
EndpointAddress address = new EndpointAddress("net.tcp://localhost:9000/YourService")
ChannelFactory<IService> factory = new ChannelFactory<IService>(binding, address);
IService proxy = factory.CreateChannel();
and then you have your programmatically created proxy, which you can now use as you wish.
This might not be what you are looking for, but it's pretty interesting.
Vipul Modi has a library that allows you to call WCF services after downloading their WSDL, all at runtime.
Vipul Modi's library (latest version)
Allows you to do this kind of thing:
Create the ProxyFactory specifying the WSDL URI of the service.
DynamicProxyFactory factory = new DynamicProxyFactory("http://localhost:8080/WcfSamples/DynamicProxy?wsdl");
Browse the endpoints, metadata, contracts etc.
factory.Endpoints
factory.Metadata
factory.Contracts
factory.Bindings
Create DynamicProxy to an endpoint by specifying either the endpoint or
contract name.
DynamicProxy proxy = factory.CreateProxy("ISimpleCalculator");
//OR
DynamicProxy proxy = factory.CreateProxy(endpoint);
Invoke operations on the DynamicProxy
double result = (double)proxy.CallMethod("Add", 1d ,2d);
Close the DynamicProxy
proxy.Close();
You don't need to code generate (or use a configuration file full of WCF specifics).
First create the interface defining the service ([ServiceContract]) with any supporting data contracts in an assembly separate from the service implementation.
Reference the interface assembly in the client assembly.
Then need to create a client proxy, for IMyService:
BasicHttpBinding binding = new BasicHttpBinding();
EndpointAddress endpoint = new EndpointAddress(url);
ChannelFactory<IMyService> chanFac = new ChannelFactory<IMyService>(binding, endpoint);
IMyService clientProxy = chanFac.CreateChannel();
Here is the solution I have been using since wcf was introduced:
In an infrastructure assembly:
internal class PerCallDisposeRealProxy<T> : RealProxy where T : class
{
private readonly Binding _binding;
private readonly EndpointAddress _endpointAddress;
private static string EndpointName
{
get
{
string endpointName = typeof(T).Name;
if (endpointName.StartsWith("I"))
{
endpointName = endpointName.Substring(1);
}
return endpointName;
}
}
internal PerCallDisposeRealProxy()
: base(typeof(T))
{
}
internal PerCallDisposeRealProxy(Binding binding, EndpointAddress endpointAddress)
: base(typeof(T))
{
if (binding == null)
throw new ArgumentNullException("binding");
if (endpointAddress == null)
throw new ArgumentNullException("endpointAddress");
_binding = binding;
_endpointAddress = endpointAddress;
}
private ChannelFactory<T> CreateChannel()
{
if (_binding != null && _endpointAddress != null)
return new ChannelFactory<T>(_binding, _endpointAddress);
else
return new ChannelFactory<T>(EndpointName);
}
[DebuggerStepThrough]
public override IMessage Invoke(IMessage message)
{
if (message == null) throw new ArgumentNullException("message");
//Extract method info
var methodCall = message as IMethodCallMessage;
Debug.Assert(methodCall != null);
MethodInfo methodInfo = methodCall.MethodBase as MethodInfo;
Debug.Assert(methodInfo != null);
T channel = null;
ChannelFactory<T> channelFactory = null;
try
{
//setup channel
try
{
channelFactory = CreateChannel();
}
catch (InvalidOperationException ex)
{
throw new ApplicationException(string.Format("Invalid endpoint configuration, make sure there is a servicemodel endpoint defined in configuration with the name {0}", EndpointName), ex);
}
channelFactory.Open();
channel = channelFactory.CreateChannel();
object result = methodInfo.Invoke(channel, methodCall.InArgs);
return new ReturnMessage(result, null, 0, methodCall.LogicalCallContext, methodCall);
}
catch (FaultException faultException)
{
string msg = "FaultException: " + faultException.Message;
MessageFault fault = faultException.CreateMessageFault();
if (fault.HasDetail)
{
System.Xml.XmlReader reader = fault.GetReaderAtDetailContents();
if (reader.Name == "ExceptionDetail")
{
ExceptionDetail detail = fault.GetDetail<ExceptionDetail>();
msg += "\n\nStack Trace: " + detail.StackTrace;
}
}
return new ReturnMessage(faultException, methodCall);
}
catch (TargetInvocationException targetInvocationException)
{
return targetInvocationException.InnerException != null ? new ReturnMessage(targetInvocationException.InnerException, methodCall) : new ReturnMessage(targetInvocationException, methodCall);
}
catch (Exception exception)
{
return new ReturnMessage(exception, methodCall);
}
finally
{
if (channel as IClientChannel != null)
{
try
{
(channel as IClientChannel).Close(TimeSpan.FromSeconds(5));
}
catch
{
try
{
(channel as IClientChannel).Abort();
}
catch
{
}
}
try
{
(channel as IClientChannel).Dispose();
}
catch
{
}
}
try
{
((IDisposable)channelFactory).Dispose();
}
catch
{
}
}
}
}
public static class ClientProxyFactory
{
public static T CreateProxy<T>() where T : class
{
return CreateProxy<T>(ProxyType.PerCallChannel);
}
public static T CreateProxy<T>(ProxyType proxyType) where T : class
{
return CreateProxy<T>(proxyType, null, null);
}
public static T CreateProxy<T>(ProxyType proxyType, Binding binding, EndpointAddress endpointAddress) where T : class
{
switch (proxyType)
{
case ProxyType.PerCallChannel:
PerCallDisposeRealProxy<T> proxy = null;
proxy = binding == null && endpointAddress == null ? new PerCallDisposeRealProxy<T>() : new PerCallDisposeRealProxy<T>(binding, endpointAddress);
Debug.Assert(proxy != null);
object transparentProxy = proxy.GetTransparentProxy();
Debug.Assert(transparentProxy != null);
Debug.Assert(transparentProxy is T);
return transparentProxy as T;
default:
throw new NotImplementedException("Did not implement proxytype:" + proxyType);
}
}
}
public enum ProxyType
{
/// <summary>
/// PerCall indicates a proxy that will create a channel pr. proxy method call and dispose of it before returning.
/// </summary>
PerCallChannel
}
And call site (in the service agent or whereever you want to call the external service from:
INumeralConverterService proxy = ClientProxyFactory.CreateProxy<INumeralConverterService>();
string result = proxy.DecimalToRoman(i);
Given the ServiceContract (and datacontracts) defined in yet another asssembly, here simply:
[ServiceContract]
public interface INumeralConverterService
{
[OperationContract]
Decimal RomanToDecimal(string roman);
[OperationContract]
string DecimalToRoman(Decimal #decimal);
}