I have created a class that acts as a plugin for another application. It should hold functions to use in the main application. It works in general - that means i can handle usual functions like calculations and even reading files. But i have problems implementing a socket class. I know how to work with sockets in general but in this case i have a problem.
As you may see in the code, there is an internal class SockAttrib
that should manage the socket creation, the listening and also the messages. Received messages are stored in a dictionary.
public class Net : Module {
private static ReadOnlyCollection<CustomMethod> customMethods;
internal class SockAttrib {
public Socket listener;
public Socket handler;
/* create the listener */
public SockAttrib(int port) {
IPHostEntry host = Dns.GetHostEntry("localhost");
IPAddress ipAddress = host.AddressList[1];
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, port);
try {
listener = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
listener.Bind(localEndPoint);
listener.Listen(10);
handler = listener.Accept();
} catch (Exception e) { Console.WriteLine("socket() " + e); }
}
/* listen */
public SockAttrib() {
try {
// Incoming data from the client.
string data = "";
byte[] bytes = null;
while (true) {
bytes = new byte[1024];
int bytesRec = handler.Receive(bytes);
data += Encoding.ASCII.GetString(bytes, 0, bytesRec);
if (data.IndexOf("<EOF>") > -1)
{
messages[++idm] = data;
//return;
}
}
}
catch (Exception e) { Console.WriteLine("listen() "+e); }
}
public void Close() {
handler.Close();
}
}
/* message index */
private static int idm = 0;
/* list of messages */
private static Dictionary<int, String> messages = new Dictionary<int, String>();
public Net() {
if (customMethods != null) return;
List<CustomMethod> moduleMethods = new List<CustomMethod>();
moduleMethods.Add(new CustomMethod(typeof(int), "socket", typeof(int)));
moduleMethods.Add(new CustomMethod(typeof(int), "listen" ));
moduleMethods.Add(new CustomMethod(typeof(string), "sockread"));
customMethods = moduleMethods.AsReadOnly();
}
public ReadOnlyCollection<CustomMethod> Prototypes {
get { return customMethods; }
}
public object OnMethodInvoke(String functionName, List<object> parameters) {
if( functionName == "socket") {
int port = (int)parameters[0];
SockAttrib sa = new SockAttrib( port );
return 1;
}
if (functionName == "listen") {
SockAttrib sa = new SockAttrib();
return 1;
}
if (functionName == "sockread") {
if (idm > 0) {
String message = messages[--idm];
return message;
} else {
return "nope";
}
}
return false;
}
}
My problem is the handler. The creation of the socket works but as soon as i connect to the socket using netcat the socket stop listening and i dont get any responses. I hope its not too much code and it should also be easy readable.
Finally the module gets exported as a library (dll) so i cant really give a minimal working example without also posting the module handler.
Though your requirements are still a bit fuzzy, I will give it a try.
First I would recommend to create a class containing the core functionality of your TCP server. This makes it easier to unit-test your code and to adapt it to changing requirements.
/// <summary>
/// This class encapsulates the TCP server
/// </summary>
public class Server : IDisposable
{
private static TcpListener _listener;
private static TcpClient _client;
private static NetworkStream _stream;
private static byte[] _buffer;
private static readonly StringBuilder _receivedText = new StringBuilder();
private const string EOF = "<EOF>";
/// <summary>
/// Starts listening on the specified port
/// </summary>
/// <param name="port">The port number</param>
public Server(int port)
{
_listener = new TcpListener(IPAddress.Any, port);
_listener.Start();
_listener.BeginAcceptTcpClient(Accepted, null);
}
public void Dispose()
{
if (_client != null)
{
_client.Dispose();
}
if (_listener != null)
{
_listener.Stop();
}
}
/// <summary>
/// Returns any text that has been sent via TCP to the port specified in the constructor.
/// </summary>
/// <returns>The received text, or null if no (complete) text has been received yet.</returns>
/// <remarks>
/// The method returns the next text delimited by "<EOF>".
/// </remarks>
public string Read()
{
lock (_receivedText)
{
var receivedText = _receivedText.ToString();
var eofIndex = receivedText.IndexOf(EOF);
if (eofIndex < 0)
return null; // no (complete) text has been received yet
var result = receivedText.Substring(0, eofIndex);
_receivedText.Remove(0, eofIndex + EOF.Length);
return result;
}
}
// called whenever a client has connected to our server.
private static void Accepted(IAsyncResult ar)
{
_client = _listener.EndAcceptTcpClient(ar);
_stream = _client.GetStream();
_buffer = new byte[4096];
_stream.BeginRead(_buffer, 0, _buffer.Length, Read, null);
}
// called whenever data has arrived or if the client closed the TCP connection
private static void Read(IAsyncResult ar)
{
var bytesReceived = _stream.EndRead(ar);
if (bytesReceived == 0)
{
// TCP connection closed
_client.Close();
_client = null;
_stream.Dispose();
_stream = null;
// prepare for accepting the next TCP connection
_listener.BeginAcceptTcpClient(Accepted, null);
return;
}
lock (_receivedText)
{
_receivedText.Append(Encoding.ASCII.GetString(_buffer, 0, bytesReceived));
}
// prepare for reading more
_stream.BeginRead(_buffer, 0, _buffer.Length, Read, null);
}
}
Integrating this into your Net
class should then be fairly simple:
// static or not? Depends on your "Module plugin" architecture
private static Server _server;
public object OnMethodInvoke(String functionName, List<object> parameters)
{
if (functionName == "socket")
{
if (_server != null)
{
// oops, already open
return 0;
}
int port = (int)parameters[0];
_server = new Server(port);
return 1;
}
if (functionName == "sockread")
{
if (_server != null)
{
return _server.Read() ?? "nope";
}
else
{
return "nope";
}
}
return false;
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.