How to make sockets work in xamarin? - sockets

In the Answer to this question Here:Server Client Application with .NET and Xamarin
the person who answered said: "On Xamarin.Android you can use all of the regular .Net socket classes"
I tried using the code in example of the Microsoft documentation and i had no errors but application just is just displaying like that on the phone:
If I delete the socket code it would display the page normally.My code behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using System.Net;
using System.Net.Sockets;
namespace App14
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class Page2 : ContentPage
{
public static string data = null;
public static void StartListening()
{
// Data buffer for incoming data.
byte[] bytes = new Byte[1024];
// Establish the local endpoint for the socket.
// Dns.GetHostName returns the name of the
// host running the application.
IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName());
IPAddress ipAddress = ipHostInfo.AddressList[0];
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 11000);
// Create a TCP/IP socket.
Socket listener = new Socket(ipAddress.AddressFamily,
SocketType.Stream, ProtocolType.Tcp);
try
{
listener.Bind(localEndPoint);
listener.Listen(10);
// Start listening for connections.
while (true)
{
var label = new Label() { Text = "searching for a connection" };
// Program is suspended while waiting for an incoming connection.
Socket handler = listener.Accept();
label.Text = "Found a Connection";
data = null;
// An incoming connection needs to be processed.
while (true)
{
int bytesRec = handler.Receive(bytes);
data += Encoding.ASCII.GetString(bytes, 0, bytesRec);
if (data.IndexOf("<EOF>") > -1)
{
break;
}
}
// Show the data on the console.
label.Text = "Text received" + data;
// Echo the data back to the client.
byte[] msg = Encoding.ASCII.GetBytes(data);
handler.Send(msg);
handler.Shutdown(SocketShutdown.Both);
handler.Close();
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
public Page2 ()
{
StartListening();
Title = "Sign in page";
InitializeComponent();
}
private void page2_click(object sender,EventArgs e)
{
Navigation.PushAsync(new Page1(), true);
}
}
}
Xaml File code:
Why is this?.and can you please provide an example of client mobile or server mobile socket

You are blocking the UI Thread with your StartListening call, since it has a infinite while loop.
The label you create in the while loop is never added as content on the page. Hence the text you add to it will never be shown. You already have a label defined with the name l on the page. In this case you could just use that to add text:
l.Text = "hello";
As commented, you should start your socket listening code on another thread to not block you UI. This could be as simple as writing Task.Run(() => StartListening());. Make sure you understand what this does and possibly how you cancel this Task again when navigating away from the page or during App lifecycle.

If you try to Connect from your mobile with the correct ip address and port number of the Listener when both devices are in the same LAN it should work. If one of the devices is behind a different LAN router/modem you won't be able to connect because the router will block all incoming connections unless you use a port routing mechanism like UPnP.

Related

Unity TCP client: connection to a server

I have a hardware that streams data to a windows server, and I have IP and port of this server. Now I want to connect unity app as a TCP client to this server. I have tried many solutions, but still have hard time to figure out best way to do this. Any help is highly appreciated. One issue that I have with current tutorials is that most of them define a unity server and try to connect another unity app as client and mostly based on localhost. However, I just want to connect a unity app as a TCP client to a server which I have IP and port.
Warm regards,
enter image description here
First of all, at the top of your script:
using System;
using System.Net.Sockets;
Then, create an instance of TcpClient and begin the connection:
socket = new TcpClient
{
// dataBufferSize is in bytes
ReceiveBufferSize = dataBufferSize,
SendBufferSize = dataBufferSize
};
socket.BeginConnect(ip, port, ConnectCallback, socket);
In ConnectCallback():
private void ConnectCallback(IAsyncResult asyncResult)
{
socket.EndConnect(asyncResult);
if (!socket.Connected) return;
// stream is a NetworkStream you should declare somewhere
stream = socket.GetStream();
// receiveBuffer is a byte[] you should declare somewhere
receiveBuffer = new byte[dataBufferSize];
stream.BeginRead(receiveBuffer, 0, dataBufferSize, ReceiveCallback, null);
}
In ReceiveCallback():
private void ReceiveCallback(IAsyncResult asyncResult)
{
try
{
int byteLength = stream.EndRead(asyncResult);
if (byteLength <= 0)
{
// Disconnect client
return;
}
// Transfer data from receiveBuffer to data variable for handling
byte[] data = new byte[byteLength];
Array.Copy(receiveBuffer, data, byteLength);
// Handle data in any way you want to
// BeginRead again so you can keep receiving data
stream.BeginRead(receiveBuffer, 0, dataBufferSize, ReceiveCallback, null);
}
catch (Exception e)
{
Console.WriteLine($"Error receiving TCP data: {e.Message}");
}
}
I got this information from this tutorial.

Xamarin Forms How to change Port or IPAddress of socket connection

I have a UWP (soon to be MacOS also) application that listens for incoming messages. The user can configure which IP Address and Port to listen on. Once the socket connection is listening, the user can also go back into the settings and change the IP Address or Port. I am trying to figure out how to shut down the existing listener and restart it using the new Port / IP Address when the user changes the values. Here is my code that starts the listener. Any help would be appreciated.
private static Socket iobj_listener;
public async static Task StartListening()
{
try
{
Debug.WriteLine("Point 1");
IPEndPoint localEndPoint = new IPEndPoint(ViewModelObjects.AppSettings.ServerIPAddress, ViewModelObjects.AppSettings.ServerPort);
// Create a TCP/IP socket.
iobj_listener = new Socket(ViewModelObjects.AppSettings.ServerIPAddress.AddressFamily,
SocketType.Stream, ProtocolType.Tcp);
// Bind the socket to the local endpoint and listen for incoming connections.
iobj_listener.Bind(localEndPoint);
iobj_listener.Listen(100);
ViewModelObjects.AppSettings.ListeningOnSocket = true;
while (true)
{
Debug.WriteLine("Point 2");
// Set the event to nonsignaled state.
allDone.Reset();
// Start an asynchronous socket to listen for connections.
Debug.WriteLine("Waiting for a connection on " + ViewModelObjects.AppSettings.ServerIPAddress.ToString() + "...");
iobj_listener.BeginAccept(
new AsyncCallback(AcceptCallback),
iobj_listener);
// Wait until a connection is made before continuing.
allDone.WaitOne();
}
}
catch (Exception e)
{
Debug.WriteLine(e.ToString());
}
finally
{
Debug.WriteLine("Point 3");
ViewModelObjects.AppSettings.ListeningOnSocket = false;
}
}
SO I could not find any quick answers so had to kind of figure this out on my own. If you see anything wrong with this, please let me know.
First of all I declared an e_Num as follows
public enum ge_SocketStatus
{
e_NotListening = 0,
e_Listening = 1,
e_Restart = 2
}
Then I added a StopListening function to my class that handles all my Socket communications and set the socket status to not listening as follows:
public static async Task StopListening()
{
try
{
if (iobj_listener.Connected)
{
//Wait till the connection ends or 30 seconds - this is so any last messages can be processed.
await Task.Delay(30000);
}
ViewModelObjects.AppSettings.SocketStatus = ge_SocketStatus.e_NotListening;
iobj_listener.Close(1);
}
catch (Exception ex)
{
App.AppException(ex);
}
}
I then use the value of this enum to know when to end the loop:
public async static Task StartListening()
{
try
{
Debug.WriteLine("Point 1");
IPEndPoint localEndPoint = new IPEndPoint(ViewModelObjects.AppSettings.ServerIPAddress, ViewModelObjects.AppSettings.ServerPort);
// Create a TCP/IP socket.
iobj_listener = new Socket(ViewModelObjects.AppSettings.ServerIPAddress.AddressFamily,
SocketType.Stream, ProtocolType.Tcp);
// Bind the socket to the local endpoint and listen for incoming connections.
iobj_listener.Bind(localEndPoint);
iobj_listener.Listen(100);
ViewModelObjects.AppSettings.SocketStatus = ge_SocketStatus.e_Listening;
while (ViewModelObjects.AppSettings.SocketStatus == ge_SocketStatus.e_Listening)
{
Debug.WriteLine("Point 2");
// Set the event to nonsignaled state.
allDone.Reset();
// Start an asynchronous socket to listen for connections.
Debug.WriteLine("Waiting for a connection on " + ViewModelObjects.AppSettings.ServerIPAddress.ToString() + "...");
iobj_listener.BeginAccept(
new AsyncCallback(AcceptCallback),
iobj_listener);
// Wait until a connection is made before continuing.
allDone.WaitOne();
}
}
catch (Exception e)
{
Debug.WriteLine(e.ToString());
}
finally
{
Debug.WriteLine("Point 3");
}
}
This line above
while (ViewModelObjects.AppSettings.SocketStatus == ge_SocketStatus.e_Listening)
used to be
while (true)
so the loop would never end.
One gotcha I found is in the AcceptCallback used in the BeginAccept function of my socket. In this code, I also had to detect if the socket was connected because this function is called one last time after the StartListening loop exits. At the point the socket is not connected so trying to do anything with is, such as EndAccept, causes the application to throw an exception. Below you can see where I added the line
if (listener.Connected)
in order to stop the code from crashing after I had closed the connection.
public static void AcceptCallback(IAsyncResult ar)
{
// Signal the main thread to continue.
allDone.Set();
// Get the socket that handles the client request.
Socket listener = (Socket)ar.AsyncState;
//If we have shut down the socket don't do this.
if (listener.Connected)
{
Socket handler = listener.EndAccept(ar);
// Create the state object.
StateObject state = new StateObject();
state.workSocket = handler;
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
}
Once all StopListening function ends and everything from the sockets is disconnected, I can call start listening again and open the socket on a different IPAddress and or Port.
I hope this helps as I could not find a good solution to this.

Connection not working from Android to UWP on desktop - ZeroMq (NetMq)

I tried some examples of ZMQ on C++,C# and Python. I am trying to have Request-Reply pattern to connect Android device to PC running UWP with Xamarin forms.
Below is the Requestor code:
public void HelloWorld()
{
var timer = new Timer(60000);
timer.Start();
timer.Elapsed += (sender, args) =>
{
this.Cancel = true;
timer.Stop();
};
// Create
const string endpoint = "tcp://PC_ip:3245";
using (var request = new RequestSocket())
{
request.Bind(endpoint);
Thread.Sleep(2000);
while (!Cancel)
{
request.SendFrame("Requester says hello");
var reply = request.ReceiveFrameString();
Debug.WriteLine("Gets reply {0}",reply);
}
}
}
Reply socket code:
public void HelloWorld()
{
var timer = new Timer(60000);
const string endpoint = "tcp://PC_ip:3245";
timer.Start();
timer.Elapsed += (sender, args) =>
{
timer.Stop();
Cancel = true;
};
using (var replierSocket = new ResponseSocket())
{
replierSocket.Connect(endpoint);
Thread.Sleep(2000);
while (!Cancel)
{
var replyFromRequester = replierSocket.ReceiveFrameString();
Debug.WriteLine("Got reply {0}", replyFromRequester);
replierSocket.SendFrame("Response socket say hello");
}
}
}
Cancel is boolean
I went through some questions posted on this and added delay and these connection code blocks only trigger after button clicks on app.
While debugging , request.ReceiveFrameString() replierSocket.ReceiveFrameString(); are not even hit.
I am new to network programming , I understand that for REQ/REP pattern the code has to be in particular order which I traced and fixed I believe and turned off firewall on my PC so that firewall wont block my incoming connections from Android device.
PC_ip stands for IPv4 address I got from ipconfig /all for my wifi. I also tried external ip of my machine from sites like whatsmyip.org at ResponseSocket but I still dont get response between devices.
Please let me know what am I doing wrong.
Issue replication repository : GitHub/me/XamZeroMq

Matlab can not read the socket for the second time

I want to communicate between two programs. MATLAB is able to read the socket once but when it tries to read it again, I get the following warning
Warning: Unsuccessful read: A timeout occurred before the Terminator was reached.
Matlab code:
clc
clear all
while(1)
clear tcpipServer
tcpipServer = tcpip('127.0.0.1', 55000, 'NetworkRole', 'Server');
set(tcpipServer, 'Timeout', 30);
fopen(tcpipServer);
rawData = fgetl(tcpipServer);
fclose(tcpipServer);
end
The data which is sent to Matlab is defined as string and the value is "y\n".
The problem is that I only receive "y\n" once.
The other side connected to the socket is the Unity game engine. I think the code in the unity is correct because I tested that by c# windows app and I can get the value as long as program is running.
Does anyone know why this is happening?
This is the solution i found.
Matlab code:
clc
clear all
%% server initialization
tcpipServer = tcpip('127.0.0.1',55000,'NetworkRole','Server');
set(tcpipServer,'Timeout',30);
while(1)
%% server code: listening for incoming requests
fopen(tcpipServer);
%rawData = fread(tcpipServer,1,'char');
rawData = fgetl(tcpipServer)
extractedData=str2double(strsplit(rawData,'-'))
fclose(tcpipServer);
Unity C# code:
using UnityEngine;
using System.Collections;
using System.Net;
using System.Net.Sockets;
using System.Linq;
using System;
using System.IO;
using System.Text;
public class server : MonoBehaviour {
// Use this for initialization
TcpListener listener;
String msg;
StreamWriter theWriter1;
internal Boolean socketReady = false;
TcpClient mySocket;
NetworkStream theStream;
StreamWriter theWriter;
StreamReader theReader;
String Host = "localhost";
Int32 Port = 55000;
bool firstTimeRun=true;
void Start () {
}
// Update is called once per frame
void Update () {
setupSocket ();
if (Input.GetMouseButtonDown (0))
{
setupSocket ();
}
}
public void setupSocket() {
try {
TcpClient mySocket1;
NetworkStream theStream1;
//StreamWriter theWriter1;
mySocket1 = new TcpClient(Host, Port);
theStream1 = mySocket1.GetStream();
theWriter1 = new StreamWriter(theStream1);
Invoke("write", 0.2f);
}
catch (Exception e) {
Debug.Log("Socket error: " + e);
}
}
void write()
{
float x;float y;
x = GameObject.FindGameObjectWithTag ("Player").transform.position.x;
y=GameObject.FindGameObjectWithTag ("Player").transform.position.y;
theWriter1.Write(x+"-"+y+"\n");
theWriter1.Flush();
}
The reason I used "\n" is using fgetl function in Matlab code. if you want to make the code more functional use delay along with Asynchronous tasks in Unity. for those wandering why using delay is required, see the comments under question.

TCPClient will only ever connect once

I am building an app that communicates to a machine via TCP. The issue I have is that I can connect and all is well but once I disconnect and then try and reconnect all I get is error
The code I have in the form is
private void btn_connect_Click(object sender, EventArgs e)
{
try
{
Log(LogMsgType.Normal, String.Format("Connected on {0}\n", DateTime.Now));
string ip = (txt_ipadd_1.Text + "." + txt_ipadd_2.Text + "." + txt_ipadd_3.Text + "." + txt_ipadd_4.Text);
Log(LogMsgType.Normal, String.Format("Connecting to IP address " + ip + " Port # " + txt_port.Text +"\n"));
string con = Codenet.Connect_TCP(ip, Convert.ToInt32(txt_port.Text)); //connect to this one
Log(LogMsgType.Normal, String.Format(con + "\n"));
toolStripStatusLabel1.Text = "Connected";
btn_connect.Visible = false;
btn_disconnect.Visible = true;
ip = "";
}
catch (Exception d)
{
Log(LogMsgType.Error, String.Format("Error..... " + d.StackTrace +"\n"));
}
}
Which goes over to code from the Codenet dll which does the connection and disconnection
public static string Connect_TCP(string ip, int Port)
{
tcpclnt.Connect(ip, Port);
connection_type = "TCP";
return "Connected OK ";
}
public static void DisConnect_TCP()
{
tcpclnt.GetStream().Close();
tcpclnt.Close();
}
The error I get is:
Error..... at System.Net.Sockets.TcpClient.Connect(String hostname, Int32 port) at
comms_engine.Codenet.Connect_TCP(String ip, Int32 Port) in C:\a\code\g_com_test_1\comms_engine\Codenet.cs:line 37 at
comms_test.CommTester.btn_connect_Click(Object sender, EventArgs e) in C:\a\code\g_com_test_1\dll_test\ethernet.cs:line 98
It will not perform the tcpclnt.Connect(ip, Port) even if the ip is different.
It seems that something is still open but what I do not know as the machine has disconnected. I know that as I have UDP in it sending out how many connections it has and I can see then increase and decrease OK.
I tried "public string Connect_TCP(string ip, int Port)" and making a NEW instance of the dll befor calling the connect from the form but that did not work either.
Any ideas what I am doing wrong.
Thanks for answers so far but still I have an issue. I read the MSDN and made a test app and I can now see what is wrong but am clueless on how to fix it at the moment. I am declaring the Client as Static global so I can get it from the connect and disconnect button. I think thta this always creates new instances and I never close them. First time round the machine tells me it has diconnected bu t the app never reconnects In debug mode it falls over at client.Connect(ipEndPoint); with "InvalidOperationException was unhandled" followed by "Once the socket has been disconnected, you can only reconnect again asynchronously, and only to a different EndPoint. BeginConnect must be called on a thread that won't exit until the operation has been completed."
I guess I need to put this Client inside the btn_connect_click and get a pointer to it from inside the disconnect but how?
The full code is
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Net;
using System.IO;
using System.IO.Ports;
using System.Diagnostics;
using System.Threading;
namespace TCP_Test
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
static Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
private void btn_connect_Click(object sender, EventArgs e)
{
string ip = (txt_ipadd_1.Text + "." + txt_ipadd_2.Text + "." + txt_ipadd_3.Text + "." + txt_ipadd_4.Text);
IPHostEntry ipHost = Dns.GetHostEntry(Dns.GetHostName());
IPAddress ipAddr = IPAddress.Parse(ip);
IPEndPoint ipEndPoint = new IPEndPoint(ipAddr, Convert.ToInt32(txt_port.Text));
// Connect the socket to the remote end point.
client.Connect(ipEndPoint);
rchTxtBx_output.AppendText("connected to " + ipEndPoint + "\n\r");
}
private void btn_disconnect_Click(object sender, EventArgs e)
{
client.Shutdown(SocketShutdown.Both);
client.Disconnect(true);
if (client.Connected)
rchTxtBx_output.AppendText("We're still connnected \n\r");
else
rchTxtBx_output.AppendText("We're disconnected \n\r");
}
}
}
Once you disconnect a TCP socket, it cannot be reconnected. You have to release the socket and create a new one. This is not just limited to the .NET TCPClient class, this is how all socket APIs work in general (WinSock, BSD, etc).
The exception to this rule is the WinSock2-specific DisconnectEx() function, which has a TF_REUSE_SOCKET flag that allows ConnectEx() (and AcceptEx()) to re-use an existing SOCKET instead of requiring a new socket to be created. But that does not apply in your situation.
In the majority of cases, once you disconnect a socket, you have to create a new socket from scratch.
Thanks for the help I have now solved it. See above for full code but below is teh snippet that makes it work. Just add client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); inside the connect button. My testing now shows it connects and disconnects and allows reconnect or connect to anothe machine. Only took me two man days to work that out with your help.
static Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
private void btn_connect_Click(object sender, EventArgs e)
{
if (client.Connected)
{
client.Shutdown(SocketShutdown.Both);
client.Disconnect(true);
client.Close();
}
else
{
client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
string ip = (txt_ipadd_1.Text + "." + txt_ipadd_2.Text + "." + txt_ipadd_3.Text + "." + txt_ipadd_4.Text);
IPHostEntry ipHost = Dns.GetHostEntry(Dns.GetHostName());
When the connection is closed, underlying socket(TCPClient.Client returns socket) needs to be set for reuse . Use the Socket.Disconnect(true) instead of close to reconnect on the socket. Check the link:
http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.disconnect(v=vs.90).aspx