HoloLens application based on Unity not able to receive TCP data - unity3d

I am working on an application for the HoloLens (1) based on Unity. I am trying to send data via TCP from a Qt-based application (Windows) to the HoloLens. Wireshark shows, that my test package is correctly sent and acknowledged by the HoloLens. The data starts with four bytes containing the length of the message, which starts right after those four bytes. In the following code, first, the four bytes are trying to be received, and afterwards, the message itself.
After executing the code for connecting and sending the data in the client application, StreamSocketListener_ConnectionReceived() is called on the HoloLens. In the next Update() call, ReadTcpData() is called. Now, execution 'stops' at the first call to
tcpMessageSizeBuffer[tcpMessageSizeBytesRead++] = (byte)stream.ReadByte();
At least the debugger jumps out of the code when I try to step over the line. There is no exception caught in the catch block.
I was expecting that the EndOfStream property of StreamReader would be false if there is nothing to read, and consequently, I would receive a valid byte from the stream when calling ReadByte when it was true. I have no clue what goes wrong here. Any hints would be greatly appreciated.
using System;
using System.IO;
using UnityEngine;
#if UNITY_WINRT && !UNITY_EDITOR
using Windows.Networking.Sockets;
using Windows.Networking;
using Windows.Networking.Connectivity;
using Windows.Storage.Streams;
using System.Runtime.InteropServices.WindowsRuntime;
#endif
public class NetworkManager : MonoBehaviour
{
public int tcpPort;
#if UNITY_WINRT && !UNITY_EDITOR
private StreamSocketListener streamSocketListener;
private StreamReader tcpStreamReader = null;
private StreamSocketListenerConnectionReceivedEventArgs connectionArgs;
private DatagramSocket datagramSocket;
private MemoryStream pendingDatagramSocketStream;
#endif
private UInt32 tcpMessageSizeByteCount;
private UInt32 tcpMessageSizeBytesRead;
private byte[] tcpMessageSizeBuffer;
private bool tcpMessageSizeReceived;
private UInt32 tcpMessageByteCount;
private UInt32 tcpMessageBytesRead;
private byte[] tcpMessageBuffer;
// Use this for initialization
void Start()
{
Application.runInBackground = true;
ResetTcpConnection();
}
public void ResetTcpConnection()
{
#if UNITY_WINRT && !UNITY_EDITOR
ResetTcpRead();
if (streamSocketListener != null)
{
tcpStreamReader.Dispose();
tcpStreamReader = null;
connectionArgs = null;
streamSocketListener.Dispose();
streamSocketListener = null;
}
streamSocketListener = new StreamSocketListener();
streamSocketListener.ConnectionReceived += this.StreamSocketListener_ConnectionReceived;
streamSocketListener.BindServiceNameAsync(tcpPort.ToString());
#endif
}
#if UNITY_WINRT && !UNITY_EDITOR
// Process the client connection.
private async void StreamSocketListener_ConnectionReceived(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
{
connectionArgs = args; // Keep this alive
tcpStreamReader = new StreamReader(connectionArgs.Socket.InputStream.AsStreamForRead());
}
#endif
// Update is called once per frame
void Update()
{
#if UNITY_WINRT && !UNITY_EDITOR
if (tcpStreamReader != null && !tcpStreamReader.EndOfStream)
{
Stream stream = tcpStreamReader.BaseStream;
ReadTcpData(ref stream);
}
#endif
}
private void ResetTcpRead()
{
tcpMessageSizeByteCount = sizeof(UInt32);
tcpMessageSizeBytesRead = 0;
tcpMessageSizeBuffer = new byte[tcpMessageSizeByteCount];
tcpMessageSizeReceived = false;
tcpMessageByteCount = 0;
tcpMessageBytesRead = 0;
tcpMessageBuffer = null;
}
private void ReadTcpData(ref Stream stream)
{
try
{
if (!tcpMessageSizeReceived)
{
#if UNITY_WINRT && !UNITY_EDITOR
while(tcpMessageSizeBytesRead < tcpMessageSizeByteCount && !tcpStreamReader.EndOfStream)
{
tcpMessageSizeBuffer[tcpMessageSizeBytesRead++] = (byte)stream.ReadByte();
}
if(tcpMessageSizeBytesRead == tcpMessageSizeByteCount)
{
tcpMessageByteCount = BitConverter.ToUInt32(tcpMessageSizeBuffer, 0);
}
#endif
if (tcpMessageByteCount > 0)
{
tcpMessageBuffer = new byte[tcpMessageByteCount];
tcpMessageSizeReceived = true;
}
}
else
{
// Read the message
int bytesToRead = (int)(tcpMessageByteCount - tcpMessageBytesRead);
bytesToRead = bytesToRead > 10000 ? 10000 : bytesToRead;
int bytesRead = stream.Read(tcpMessageBuffer, (int)tcpMessageBytesRead, bytesToRead);
tcpMessageBytesRead += (uint)bytesRead;
// Did we finish reading the message?
if (tcpMessageBytesRead >= tcpMessageByteCount)
{
ResetTcpRead();
}
}
}
catch (Exception e)
{
throw e;
}
}
}

Found the problem - I have to use BinaryReader and replace the code reading the length of the message such:
while(tcpMessageSizeBytesRead < tcpMessageSizeByteCount)
{
tcpMessageSizeBuffer[tcpMessageSizeBytesRead++] = (byte)binaryReader.ReadByte();
}
For the sake of consistency, also the part of the code actually reading the message can be adapted to use BinaryReader.

Related

Unity Darkrift2 Game isn't connecting to server from different pc

I am making online multiplayer game with Darkrift2 and with Unity.
I deeply know how to make games but i am new at online part.
I made the game succesfully with LAN.
I done the server side.
But just the game that opened with server can connect.
I am putting the server to my pc and go and open the game in another pc.
It can't connect.
I could definitely make an obvious mistake but IPV4 ADDRESS IS SAME AS PC'S IPV4 ADDRESS AND PORT NO IS SAME ON EVERY PC.
Also there is no firewall ban, Game is opening in another pc's.
By the way probably you don't have to read the whole code, Just the connection part.
Client
NetworkManager.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DarkRift.Client.Unity;
using DarkRift;
using Tags;
using DarkRift.Client;
using UnityEngine.InputSystem;
using System.Net;
using System.Linq;
public class NetworkManager : MonoBehaviour
{
IPAddress IPv4;
public UnityClient client;
public GameObject Bug;
public Transform[] PlayerBegin;
public Light DirLight;
public List<GameObject> Bugs;
private void Awake()
{
IPv4 = Dns.GetHostEntry(Dns.GetHostName())
.AddressList.First(
f => f.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork);
Debug.Log(IPv4);
client.Connect(IPv4, client.Port, true);
client.MessageReceived += OnMessageReceived;
//DontDestroyOnLoad(gameObject);
}
private void OnDestroy()
{
client.MessageReceived -= OnMessageReceived;
}
public void Interaction(byte Condition)//legdeathi de içine koyunca sorun yaratır mı? sanırım hayır
{
using (DarkRiftWriter writer = DarkRiftWriter.Create())
{
writer.Write(client.ID);
writer.Write(Condition);
using (Message message = Message.Create((ushort)Models.Tags0.InteractionType, writer))
{
client.SendMessage(message, SendMode.Reliable);
}
}
}
public void PosRot(Vector3 Pos, Quaternion Rot, bool ThereIsPos)
{
XYZ Position=null;
if (ThereIsPos)
{
Position = new XYZ();
Position.X = Pos.x;
Position.Y = Pos.y;
Position.Z = Pos.z;
}
XYZ Rotation = new XYZ();
Rotation.X = Rot.x;
Rotation.Y = Rot.y;
Rotation.Z = Rot.z;
float RotW = Rot.w;
using (DarkRiftWriter writer = DarkRiftWriter.Create())
{
writer.Write(client.ID);
var tag = Models.Tags0.Rot;
if (ThereIsPos)
{
tag = Models.Tags0.PosRot;
writer.Write(Position);
}
writer.Write(Rotation);
writer.Write(RotW);
using (Message message = Message.Create((ushort)tag, writer))
{
client.SendMessage(message, SendMode.Unreliable);
}
}
}
public int PlayerId;
public ushort PlayerCount;
private void OnMessageReceived(object sender, MessageReceivedEventArgs e)
{
using (Message message = e.GetMessage())
{
using (DarkRiftReader reader = message.GetReader())
{
if (message.Tag == (ushort)Models.Tags0.InteractionType)
{
ushort Id = reader.ReadUInt16();
GameObject tarantula = Bugs[Id].transform.GetChild(0).gameObject;
byte Interaction = reader.ReadByte();
Spider s = tarantula.GetComponent<Spider>();
switch (Interaction)
{
case 0: s.Attack(); break;
case 1:
s.DefenceGetBug();
break;
case 2:
s.DefenceOff();
break;
case 3:
s.Death();
break;
case 4:
s.LegsHealthDown();
break;
default:
s.LegHealthDown(s.Legs[Interaction - 5], 3);
break;
}
}
byte ThereIsPos=0;
if(message.Tag == (ushort)Models.Tags0.PosRot)
{
ThereIsPos = 1;
}
else if(message.Tag == (ushort)Models.Tags0.Rot)
{
ThereIsPos = 2;
}
if (ThereIsPos != 0)
{
ushort Id = reader.ReadUInt16();
GameObject tarantula = Bugs[Id].transform.GetChild(0).gameObject;
if (ThereIsPos == 1)
{
XYZ Position = reader.ReadSerializable<XYZ>();
tarantula.transform.position = new Vector3(Position.X, Position.Y, Position.Z);
}
XYZ Rotation = reader.ReadSerializable<XYZ>();
float RotW = reader.ReadSingle();
tarantula.transform.rotation = new Quaternion(Rotation.X, Rotation.Y, Rotation.Z, RotW);
}
if (message.Tag == (ushort)Models.Tags0.NewPlayer)
{
PlayerCount = (ushort)(reader.ReadUInt16() + 1);
//Debug.Log("PlayerCount : " + PlayerCount);
PlayerId = Bugs.Count;//Player No
//Debug.Log("PlayerId : " + PlayerId);
while (PlayerId < PlayerCount)
{
Debug.Log("Player Spawn" + PlayerId);
GameObject bug = Instantiate(Bug, PlayerBegin[PlayerId].position, Quaternion.identity, null);
bug.name = "Bug" + PlayerId;
Spider spider;
spider = bug.GetComponentInChildren<Spider>();
spider.nm = this;
spider.DirLight = DirLight;
//spider.transform.position = PlayerBegin[PlayerId].position;
//PosChange(spider.gameObject.transform.position);
if (client.ID != PlayerId)
{
//spider.enabled = false;
bug.transform.Find("Camera").gameObject.SetActive(false);
bug.transform.Find("CM FreeLook1").gameObject.SetActive(false);
bug.transform.Find("Camera Late").gameObject.SetActive(false);
bug.transform.Find("Canvas").gameObject.SetActive(false);
bug.GetComponentInChildren<PlayerInput>().enabled = false;
}
Bugs.Add(bug);
PlayerId++;
}
}
}
}
}
}
Server code .Net (only the main class):
using System;
using DarkRift.Server;
using DarkRift;
using Tags;
namespace Bug_Wars_Online
{
public class BugWarsOnline : Plugin
{
public override bool ThreadSafe => false;
public override Version Version => new Version(1, 0, 0);
public BugWarsOnline(PluginLoadData pluginLoadData) : base(pluginLoadData)
{
ClientManager.ClientConnected += OnClientConnected;
ClientManager.ClientDisconnected += OnClientDisconnected;
}
private void OnClientConnected(object sender, ClientConnectedEventArgs e)
{
Console.WriteLine("Connected");
e.Client.MessageReceived += OnMessageReceived;
using (DarkRiftWriter writer = DarkRiftWriter.Create())
{
writer.Write(e.Client.ID);
writer.Write(0);
using (Message message = Message.Create((ushort)Models.Tags0.NewPlayer, writer))
{
foreach (IClient client in ClientManager.GetAllClients())
{
//if (client.ID != e.Client.ID)
{
client.SendMessage(message, SendMode.Reliable);
Console.WriteLine("Player" + client.ID + " Connected");
Console.WriteLine("ClientManager.Count" + ClientManager.Count);
}
}
}
}
}
private void OnClientDisconnected(object sender, ClientDisconnectedEventArgs e)
{
Console.WriteLine("Disconnected");
//destroy et!
}
private void OnMessageReceived(object sender, MessageReceivedEventArgs e)
{
using (Message message = e.GetMessage())
{
if (message.Tag == (ushort)Models.Tags0.PosRot|| message.Tag == (ushort)Models.Tags0.Rot)
{
foreach (IClient client in ClientManager.GetAllClients())
{
if (client.ID != e.Client.ID)
{
client.SendMessage(message, SendMode.Unreliable);
}
}
}
if (message.Tag == (ushort)Models.Tags0.InteractionType)
{
foreach (IClient client in ClientManager.GetAllClients())
{
if (client.ID != e.Client.ID)
{
client.SendMessage(message, SendMode.Reliable);
}
}
}
}
}
}
}
Okay I should have enter the ipv4 address of server’s pc.

Unity Mirror - networked game object not showing up

I'm using Unity Mirror for networking my app so that a central server (not host) can send commands to the clients it is connected to.
On play, the "game" will make the program a client or server automatically on play, so i don't have to use the Client/Server only buttons provided by the NetworkManagerHUD.
Currently I'm facing 2 problems:
Client disconnects right after a connection with server is made. When I override the OnClientConnect function, I put the line base.OnClientConnect(conn). After stepping into the original function, I conclude it is the autoCreatePlayer set to true that is causing this problem. (the server and client are two instances of the program running on the same computer as I can only test using localhost).
public override void OnClientConnect(NetworkConnection conn)
{
base.OnClientConnect(conn); //This line causes the error message
clientConnected = true;
GameObject[] prefabs = Resources.LoadAll<GameObject>("NetworkingComponents");
foreach (var prefab in prefabs)
{
NetworkClient.RegisterPrefab(prefab);
}
GameObject[] gos = Resources.LoadAll<GameObject>("NetworkingComponents");
}
Perhaps the most critical issue. Referring to the previous problem, if i did remove the line
base.OnClientConnect(conn), client can connect, but all networked gameobjects (with NetworkIdentity) are still not showing up when connected as client, even though the NetworkManagerHUD says the program is connected as client. (Strangely, they are showing up if connected as Server.)
Here is the rest of the overriden NetworkManager code.
public class MyNetworkManager : NetworkManager
{
public GameObject dropdown;
public Canvas canvas;
//---------------------------Networking stuff----------------------------------
public List<NetworkNode> networkedNodes { get; } = new List<NetworkNode>();
public List<Settings> networkedSettings { get; } = new List<Settings>();
public List<NetworkedVisualisersDisplay> visualisersDisplays { get; } = new List<NetworkedVisualisersDisplay>();
public List<Visualiser> visualisers{ get; } = new List<Visualiser>();
public static MyNetworkManager instance = null;
public NetworkedVisualisersDisplay visDisplayPrefab;
public NetworkNode networkNode;
private string homeName;
public volatile bool clientConnected = false;
public bool IsClientConnected()
{
return clientConnected;
}
//the purpose of having a delay is that we need to determine if the call to StartClient() actually started the player as a client. It could fail if it’s the first player instance on the network.
public IEnumerator DelayedStart()
{
//base.Start();
StartClient();
yield return new WaitForSeconds(2);
print("conn count " + NetworkServer.connections.Count);
if (!IsClientConnected())
{
NetworkClient.Disconnect();
print(“starting as server”);
StartServer();
clientConnected = false;
}
else
{
print("starting as client");
}
visDisplayPrefab = Resources.Load<NetworkedVisualisersDisplay>("NetworkingComponents/NetworkedVisualisersDisplay");
if (instance == null)
{
instance = this;
print("instance = " + this);
}
else
{
print("manager destroyed");
Destroy(gameObject);
}
yield return null;
}
//-----------------------------------------------------------------------------
public override void Start(){
StartCoroutine(DelayedStart());
}
public override void OnStartServer()
{
GameObject[] prefabs = Resources.LoadAll<GameObject>("NetworkingComponents");
foreach (var prefab in prefabs)
{
spawnPrefabs.Add(prefab);
}
}
public override void OnServerChangeScene(string scenename)
{
if (scenename.Equals("Visualisers"))
{
for (int i = 0; i < visualisersDisplays.Count; i++)
{
var conn = networkedNodes[i].connectionToClient;
NetworkedVisualisersDisplay visSceneInstance = Instantiate(visualisersDisplays[i]);
NetworkServer.Destroy(conn.identity.gameObject);
NetworkServer.ReplacePlayerForConnection(conn, visSceneInstance.gameObject);
}
}
else if (Settings.Instance.sceneNames.Contains(scenename))
{
for (int i = 0; i < visualisersDisplays.Count; i++)
{
var conn = visualisers[i].connectionToClient;
var visInstance = Instantiate(visualisers[i]);
NetworkServer.Destroy(conn.identity.gameObject);
NetworkServer.ReplacePlayerForConnection(conn, visInstance.gameObject);
}
}
}
public override void OnServerAddPlayer(NetworkConnection conn)
{
NetworkNode n = Instantiate(networkNode);
NetworkServer.AddPlayerForConnection(conn, n.gameObject);
NetworkNode.instance.DisplayMessage();
}
public override void OnClientConnect(NetworkConnection conn)
{
base.OnClientConnect(conn);
//we are connected as a client
clientConnected = true;
GameObject[] prefabs = Resources.LoadAll<GameObject>("NetworkingComponents");
foreach (var prefab in prefabs)
{
NetworkClient.RegisterPrefab(prefab);
}
}
}
Any help will be greatly appreciated!

Parse.com - iOS push notifications and Unity integration

I notice that Parse Unity support still doesn't provide push notification for iOS.
Has anyone implemented a Unity plugin or another solution to support iOS Push Notifications via Parse?
(Cross posted on Unity Answers.)
It's actually possible now, using a ParseObject to mock up the ParseInstallation object.
Gist here: https://gist.github.com/gfosco/a3d092651c32ba3385e6
Explanation in the Parse Google Group: https://groups.google.com/d/msg/parse-developers/ku8-r91_o6s/6ioQ9T2TP7wJ
Attach this script to a GameObject, replace the important parts with your own:
using UnityEngine;
using System.Collections;
using Parse;
public class PushBehaviorScript : MonoBehaviour {
bool tokenSent = false;
public ParseObject currentInstallation = null;
void Start () {
if (PlayerPrefs.HasKey ("currentInstallation")) {
string objId = PlayerPrefs.GetString ("currentInstallation");
currentInstallation = ParseObject.CreateWithoutData ("_Installation", objId);
}
if (currentInstallation == null) {
#if UNITY_IPHONE && !UNITY_EDITOR
NotificationServices.RegisterForRemoteNotificationTypes (RemoteNotificationType.Alert | RemoteNotificationType.Badge | RemoteNotificationType.Sound);
#endif
}
}
void FixedUpdate () {
if (!tokenSent && currentInstallation == null) {
#if UNITY_IPHONE && !UNITY_EDITOR
byte[] token = NotificationServices.deviceToken;
if(token != null) {
tokenSent = true;
string tokenString = System.BitConverter.ToString(token).Replace("-", "").ToLower();
Debug.Log ("OnTokenReived");
Debug.Log (tokenString);
ParseObject obj = new ParseObject("_Installation");
obj["deviceToken"] = tokenString;
obj["appIdentifier"] = "com.parse.unitypush";
obj["deviceType"] = "ios";
obj["timeZone"] = "UTC";
obj["appName"] = "UnityPushTest";
obj["appVersion"] = "1.0.0";
obj["parseVersion"] = "1.3.0";
obj.SaveAsync().ContinueWith(t =>
{
if (obj.ObjectId != null) {
PlayerPrefs.SetString ("currentInstallation", obj.ObjectId);
}
});
}
#endif
}
}
}
To implement iOS push with parse.com You need to get the token from apple first. Then save Current instalation
Unity does have this functionality build in now.
//push notification
bool tokenSent = false;
void RegisterForPush()
{
Debug.Log("Register for push");
tokenSent = false;
#if UNITY_IOS
UnityEngine.iOS.NotificationServices.RegisterForNotifications(NotificationType.Alert |
NotificationType.Badge |
NotificationType.Sound, true);
#endif
}
void Update () {
if (!tokenSent) {
byte[] token = UnityEngine.iOS.NotificationServices.deviceToken;
if (token != null) {
// send token to a provider
tokenSent = true;
string hexToken = "%" + System.BitConverter.ToString(token).Replace('-', '%');
#if UNITY_IOS
ParseManager.instance.RegisterForPush(hexToken);
#endif
}
}
}
And inside ParseManager (or whatever class you use to manage parse>client communication)
public void RegisterForPush(string token) {
Debug.Log("Parse updating instalation");
ParseInstallation instalation = ParseInstallation.CurrentInstallation;
instalation["deviceToken"] = token;
instalation["user"] = ParseUser.CurrentUser.ObjectId;
instalation["timeZoneOffset"] = TimeZone.CurrentTimeZone.GetUtcOffset(DateTime.Now);
instalation.SaveAsync();
}
Tested on Unity 5.1.2 and iOS 8.4

Java Socket ArrayList NullPointer Exception

Whenever I run my Chat Server it works until the client connects and then I get a Null Pointer exception at
connections.add(socket);
which crashes it and then this happens every time I run it.
I know my client side is fine because I watched a tutorial a few months ago on how to do this and that server works with my client without crashing
private static ServerSocket server;
private static Socket socket;
private static Scanner input;
private static PrintWriter output;
private static final int port = 444;
private static String message;
private static ArrayList<Socket> connections;
private static Server serverClass;
public Server() {
message = "";
connections = new ArrayList<Socket>();
serverClass = new Server();
}
public void run() {
try {
try {
input = new Scanner(socket.getInputStream());
output = new PrintWriter(socket.getOutputStream());
while(true) {
checkConnection();
if(!input.hasNext()) {
return;
}
message = input.nextLine();
System.out.println("Client said: " + message);
for(int i = 1; i <= connections.size(); i++) {
Socket tempSock = (Socket) connections.get(i - 1);
output.println(message);
output.flush();
}
}
}
finally {
socket.close();
}
}
catch(Exception e) {
System.out.print(e);
}
}
public void checkConnection() throws IOException {
if(!socket.isConnected()) {
for(int i = 1; i <= connections.size(); i++) {
if(connections.get(i) == socket) {
connections.remove(i);
}
}
for(int i = 1; i <= connections.size(); i++) {
Socket disconnectedUser = (Socket) connections.get(i - 1);
System.out.println(disconnectedUser + " has Disconnected!");
}
}
}
public static void main(String[] args) throws IOException{
try {
server = new ServerSocket(port);
System.out.println("Waiting for Clients... ");
while(true) {
socket = server.accept();
connections.add(socket);
System.out.println("Client connected from: " + socket.getLocalAddress().getHostName());
Thread thread = new Thread(serverClass);
thread.start();
}
}
catch(Exception e) {
e.printStackTrace();
}
}
If you get an NPE at connections.add(socket), it can only be because connections is null. Which it is, because you haven't constructed an instance of Server: instead you are trying to execute all its code from main(). The only place you call new Server() from is inside the constructor for Server, which you have clearly never executed at all, as you would have got a StackOverflowError from the infinite recursion.
This code is a real mess:
fix the infinite recursion
construct an instance of Server in your main() method
make connections non-static
make socket a local variable in the accept loop
fix your loops to iterate from 0 to connections.size()-1
and try again.

Asynchronous Client Socket ManualResetEvent holding up execution

I am attempting to utilize MSDN's Asynchronous Client Socket code sample to connect and control some home equipment. As I understand, the sample code's ReceiveCallback method uses an instance of the EventWaitHandle ManualResetEvent and the method receiveDone.WaitOne() to hold processing of the current thread until the thread receives a signal that all of the socket's data has been transmitted from the remote device. After all of the socket's data has been transmitted (the socket's data is empty and bytesRead = 0), the Waithandle is removed and the application continues processing.
Unfortunately, by stepping-through the execution of the code, it appears that after the last time that the client returns data from the remote device, ReceiveCallback never returns to see if the data-queue is empty (i.e. bytesRead = 0), and thus never enters the "else" condition in ReceiveCallback where the state of the ManualResetEvent would have been reset and the application would have continued to process. Thus, since it never enters the "else" condition, ManualResetEvent is never reset and the application freezes.
Although I can remove the "receiveDone.WaitOne()" method from the code - permitting execution without waiting for the ManualResetEvent's notification that all of the data has been received; this returns a data-string from the equipment that is typically incomplete.
Am I using this code sample incorrectly? Has anyone seen this before or had any experience on how to work-around this issue?
7/14/2012 - UPDATE: After further testing of the MSDN's Asynchronous Client Socket Example, it became clear that ReceiveCallback actually re-polls the port and the "bytesRead = 0" condition is satisfied only when the socket is released (i.e. client.Shutdown(SocketShutdown.Both); client.Close(); ). If I understand this correctly, this means that the connection has to be closed to get past the receiveDone.WaitOne() method. If the connection is closed to satisfy the WaitOne() Waithandle, it totally defeats the purpose of the application in that I had been hoping to leave the connection open so that the application could listen for equipment updates, which happen continually.
7/16/2012 - UPDATE: I have written to Microsoft Tech Support who have responded that "We're doing research on this issue. It might take some time before we get back to you." As such, it seems that it doesn't appear that this challenge can be resolved at this time through massaging this code.
Without the availability of the Asynchronous Client Socket example code as a foundation for writing asynchronous communication procedures, may I ask if anyone can please suggest a replacement routine that is more reliable? There are three pieces of equipment, each with it's own ip-address and port number. Thus, it would be ideal if a class could be utilized, where an instance could be created for each device. Additionally, the port must remain open to receive spontaneous updates continually sent by the equipment. Lastly, the updates do not have a end character or defined length signalling that the transmission of the message is complete, thus the routine must continually poll the port for available data. Any advice or suggestions would be greatly appreciated.
7/18/2012 - WORKAROUND: After spending a considerable amount of time attempting to get the MSDN's Asynchronous Client Socket code sample working, it became clear that I would have to look elsewhere to get the device responses continuously recognized by the program. In the hope to save someone else the brain-damage, I have included the work-around that I used which seems to work well to this point. If anyone has any suggestions, please don't hesitate to add to this question!
//
// ORIGINAL CODE ATTEMPT
//
public static Socket LutronClient;
public static String LutronResponse = String.Empty;
private const int LutronPort = 4999;
private const string LutronIP = "192.168.1.71";
private static ManualResetEvent LutronConnectDone = new ManualResetEvent(false);
private static ManualResetEvent LutronSendDone = new ManualResetEvent(false);
private static ManualResetEvent LutronReceiveDone = new ManualResetEvent(false);
private static void StartLutronClient()
{
try
{
var lutronIPAddress = IPAddress.Parse(LutronIP);
var lutronRemoteEP = new IPEndPoint(lutronIPAddress, LutronPort);
LutronClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
LutronClient.BeginConnect(lutronRemoteEP, LutronConnectCallback, LutronClient);
LutronConnectDone.WaitOne();
LutronSend(LutronClient, "sdl,14,100,0,S2\x0d");
LutronSendDone.WaitOne();
LutronReceive(LutronClient);
LutronReceiveDone.WaitOne(new TimeSpan(5000));
MessageBox.Show("Response received from Lutron: " + LutronResponse);
txtBoxLutron.Text = LutronResponse;
LutronClient.Shutdown(SocketShutdown.Both);
LutronClient.Close();
}
catch (Exception e) { MessageBox.Show(e.ToString()); }
}
private static void LutronConnectCallback(IAsyncResult lutronAr)
{
try
{
var lutronClient = (Socket)lutronAr.AsyncState;
lutronClient.EndConnect(lutronAr);
LutronConnectDone.Set();
}
catch (Exception e) { MessageBox.Show(e.ToString()); }
}
private static void LutronReceive(Socket lutronClient)
{
try
{
var lutronState = new LutronStateObject { LutronWorkSocket = lutronClient };
lutronClient.BeginReceive(lutronState.LutronBuffer, 0, LutronStateObject.BufferSize, 0, new AsyncCallback(LutronReceiveCallback), lutronState);
}
catch (Exception e) { MessageBox.Show(e.ToString()); }
}
private static void LutronReceiveCallback(IAsyncResult lutronAR)
{
try
{
var lutronState = (LutronStateObject)lutronAR.AsyncState;
var lutronClient = lutronState.LutronWorkSocket;
var bytesRead = lutronClient.EndReceive(lutronAR);
if (bytesRead > 0)
{
lutronState.LutronStringBuilder.AppendLine(Encoding.ASCII.GetString(lutronState.LutronBuffer, 0, bytesRead));
lutronClient.BeginReceive(lutronState.LutronBuffer, 0, LutronStateObject.BufferSize, 0, new AsyncCallback(LutronReceiveCallback), lutronState);
}
else
{
if (lutronState.LutronStringBuilder.Length > 0) { LutronResponse = lutronState.LutronStringBuilder.ToString(); }
LutronReceiveDone.Set();
}
}
catch (Exception e) { MessageBox.Show(e.ToString()); }
}
public static void LutronSend(Socket client, String data)
{
var byteData = Encoding.ASCII.GetBytes(data);
client.BeginSend(byteData, 0, byteData.Length, 0, LutronSendCallback, client);
}
private static void LutronSendCallback(IAsyncResult ar)
{
try
{
var client = (Socket)ar.AsyncState;
var bytesSent = client.EndSend(ar);
LutronSendDone.Set();
}
catch (Exception e) { MessageBox.Show(e.ToString()); }
}
public class LutronStateObject
{
public Socket LutronWorkSocket;
public const int BufferSize = 256;
public byte[] LutronBuffer = new byte[BufferSize];
public StringBuilder LutronStringBuilder = new StringBuilder();
}
}
This is the work-around I used:
//
// WORK-AROUND
//
using System;
using System.Windows.Forms;
namespace _GlobalCacheInterface
{
public partial class GlobalCacheDataScreen : Form
{
//Interface objects
private static GC_Interface _lutronInterface;
private const int LutronPort = 4999;
private const string LutronIP = "192.168.1.71";
delegate void ThreadSafeLutronCallback(string text);
private static GC_Interface _elanInterface;
private const int ElanPort = 4998;
private const string ElanIP = "192.168.1.70";
delegate void ThreadSafeElanCallback(string text);
private static GC_Interface _tuneSuiteInterface;
private const int TuneSuitePort = 5000;
private const string TuneSuiteIP = "192.168.1.70";
delegate void ThreadSafeTuneSuiteCallback(string text);
public GlobalCacheDataScreen()
{
InitializeComponent();
_lutronInterface = new GC_Interface(LutronIP, LutronPort);
_elanInterface = new GC_Interface(ElanIP, ElanPort);
_tuneSuiteInterface = new GC_Interface(TuneSuiteIP, TuneSuitePort);
// Create event handlers to notify application of available updated information.
_lutronInterface.DataAvailable += (s, e) => ThreadSafeTxtBoxLutron(_lutronInterface._returnString);
_elanInterface.DataAvailable += (s, e) => ThreadSafeTxtBoxElan(_elanInterface._returnString);
_tuneSuiteInterface.DataAvailable += (s, e) => ThreadSafeTxtBoxTuneSuite(_tuneSuiteInterface._returnString);
_lutronInterface.Connected += (s, e) => UpdateUI();
_elanInterface.Connected += (s, e) => UpdateUI();
_tuneSuiteInterface.Connected += (s, e) => UpdateUI();
UpdateUI();
}
private void UpdateUI()
{
_buttonConnectToLutron.Enabled = !_lutronInterface._isConnected;
_buttonConnectToElan.Enabled = !_elanInterface._isConnected;
_buttonConnectToTuneSuite.Enabled = !_tuneSuiteInterface._isConnected;
_buttonDisconnectFromLutron.Enabled = _lutronInterface._isConnected;
_buttonDisconnectFromElan.Enabled = _elanInterface._isConnected;
_buttonDisconnectFromTuneSuite.Enabled = _tuneSuiteInterface._isConnected;
string connectLutronStatus = _lutronInterface._isConnected ? "Connected" : "Not Connected";
string connectElanStatus = _elanInterface._isConnected ? "Connected" : "Not Connected";
string connectTuneSuiteStatus = _tuneSuiteInterface._isConnected ? "Connected" : "Not Connected";
_textBoxLutronConnectStatus.Text = connectLutronStatus;
_textBoxElanConnectStatus.Text = connectElanStatus;
_textBoxTuneSuiteConnectStatus.Text = connectTuneSuiteStatus;
}
private void ThreadSafeTxtBoxLutron(string message) { if (_lutronRichTextRxMessage.InvokeRequired) { var d = new ThreadSafeLutronCallback(ThreadSafeTxtBoxLutron); _lutronRichTextRxMessage.Invoke(d, new object[] { message }); } else { _lutronRichTextRxMessage.Text = message; } }
private void ThreadSafeTxtBoxElan(string message) { if (_elanRichTextRxMessage.InvokeRequired) { var d = new ThreadSafeElanCallback(ThreadSafeTxtBoxElan); _elanRichTextRxMessage.Invoke(d, new object[] { message }); } else { _elanRichTextRxMessage.Text = message; if (message.EndsWith("\r")) { MessageBoxEx.Show(message, "Message from Lutron Elan", 1000); } } }
private void ThreadSafeTxtBoxTuneSuite(string message) { if (_tuneSuiteRichTextRxMessage.InvokeRequired) { var d = new ThreadSafeTuneSuiteCallback(ThreadSafeTxtBoxTuneSuite); _tuneSuiteRichTextRxMessage.Invoke(d, new object[] { message }); } else { _tuneSuiteRichTextRxMessage.Text = message; if (message.EndsWith("\r")) { MessageBoxEx.Show(message, "Message from TuneSuite", 1000); } } }
private void _buttonConnectToLutron_Click(object sender, EventArgs e) { _lutronInterface.Connect(); }
private void _buttonDisconnectFromLutron_Click(object sender, EventArgs e) { _lutronInterface.Disconnect(); }
private void _buttonConnectToElan_Click(object sender, EventArgs e) { _elanInterface.Connect(); }
private void _buttonDisconnectFromElan_Click(object sender, EventArgs e) { _elanInterface.Disconnect(); }
private void _buttonConnectToTuneSuite_Click(object sender, EventArgs e) { _tuneSuiteInterface.Connect(); }
private void _buttonDisconnectFromTuneSuite_Click(object sender, EventArgs e) { _tuneSuiteInterface.Disconnect(); }
private void _buttonLutronSendMessage_Click(object sender, EventArgs e) { _lutronInterface.SendCommand(_lutronRichTextTxMessage.Text); }
private void _buttonElanSendMessage_Click(object sender, EventArgs e) { _elanInterface.SendCommand(_elanRichTextTxMessage.Text); }
private void _buttonTuneSuiteSendMessage_Click(object sender, EventArgs e) { _tuneSuiteInterface.SendCommand(_elanRichTextTxMessage.Text); }
private void _buttonLightOn_Click(object sender, EventArgs e) { _lutronInterface.SendCommand("sdl,14,100,0,S2"); }
private void _buttonLightOff_Click(object sender, EventArgs e) { _lutronInterface.SendCommand("sdl,14,0,0,S2"); }
private void _buttonStereoOnOff_Click(object sender, EventArgs e) { _elanInterface.SendCommand("sendir,4:3,1,40000,4,1,21,181,21,181,21,181,21,181,21,181,21,181,21,181,21,181,21,181,21,181,21,181,21,800"); }
private void _button30_Click(object sender, EventArgs e) { _tuneSuiteInterface.SendCommand("\xB8\x4D\xB5\x33\x30\x00\x30\x21\xB8"); }
private void _button31_Click(object sender, EventArgs e) { _tuneSuiteInterface.SendCommand("\xB8\x4D\xB5\x33\x31\x00\x30\x21\xB8"); }
private void _button26_Click(object sender, EventArgs e) { _tuneSuiteInterface.SendCommand("\xB8\x4D\xB5\x32\x36\x00\x30\x21\xB8"); }
}
}
and the GC_Interface class:
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Windows.Forms;
namespace _GlobalCacheInterface
{
class GC_Interface
{
// Declare an event handler to notify when updates are available.
public event EventHandler<EventArgs> DataAvailable;
public string _returnString = "";
// Declare an event handler to notify status of connection.
public event EventHandler<EventArgs> Connected;
public bool _isConnected;
public AsyncCallback ReceiveCallback;
public Socket Client;
private string _ipAddress;
private int _port;
private bool _waitForEndCharacter;
private byte _endCharacter;
byte[] m_DataBuffer = new byte[10];
IAsyncResult m_Result;
public GC_Interface(string ipAddress, int port) { Init(ipAddress, port, false, 0); }
private void Init(string ipAddress, int port, bool waitForEndCharacter, byte endCharacter)
{
_ipAddress = ipAddress;
_port = port;
_waitForEndCharacter = waitForEndCharacter;
_endCharacter = endCharacter;
_isConnected = false;
}
public bool Connect()
{
try
{
// Create a TCP/IP socket.
Client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// Establish the remote endpoint for the socket.
var address = IPAddress.Parse(_ipAddress);
var remoteEP = new IPEndPoint(address, _port);
// Connect to the remote endpoint.
Client.Connect(remoteEP);
if (Client.Connected)
{
_isConnected = true;
ConnectedEventHandler();
WaitForData();
}
return true;
}
catch (SocketException se) { MessageBox.Show("\n connection failed, is the server running?\n" + se.Message ); return false; }
}
public bool SendCommand(string command)
{
try
{
// Convert the string data to byte data using ASCII encoding.
var byteData = Encoding.Default.GetBytes(command);
// Add a carraige-return to the end.
var newArray = new byte[byteData.Length + 1];
byteData.CopyTo(newArray, 0);
newArray[newArray.Length - 1] = 13;
if (Client == null) { return false; }
Client.Send(newArray);
return true;
}
catch (SocketException se) { MessageBox.Show(se.Message); return false; }
}
public void WaitForData()
{
try
{
if (ReceiveCallback == null) { ReceiveCallback = new AsyncCallback(OnDataReceived); }
var theSocPkt = new SocketPacket { thisSocket = Client };
m_Result = Client.BeginReceive(theSocPkt.DataBuffer, 0, theSocPkt.DataBuffer.Length, SocketFlags.None, ReceiveCallback, theSocPkt);
}
catch (SocketException se) { MessageBox.Show(se.Message); }
}
public class SocketPacket
{
public System.Net.Sockets.Socket thisSocket;
public byte[] DataBuffer = new byte[1];
}
public void OnDataReceived(IAsyncResult asyn)
{
try
{
SocketPacket theSockId = (SocketPacket)asyn.AsyncState;
var iRx = theSockId.thisSocket.EndReceive(asyn);
char[] Chars = new char[iRx + 1];
System.Text.Decoder d = System.Text.Encoding.UTF8.GetDecoder();
int CharLen = d.GetChars(theSockId.DataBuffer, 0, iRx, Chars, 0);
System.String szData = new System.String(Chars);
_returnString = _returnString + szData.Replace("\0", "");
// When an update is received, raise DataAvailable event
DataAvailableEventHandler();
WaitForData();
}
catch (ObjectDisposedException) { System.Diagnostics.Debugger.Log(0, "1", "\nOnDataReceived: Socket has been closed\n"); }
catch (SocketException se) { MessageBox.Show(se.Message); }
}
public bool Disconnect()
{
try
{
if (Client == null) { return false; }
Client.Close();
Client = null;
_isConnected = false;
return true;
}
catch (Exception) { return false; }
}
protected virtual void DataAvailableEventHandler()
{
var handler = DataAvailable;
if (handler != null) { handler(this, EventArgs.Empty); }
}
protected virtual void ConnectedEventHandler()
{
var handler = Connected;
if (handler != null) { handler(this, EventArgs.Empty); }
}
}
}
I had the same issue, adding an Available check to the code fixed my problem. Below is the revised code.
private static void ReceiveCallback( IAsyncResult ar ) {
try {
StateObject state = (StateObject) ar.AsyncState;
Socket client = state.workSocket;
int bytesRead = client.EndReceive(ar);
if (bytesRead > 0) {
state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));
// Check if there is anymore data on the socket
if (client.Available > 0) {
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
}
}
if (bytesRead == 0 || client.Available == 0) {
if (state.sb.Length > 1) {
response = state.sb.ToString();
}
receiveDone.Set();
}
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
}
Hope that helps.
I'm sidestepping the question here. I try to answer what you need, not what you ask for:
Use synchronous code. It will be easier to understand, you don't need callbacks or events. Also, for low thread counts, it is likely to perform better.
You also avoid bugs that you have in your current code. If an exception occurs your computation never completes. Synchronous code does not have that problem.