I need to create an application that allows the user to connect to a device (a piece of hardware that will be sent arguments/commands) via wifi (wifi is end goal but I am settling at the moment for any connection) and then send commands to said device. I know some C# and some sockets/IP stuff, but not sockets/IP using C#. The visual, GUI side of the program is not what I am struggling with. I cannot seem to get the socket up and running and make any real connection. I keep getting the "An Invalid Argument was Supplied" exception.
Any tips on this particular issue or help with C# networking/sockets/etc. are welcome.
I declare a new socket by:
Socket sck;
sck = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
The exception is thrown when it tries to process the "sck = new Socket(...);"
Code in Question:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Text.RegularExpressions;
namespace InternetConnect
{
public partial class Form1 : Form
{
// Global Variables
int portNumber = 0;
string ipAddress = "";
TcpClient client;
Socket sck;
EndPoint epLocal, epRemote;
// Needs to be defined at some point
// These are just holding a place for now
string extraIP = "127.0.0.1";
string extraPort = "135";
public Form1()
{
InitializeComponent();
try
{
sck = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
sck.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
}
catch (SocketException ex)
{
MessageBox.Show(ex.Message);
}
}
private void connectButton_Click(object sender, EventArgs e)
{
try
{
epLocal = new IPEndPoint(IPAddress.Parse(ipAddressTextBox.Text), Convert.ToInt32(portNumberTextBox.Text));
MessageBox.Show("Before Bind");
sck.Bind(epLocal);
MessageBox.Show("After Bind");
epRemote = new IPEndPoint(IPAddress.Parse(extraIP), Convert.ToInt32(extraPort));
sck.Connect(epRemote);
MessageBox.Show("After Connect");
byte[] buffer = new byte[1500];
sck.BeginReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, ref epRemote, new AsyncCallback(MessageCallBack), buffer);
connectButton.Text = "Connected";
connectButton.Enabled = false;
sendButton.Enabled = true;
outgoingTextBox.Focus();
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
ipAddress = ipAddressTextBox.Text;
// To make sure there are no letters in the IP Address
int errorCounter = Regex.Matches(ipAddress, #"[a-zA-Z]").Count;
if (errorCounter == 0)
{
if (ipAddress != "")
{
// To make sure the port number is entered without letters
if (int.TryParse(portNumberTextBox.Text, out portNumber))
{
WriteToStatusBar("IP Address and Port Number Valid");
}
else
{
MessageBox.Show("Please enter a valid Port Number.");
}
}
else
{
MessageBox.Show("Please enter a valid IP Address.");
}
}
else
{
MessageBox.Show("Please enter a valid IP Address.");
}
#endregion
try
{
client = new TcpClient();
WriteToStatusBar("Connecting...");
client.Connect(ipAddress, portNumber);
WriteToStatusBar("Connected");
outgoingTextBox.Text = "Enter message to be sent to the device...";
client.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
As per request of the OP some information about using TcpClient and even TcpListener in case you need to create a server as well. Following link will help you get started with using TcpClient :https://msdn.microsoft.com/en-us/library/system.net.sockets.tcpclient%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396
or this one in code project covers both client and server: http://www.codeproject.com/Articles/1415/Introduction-to-TCP-client-server-in-C
in that code (the first link) you'll find following statement : stream.Write(data, 0, data.Length);
You want to write multiple times to the socket then suppose you write the same data twice you can do simply :
stream.Write(data, 0, data.Length);
stream.Write(data, 0, data.Length);
At the receiver, you have to realise that because TCP is streaming, you might receive to 2 send 'messages' in one receive or scattered over multiple receives.
Further in the code you will find :
stream.Close();
client.Close();
Which ends the communication and closes the socket, sending after doing the close is not possible anymore.
Using sockets, but also tcpclient and tcplistener (because they are based on sockets), is something that I consider a bit advanced material. Without really understanding what streaming is, what IP addressing is, what TCP, UDP is, how to use sockets , some basic understanding of threads. It is easy to get lost. I am a professional programmer, and I did not touch this material after I read quite a lot of information about it.
The best thing to do is to do more investigation, based on examples on the internet & books. And ask very specific dedicated questions using this medium. Elaborate questions have a tendency to get closed without proper answer which will lead to frustration of course.
Related
I can send UDP messages from one computer to itself on a certain port but I can't send this messages from one computer to another in the same lan. I am pretty new to C# and I am also new to network programming so the solution is probably easy...
I have tried an example from Microsoft Docs and did only small changes. (names, etc.)
Both codes run as seperate programs.
//RECEIVE
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
public class UDPListener
{
private const int listenPort = 11000;
private static void Listen()
{
UdpClient listener = new UdpClient(listenPort);
IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, listenPort);
try
{
while (true)
{
Console.WriteLine("Waiting for broadcast");
byte[] receivebytes = listener.Receive(ref groupEP);
Console.WriteLine(Encoding.ASCII.GetString(receivebytes, 0, receivebytes.Length));
}
}
catch (SocketException e)
{
Console.WriteLine(e);
}
finally
{
listener.Close();
}
}
public static void Main()
{
Listen();
}
}
//SEND
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
class Program
{
static void Main(string[] args)
{
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
//192.168.2.5 is the IP Address of the receiving computer
IPAddress broadcast = IPAddress.Parse("192.168.2.5");
byte[] bytestosend = Encoding.ASCII.GetBytes("Hi");
IPEndPoint ep = new IPEndPoint(broadcast, 11000);
socket.SendTo(bytestosend, ep);
Console.WriteLine("bytestosend were send");
}
}
The sending program tells me that the message was sent but the receiving program gives no output.
EDIT 1: The Ressource Manager said that on the receiving computer was no network activity while on the sending computer is actually sending something.
EDIT 2: I tried my applications in an other network and it worked fine. I did some research and I found that sometimes routers don't allow udp broadcasts through a subnet (or anything similar...). So I assume it might be my router. (unfortunately I can not access it's settings.) So I think the problem has resolved itself!
I'm writing an application to send syslogs to a central server. The network connection to the server is expected to be unreliable. For this reason, I need to be able to check whether the messages have been received by the server, so that if the send is unsuccessful I can try sending them again later.
I have no control over the server, only the ability to form a TCP connection with it - most questions I've seen recommend writing custom ACK logic, but that's not an option for me. I need an entirely client-side approach. Is there any way I can access the TCP acknowledgements in order to mark a message as sent?
The TCP protocol guarantees that the data is properly delivered at the remote server. To quote from the original TCP specification in RFC 793:
An acknowledgment by TCP does not guarantee that the data has been
delivered to the end user, but only that the receiving TCP has taken
the responsibility to do so.
So if you do not get an error from your sending part, you have a confirm that the remove server TCP stack has properly received your data. TCP has built-in detection for damaged, lost, duplicated, or out of order delivered packets (using sequence numbers) and will retransmit packets when it detects problems.
As the other poster has mentioned, TCP is a reliable protocol. You can wrap this in some exception handling code to ensure that messages are re-sent when the connection is restored, for example:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
namespace TCPClientTest
{
class SyslogMessage
{
public Guid Guid = Guid.NewGuid();
public byte[] MessageData;
}
class Program
{
static readonly int TCP_PORT = 1337;
static byte[] SendTCPMessage(String hostname, int port, byte[] data)
{
using (var client = new TcpClient(hostname, port))
{
using (var stream = client.GetStream())
{
stream.Write(data, 0, data.Length);
var responseData = new byte[1024];
var byteCount = stream.Read(responseData, 0, responseData.Length);
return responseData.Take(byteCount).ToArray();
}
}
}
static void SendSyslog(String hostname, int port, SyslogMessage m)
{
SendTCPMessage(hostname, port, m.MessageData);
}
static Queue<SyslogMessage> sysLogQueue = new Queue<SyslogMessage>(new List<SyslogMessage>()
{
new SyslogMessage() { MessageData = Encoding.UTF8.GetBytes("Test data 1")},
new SyslogMessage() { MessageData = Encoding.UTF8.GetBytes("Test data 2")}
});
static void Main(string[] args)
{
System.Threading.Timer timer = new System.Threading.Timer(SendLogs, null, 1000, 5000);
Console.WriteLine("Press return to continue...");
Console.Read();
}
static void SendLogs(object state)
{
while (sysLogQueue.Count > 0)
{
try
{
var m = sysLogQueue.Peek();
SendSyslog("localhost", TCP_PORT, m);
Console.WriteLine("Sent sys log: " + Encoding.UTF8.GetString(m.MessageData));
// Remove from queue.
sysLogQueue.Dequeue();
}
catch (SocketException e)
{
// Leave in the queue.
Console.WriteLine("Failed to send log: Socket exception occurred: {0}", e);
// Break until next attempt.
break;
}
}
}
}
}
I've used port 1337 for this, just set this to whatever you need.
I have a client-server application that uses a UDP socket to send the data , the data only have to travel from client to server , and the server will always have the same IP. The only requirement is that I have to send messages about 10 messages per second
Currently I am doing it the following way :
public void SendData(byte[] packet)
{
IPEndPoint end_point = new IPEndPoint(serverIP, serverPort);
UdpClient udpChannel = new UdpClient(sourcePort);
udpChannel.Connect(end_point);
udpChannel.Send(packet, packet.Length);
udpChannel.Close();
}
The problem I have is that when I use the command "udpChannel.Close()" it takes 2-3 seconds to be performed when the server is not listening. (I've seen the same problem in: What is the drawback if I do not invoke the UdpClient.Close() method?)
My question would be, if I always send packets to the same IP address and port, is it necessary to connect the socket and close it after each send request?
The code I intend to use would be as follows:
UdpClient udpChannel;
public void SendData(byte[] packet)
{
udpChannel.Send(packet, packet.Length);
}
public void Initialize(IPAddress IP, int port)
{
IPEndPoint end_point = new IPEndPoint(serverIP, serverPort);
UdpClient udpChannel = new UdpClient(sourcePort);
udpChannel.Connect(end_point);
}
public void Exit()
{
udpChannel.Close();
}
Doing it this way, would it be necessary to do some checking in the "SendData" method before sending the data?
Is there any problem in the above code?
Thank you!
UDP is connectionless, calling udpChannel.Connect merely specifies a default host endpoint for use with the Send method. You do not need to close the client between sends, leaving it open will not leave any connections or listeners running between sends.
You shouldn't connect/close after each send request. When you start working - you connect to socket. And you can send data. You should close UdpClient when you do not want to send/recieve data, for example when you closing Form.
In your case you can check that udpClient != null when close/send client and you can use try/catch, for example:
try
{
udpClient.Send(sendBytes, sendBytes.Length);
}
catch (Exception exc)
{
// handle the error
}
Use try/catch when you connecting, because port may be busy or other problem with connection.
And look at UdpClient.SendAsync :)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using System.Text;
using System.Net.Sockets;
using System;
using System.Net;
public class Server : MonoBehaviour
{
//int[] ports;
UdpClient udp; // Udp client
private void Start()
{
udp = new UdpClient(1234);
udp.BeginReceive(Receive, null);
}
void Send(string msg, IPEndPoint ipe)
{
UdpClient sC = new UdpClient(0);
byte[] m = Encoding.Unicode.GetBytes(msg);
sC.Send(m, msg.Length * sizeof(char), ipe);
Debug.Log("Sending: " + msg);
sC.Close();
}
void Receive(IAsyncResult ar)
{
IPEndPoint ipe = new IPEndPoint(IPAddress.Any, 0);
byte[] data = udp.EndReceive(ar, ref ipe);
string msg = Encoding.Unicode.GetString(data);
Debug.Log("Receiving: " + msg);
udp.BeginReceive(Receive, null);
}
}
At the Send() I use new UDP CLient and close it after every time. Its better, u can send and receive at the same time.
I have the following code in C#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
namespace Networking
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("---Connecting to a Host using a Specific Port---");
Console.WriteLine();
Console.WriteLine("Attempting Connection...");
Console.WriteLine();
string hostname = "www.yahoo.com";
int port_no = 21; //HTTP = 80, HTTPS = 443, FTP = 21
IPAddress ipa = (IPAddress)Dns.GetHostAddresses(hostname)[0];
try
{
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Connect(ipa, port_no);
if (socket.Connected == true)
{
Console.WriteLine("The connection was successful!");
}
}
catch (System.Net.Sockets.SocketException ex)
{
if (ex.ErrorCode == 10061)
{
Console.WriteLine("The connection was NOT successful!");
}
else
{
Console.WriteLine(ex.Message);
}
}
Console.WriteLine();
Console.WriteLine("Press enter to exit");
Console.ReadLine();
}
}
}
As you can see, the program attempts to connect to a particular website using a specific port number.
How can the program be modified so that I can know if data is being sent over a particular port after connection? Maybe count the number of bytes sent or the type of file? Thank you :)
There are several possibilities. First you can call Accept on the socket and make your program block until data is available. As soon as data is available you can Receive data into a byte array and work on this.
Secondly you can call BeginAccept and asynchronously wait for data to arrive and handle it accordingly.
See the documentation on sockets in C#.
Am starting with socket programming with a simple UDPClient program to send some data. The large code snippet is below:
using System;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
class ShowIP
{
public static void Main(string[] args)
{
string name = Dns.GetHostName();
//name = "GSL1460";
name = "GSL1296";
try
{
IPAddress[] addrs = Dns.GetHostEntry(name).AddressList;
foreach (IPAddress addr in addrs)
Console.WriteLine("{0}/{1}", name, addr);
Console.WriteLine("Started listening");
Thread listenerThread = new Thread(new ThreadStart(StartListeningUDP));
listenerThread.Start();
Console.WriteLine("Started sending");
for (int counter = 0; counter <= 3; counter++)
{
Thread.Sleep(1000);
Console.WriteLine("Sending {0} time", counter.ToString());
StartSendingUDP(addrs[0]);
}
Console.ReadLine();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
private static void StartListeningUDP()
{
UdpClient udpListener = null;
IPEndPoint nwPoint = new IPEndPoint(IPAddress.Any, 12345);
while (true)
{
try
{
udpListener = new UdpClient(12345);
Console.WriteLine("Waiting to receive");
Byte[] receivedBytes = udpListener.Receive(ref nwPoint);
string receivedData = Encoding.ASCII.GetString(receivedBytes);
Console.WriteLine("Data received : " + receivedData);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
finally
{
udpListener.Close();
}
}
}
private static void StartSendingUDP(IPAddress clientAddress)
{
UdpClient udpSender = new UdpClient();
try
{
Byte[] sendBytes = Encoding.ASCII.GetBytes("Say HI to Papa...");
Console.WriteLine("Data Sent : Say HI to Papa...");
udpSender.Send(sendBytes, sendBytes.Length, new IPEndPoint(clientAddress, 12345));
}
finally
{
udpSender.Close();
}
}
}
The sample works fine on local machine, but am not able to send data to another machine on the intranet.
During testing
Am uncommenting the appropriate code to send data to his machine
Am running the Receiver bit on his machine
Have checked that the required port is open on his machine
Am I missing something? Please suggest.
udpSender.Flush?
I'm not a C# person, so I can't comment too much on your code, but it looks basically okay. Make sure that the IP address you're sending to is being resolved correctly to your receiving machine.
Also, see if Windows has firewalled your internet connection, and try disabling the firewall if so. And, I know that Microsoft has some ideas about "safe" code that have caused us some problems in the past. I don't have any specifics, but there might be settings in the project that keep it from being able to access the network.
The UDP-Listener might be listening on localhost only. You could try to replace
udpListener = new UdpClient(12345)
in StartListeningUDP() with
udpListener = new UdpClient(new IPEndPoint(IPAddress.Any,12345))
you can't really send UDP over the internet without doing few things before.
you will get too many udp filters on the way.
even if you will disable your firewall, your router/provider modem can be set to block it.
else - your provider servers will block it.
so in fact you will have to make sure that this port is open for UDP, just as on your localhost it won't work unless you will open this port in the firewall and/or install the loopback adapter