I need to receive small UDP packets from within my ERP system (Dynamics Nav). I created a class with some code from the internet (first time I use code for UDP), added a COM interface and use the resulting DLL within Nav. It worked right away, but I realized that the udp.Close() function did not really close the thing, and I couldn´t instantiate the class a second time.
After looking at many topics about udp.BeginReceive() at stackoverflow and on other sites, I understand the reason for that. There needs to be a final udp.EndReceive call before closing the upd object.
Since the class is running in the background its using a callback function defined by udp.BeginReceive. The callback function then gets the data by calling udp.EndReceive and finally stores it to a string. Nav can retrieve that string whenever it wants that data trough a simple property.
public string GetMessage { get { return(message); } }
public void Start()
{
udp = new UdpClient(PORT_NUMBER);
StartListening();
}
public void Stop()
{
udp.Close();
}
private void StartListening()
{
ar_ = udp.BeginReceive(Receive, new object());
}
private void Receive(IAsyncResult ar)
{
IPEndPoint ip = new IPEndPoint(IPAddress.Any, PORT_NUMBER);
byte[] bytes = udp.EndReceive(ar, ref ip);
message = Encoding.ASCII.GetString(bytes);
StartListening();
}
Everthing is fine, except ...
Nav calls Start() which issues StartListening(), which defines the callback function. After receiving the data through udp.EndReceive it calls StartListening() again - this part is working fine.
As soon, as Nav calls the Stop() function however, the trouble starts, and I understand that this is, bacause there is no final call to EndReceive and thus an open session.
One may say, why don´t do an EndReceive() within the Stop() function before udp.Close()? Well, because I couldn´t find the correct parameters for that call.
Actually I do have a working class. I don´t try to close the session within the Stop() function but instead set a bool variable. The next time the callback function is issued, depending on that bool it doesn´t do a StartListening() but a upd.CLose() instead. And finally, to make sure there will be data to issue the callback function, I call my Send() function sending some single character.
While the following code is working perfectly, I know it´s kind of crazy:
public string GetMessage { get { return(message); } }
public void Start()
{
active = true;
udp = new UdpClient(PORT_NUMBER);
StartListening();
}
public void Stop()
{
active = false; // force callback function to close session
Send("x"); // issue callback function ... crazy
//udp.Close();
}
private void StartListening()
{
ar_ = udp.BeginReceive(Receive, new object());
}
private void Receive(IAsyncResult ar)
{
IPEndPoint ip = new IPEndPoint(IPAddress.Any, PORT_NUMBER);
byte[] bytes = udp.EndReceive(ar, ref ip);
message = Encoding.ASCII.GetString(bytes);
if (active) StartListening(); else udp.Close();
}
Does anyone have a hint how to issue EndReceive() within my Stop() function before calling udp.Close()?
Thanks in advance
Michael
I recently found a solution just by using a Thread() instead of the async receive - works perfectly so far:
public class Bos_UDP
{
private UdpClient udp;
const int PORT_NUMBER = 15000;
private String message = "";
public Thread receiveThread;
public string GetMessage { get { return(message); } }
public void Start()
{
udp = new UdpClient(PORT_NUMBER);
receiveThread = new Thread(ThreadReceive);
receiveThread.Start();
}
public void Stop()
{
receiveThread.Abort(new object());
udp.Close();
}
public void ThreadReceive()
{
IPEndPoint ip = new IPEndPoint(IPAddress.Any, PORT_NUMBER);
while (true)
{
var data = udp.Receive(ref ip);
message = Encoding.Default.GetString(data);
}
}
public void Send(string message)
{
UdpClient client = new UdpClient();
IPEndPoint ip = new IPEndPoint(IPAddress.Parse("255.255.255.255"), PORT_NUMBER);
byte[] bytes = Encoding.ASCII.GetBytes(message);
client.Send(bytes, bytes.Length, ip);
client.Close();
}
}
Related
I'm trying to push request/response into elasticsearch but I'm stuck after reading documentation when trying to get body of response. It's stated there: "While enabling buffering is possible, it's discouraged as it can add significant memory and latency overhead. Using a wrapped, streaming approach is recommended if the body must be examined or modified".
So this part is quite understandable since buffered response might be saved into file.
"See the ResponseCompression middleware for an example." (Full article)
I checked what is in there and I'm stuck. Should I create class that implements IHttpResponseBodyFeature?
I've implemented simple class that implements that interface:
internal class BodyReader : IHttpResponseBodyFeature, IDisposable
{
private bool _disposedValue;
public Stream Stream { get; } = new MemoryStream();
public PipeWriter Writer => throw new NotImplementedException();
public Task CompleteAsync()
{
return Task.CompletedTask;
}
public void DisableBuffering()
{
//throw new NotImplementedException();
}
public Task SendFileAsync(string path, long offset, long? count, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task StartAsync(CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
protected virtual void Dispose(bool disposing)
{
if (!_disposedValue)
{
if (disposing)
{
// TODO: dispose managed state (managed objects)
Stream?.Dispose();
}
// TODO: free unmanaged resources (unmanaged objects) and override finalizer
// TODO: set large fields to null
_disposedValue = true;
}
}
// // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources
// ~Tmp()
// {
// // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
// Dispose(disposing: false);
// }
public void Dispose()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
And then in middleware:
var bodyReader = new BodyReader();
context.Features.Set<IHttpResponseBodyFeature>(bodyReader);
try
{
await _next(context);
bodyReader.Stream.Position = 0;
using (var sr = new StreamReader(bodyReader.Stream))
{
// here should be text response but unfortunately in variable is some garbage
// I'm guessing ciphered response?
var html = sr.ReadToEnd();
}
bodyReader.Dispose();
}
finally
{
context.Features.Set(originalBodyFeature);
}
Seems that in html variable is some garbage - maybe ciphered? Also don't have an idea how to push response into pipe once again.
I'm not sure if approach is good? Maybe I shouldn't use middleware to logging or my implementation of IHttpResponseBodyFeature is incorrect?
Either way I need to push into elastic both request and response :)
I asked about this on yarp's github and I got information that this is not because of https but compression (I simply forgot about it):
https://github.com/microsoft/reverse-proxy/issues/1921#issuecomment-1301287432
Long story short it was enough to add:
builder.Services.AddReverseProxy()
.ConfigureHttpClient((context, handler) =>
{
// this is required to decompress automatically
handler.AutomaticDecompression = System.Net.DecompressionMethods.All;
})
Happy coding :)
I ran into a problem in the last step of a test project using Photon Network. When you first connect and join the room, everything goes without errors. However, after completing the match, exiting the room, and using LoadScene(), errors appear:
JoinLobby operation (229) not called because client is not connected or not yet ready, client state: JoiningLob <- in OnConnectedToMaster()
Through experience, I realized that the ConnectUsingSettings() methods and other Photon methods are called multiple times. But the connection to the lobby happens and I can create a room, but I immediately encounter MissingReferenceException errors.
I've seen a solution from guys who ran into this very same problem. The problems arose because of the events. Wherever this could happen, I unsubscribed from the events, but that doesn't help. What else can cause such problems, because I obviously missed something that prevents me from completely closing the scene during the transition?
Sorry for my language, used Google Translate
Code:
LobbyManager.cs
private void StartConnect()
{
PhotonNetwork.NickName = master.GameSettings.NickName;
PhotonNetwork.GameVersion = master.GameSettings.NickName;
PhotonNetwork.ConnectUsingSettings();
PhotonNetwork.AutomaticallySyncScene = true;
}
public override void OnConnectedToMaster()
{
Debug.Log("Connected to server");
if(!PhotonNetwork.InLobby) PhotonNetwork.JoinLobby();
}
public override void OnJoinedLobby()
{
onConnected.Invoke();//This use for show UIElements on Canvas
}
JoinRandomRoom class
public void OnClick_JoinRandomRoom()
{
if (!PhotonNetwork.IsConnected) return;
if (GameModeGlobalData.SelectedGameMode != null)
{
SetRoomOptions();
PhotonNetwork.JoinRandomRoom(expectedRoomProperties, GameModeGlobalData.SelectedGameMode.MaxPlayers);
}
}
public override void OnJoinRandomFailed(short returnCode, string message)
{
Debug.Log("Join random failed: " + message + ". Room will be created...");
_createRoomMenu.CreateAndJoinRoom();
}
public void SetRoomOptions()
{
expectedRoomProperties[RoomData.GAME_MODE] = GameModeGlobalData.SelectedGameMode.GameModeName;
}
private void OnDisable()
{
ShowPanels.RemoveAllListeners();
}
And CreateRoom.cs
private ExitGames.Client.Photon.Hashtable _roomCustomProperties = new ExitGames.Client.Photon.Hashtable();
public void CreateAndJoinRoom()
{
if (!PhotonNetwork.IsConnected) return;
if (GameModeGlobalData.SelectedGameMode != null)
{
RoomOptions roomOptions = GetCustomRoomOptions();
roomOptions.CleanupCacheOnLeave = true;
PhotonNetwork.CreateRoom(randomRoomName, roomOptions);
}
}
public RoomOptions GetCustomRoomOptions()
{
RoomOptions options = new RoomOptions();
options.MaxPlayers = _maxPlayer;
options.IsOpen = true;
options.IsVisible = true;
string[] roomProperties = new string[]{ RoomData.GAME_MODE };
_roomCustomProperties[RoomData.GAME_MODE] = GameModeGlobalData.SelectedGameMode.GameModeName;
options.CustomRoomPropertiesForLobby = roomProperties;
options.CustomRoomProperties = _roomCustomProperties;
return options;
}
The project has grown, and I blame myself for not testing it at the very beginning. Didn't think there would be problems at this stage
Sorry for this post. Its resolved. For those who may encounter this in the future, in addition to unsubscribing from events, check all classes that inherit from MonoBehaviourPunCallbacks for overridden OnDisable() methods.
Like this:
public override void OnDisable()
{
base.OnDisable();
}
This in turn will call the
PhotonNetwork.RemoveCallbackTarget(this);
Also, from the documentation:
Do not add new MonoBehaviour.OnEnable or MonoBehaviour.OnDisable. Instead, you should override those and call base.OnEnable and base.OnDisable.
I forgot about it and used MonoBehaviour.OnDisable.
I can call InvokeAsync from server code using the IHubContext interface, but sometimes I want to force these clients to disconnect.
So, is there any way to disconnect clients from server code that references the IHubContext interface?
Step 1:
using Microsoft.AspNetCore.Connections.Features;
using System.Collections.Generic;
using Microsoft.AspNetCore.SignalR;
public class ErrorService
{
readonly HashSet<string> PendingConnections = new HashSet<string>();
readonly object PendingConnectionsLock = new object();
public void KickClient(string ConnectionId)
{
//TODO: log
if (!PendingConnections.Contains(ConnectionId))
{
lock (PendingConnectionsLock)
{
PendingConnections.Add(ConnectionId);
}
}
}
public void InitConnectionMonitoring(HubCallerContext Context)
{
var feature = Context.Features.Get<IConnectionHeartbeatFeature>();
feature.OnHeartbeat(state =>
{
if (PendingConnections.Contains(Context.ConnectionId))
{
Context.Abort();
lock (PendingConnectionsLock)
{
PendingConnections.Remove(Context.ConnectionId);
}
}
}, Context.ConnectionId);
}
}
Step 2:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddSingleton<ErrorService>();
...
}
Step 3:
[Authorize(Policy = "Client")]
public class ClientHub : Hub
{
ErrorService errorService;
public ClientHub(ErrorService errorService)
{
this.errorService = errorService;
}
public async override Task OnConnectedAsync()
{
errorService.InitConnectionMonitoring(Context);
await base.OnConnectedAsync();
}
....
Disconnecting without Abort() method:
public class TestService
{
public TestService(..., ErrorService errorService)
{
string ConnectionId = ...;
errorService.KickClient(ConnectionId);
In alpha 2 there is the Abort() on HubConnectionContext you could use to terminate a connection. I don't see, however, an easy way to access it from outside the hub.
Because you control the clients you could just invoke a client method and tell the client to disconnect. The advantage is that the client disconnect gracefully. The disadvantage is that it requires sending the message to the client instead of disconnecting the client solely on the server side.
I'm a new to MassTransit and one thing I don't understand is this:
How do you create a bus between multiple .net ServiceBuses?
Take this code:
class Program
{
static void Main(string[] args)
{
var container = new WindsorContainer();
container.Register(AllTypes.FromThisAssembly().BasedOn<IConsumer>());
Console.WriteLine("Starting Buses!");
var bus1 = ServiceBusFactory.New(sbc =>
{
sbc.UseMsmq();
sbc.UseMulticastSubscriptionClient();
sbc.ReceiveFrom("msmq://localhost/bus1");
sbc.Subscribe(s => s.LoadFrom(container));
});
var bus2 = ServiceBusFactory.New(sbc =>
{
sbc.UseMsmq();
sbc.UseMulticastSubscriptionClient();
sbc.ReceiveFrom("msmq://localhost/bus2");
sbc.Subscribe(s => s.LoadFrom(container));
});
bus1.Publish(new TestMsg() { Name = "Hello Matt!" });
Console.WriteLine("Sent Message");
Console.ReadLine();
}
}
public class TestMsg
{
public string Name { get; set; }
}
public class TestMsgConsumer : Consumes<TestMsg>.All, IBusService
{
private IServiceBus bus;
public void Consume(TestMsg message)
{
Console.WriteLine("Got message on " + this.Context().Endpoint.Address);
}
public void Dispose()
{
if (bus != null)
bus.Dispose();
}
public void Start(IServiceBus bus)
{
this.bus = bus;
}
public void Stop()
{
}
}
I would expect the "Got message on ..." to show up twice, since I have two buses. However, I only get one. Obviously there is some step that ties these two Bus instances to the same logical bus I'm not understanding what that is. I can't point them at the same queue name since, again, only one would get the message.
Thanks!
EDIT/SOLUTION:
I got some help from the MT Google Groups and they got me straightened away quickly... this wasn't working as expected because I didn't have the multicast bits of MSMQ installed. Got those installed and it worked as expected.
See the original post for the answer, but it basically came down to not having the Multicast bits of MSMQ installed. Put those in and all works as expected.
I can't figure out how to update my celltable after changes have been made using an editor. If I could get the edited proxy then I can use the dataprovider to update my celltable.
public void saveCampaign() {
driver.flush();
// the problem. proxy at this point should have the new values?....
context.persist().using(proxy).fire(new Receiver<Void>() {
#Override
public void onSuccess(Void response) {
showListView();
}
});
}
The proxy in .using(proxy) does not contain the changes made on the editor. However the persist method on the server gets the updated values. If I reload the data from the server I get the correct values to the celltable.
public void editCampaign(CampaignProxy proxy) {
this.proxy = proxy;
if (proxy != null) {
context = AEHOME.requestFactory.campaignRequest();
showEditView();
}
}
private void showEditView() {
driver.initialize(eventBus, AEHOME.requestFactory, editView);
driver.edit(proxy, context);
deckPanel.showWidget(deckPanel.getWidgetIndex(editView));
}
Proxy is set in the list view: configurationPageView.proxy = selectionModel.getSelectedObject();
Any advice would be greatly appreciated. Thank you.
You can change how the request is built by doing the following:
private void showEditView() {
driver.initialize(eventBus, AEHOME.requestFactory, editView);
driver.edit(proxy, context);
// Set up method invocation and callback in advance
context.persist().using(proxy).to(new Receiver<Void>() {
#Override
public void onSuccess(Void response) {
showListView();
}
}););
deckPanel.showWidget(deckPanel.getWidgetIndex(editView));
}
public void saveCampaign() {
driver.flush().fire();
}
In GWT 2.4 it will be possible to keep your current code organization and use RequestContext.append():
public void saveCampaign() {
// Returns the context passed to edit()
RequestContext ctx = driver.flush();
// append() is generic and returns the type returned by myProxyContext();
ctx.append(requestFactory.myProxyContext()).persist().using(proxy).fire(....);
}