I've recently been helping a friend with his client/server app - he's got a static-style class and we're using a Monitor in the static ctor to wait for the connection to finish connecting. We have a major deadlock problem happening (like nothing I've ever seen), and I think it has to do with the static nature of his stuff.
I implemented the same exact types of things in a non-static-style class and it worked fine.
From what I see of your code, you're doing it the exact same way that I wrote my networking stuff (unrelated to the above friend's code). However, my netcode is not using any static stuff. What I usually have problems with is succefully getting notified when the other end closes its connection.
I will post both of my ENTIRE classes for review to see if anything is odd. Expect a ton of typos in the commenting - I did all that at about 4 AM with only two Mt. Dews to ward off the sandman.
Note that these are likely broken some way or another, and that I'm abandoning them in favor of DirectPlay (which rules, btw).
using System;using System.IO;using System.Net;using System.Windows.Forms;using System.Threading;namespace System.Net.Sockets{ /// <summary> /// Delegate type for Events dealing with Socket objects. /// </summary> public delegate void SocketHandler(object sender, Socket socket); /// <summary> /// Delegate type for Events dealing with packet data (MemoryStreams) /// </summary> public delegate bool PacketHandler(object sender, MemoryStream stream); /// <summary> /// Delegate type for Events dealing with NetClient objects. /// </summary> public delegate void NetClientHandler(object sender, NetClient client); /// <summary> /// Allows reading/writing to a remote instance of NetClient using friendly, asynchronous callbacks. /// </summary> public class NetClient { bool active; bool waitingForCallReturn; MemoryStream returningStream; Socket socket; AsyncCallback connect; AsyncCallback header; AsyncCallback packet; Thread disconnect; byte[] hbuffer; byte[] pbuffer; ManualResetEvent wait; /// <summary> /// Gets the Socket associated with this NetClient. /// </summary> public Socket Socket { get { return socket; } } /// <summary> /// Event raised when we successfully connect to a remote listener. /// </summary> public event EventHandler Connected; /// <summary> /// Event raised when we are disconnected from a remove listener. /// </summary> public event EventHandler Disconnected; /// <summary> /// Event raised when the remote host sends us a packet. /// </summary> public event PacketHandler Read; /// <summary> /// Creates a NetClient and binds it to the local computer on a system-assigned port. /// </summary> public NetClient() { Console.WriteLine("NC"); wait = new ManualResetEvent(false); returningStream=null; waitingForCallReturn=false; active=true; Application.ApplicationExit += new EventHandler(KillMe); socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); socket.Bind(new IPEndPoint(IPAddress.Any, 0)); } /// <summary> /// Creates a NetClient, binds it and connects to a remote host. /// </summary> /// <param name="hostname">Hostname or IP address to connect to.</param> /// <param name="port">The remote port to connect to.</param> public NetClient(string hostname, int port) : this() { Console.WriteLine("NC2"); /* waitingForCallReturn=false; active=true; Application.ApplicationExit += new EventHandler(KillMe); socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); socket.Bind(new IPEndPoint(IPAddress.Any, 0)); disconnect = new Thread(new ThreadStart(DisconnectProc)); disconnect.Start(); */ Connect(hostname, port); } /// <summary> /// Creates a NetClient using a Socket. Not intended for public access. /// </summary> /// <param name="s">The socket to use.</param> internal NetClient(Socket s) { Console.WriteLine("NC Socket"); wait = new ManualResetEvent(false); returningStream=null; waitingForCallReturn=false; active=true; Application.ApplicationExit += new EventHandler(KillMe); socket = s; disconnect = new Thread(new ThreadStart(DisconnectProc)); disconnect.Start(); Start(); } /// <summary> /// Shorts down and disconnects the NetClient /// </summary> ~NetClient() { Console.WriteLine("~NC"); if (socket != null) { socket.Shutdown(SocketShutdown.Both); socket.Close(); socket = null; } } /// <summary> /// Deactivates the Disconnect polling thread. Not intended for public access. /// </summary> /// <param name="o"></param> /// <param name="ea"></param> void KillMe(object o, EventArgs ea) { active=false; } /// <summary> /// Connects to a remote host. /// </summary> /// <param name="hostname">The hostname or IP address to connect to.</param> /// <param name="port">The remote port to connect to.</param> public void Connect(string hostname, int port) { IPHostEntry iphe = Dns.Resolve(hostname); Connect(new IPEndPoint(iphe.AddressList[0], port)); } /// <summary> /// Connects to a remote host. /// </summary> /// <param name="remoteEP">The IPEndPoint to connect to.</param> public void Connect(IPEndPoint remoteEP) { connect = new AsyncCallback(ConnectHandler); socket.BeginConnect(remoteEP, connect, this); } /// <summary> /// Closes any active connection to a remote host. /// </summary> public void Close() { Console.WriteLine("NC Close"); if (socket != null) { socket.Shutdown(SocketShutdown.Both); socket.Close(); socket = null; } } /// <summary> /// Activates packet receiving functionality on this NetClient. /// </summary> public void Start() { Console.WriteLine("NC Start"); hbuffer = new byte[4]; pbuffer = new byte[64]; header = new AsyncCallback(HeaderHandler); packet = new AsyncCallback(PacketHandler); socket.BeginReceive(hbuffer, 0, 4, SocketFlags.None, header, this); } /// <summary> /// Sends a packet to the remote host. /// </summary> /// <param name="stream"></param> public void SendPacket(MemoryStream stream) { MemoryStream ms = new MemoryStream(); BinaryWriter bw = new BinaryWriter(ms); bw.Write((int)stream.Length); bw.Flush(); socket.Send(ms.ToArray()); socket.Send(stream.ToArray()); bw.Close(); } /// <summary> /// Sends a packet to the remote host and waits for a response. /// </summary> /// <param name="stream">The stream to pass to the remote host.</param> /// <returns>The response stream from the remote host.</returns> public MemoryStream SendCall(MemoryStream stream) { returningStream=new MemoryStream(); waitingForCallReturn = true; SendPacket(stream); wait.WaitOne(); wait.Reset(); MemoryStream toret = returningStream; returningStream=null; waitingForCallReturn = false; return toret; } /// <summary> /// Callback for Asynchronous Connection Acceptance. /// </summary> /// <param name="iar"></param> void ConnectHandler(IAsyncResult iar) { if (Connected != null) Connected(this, new EventArgs()); socket.EndConnect(iar); disconnect = new Thread(new ThreadStart(DisconnectProc)); disconnect.Start(); Start(); } /// <summary> /// Handles receiving a packet header and starts the packet body async request. /// </summary> /// <param name="iar"></param> void HeaderHandler(IAsyncResult iar) { int bytes = socket.EndReceive(iar); if (bytes == 4) { BinaryReader br = new BinaryReader(new MemoryStream(hbuffer, 0, 4)); int size = br.ReadInt32(); br.Close(); if (size > pbuffer.Length) pbuffer = new byte[size]; socket.BeginReceive(pbuffer, 0, size, SocketFlags.None, packet, this); } } /// <summary> /// Handles receiving a packet body and starts the next packet header async request. /// </summary> /// <param name="iar"></param> void PacketHandler(IAsyncResult iar) { int bytes = socket.EndReceive(iar); BinaryReader br = new BinaryReader(new MemoryStream(hbuffer, 0, 4)); int size = br.ReadInt32(); br.Close(); if (bytes != size) MessageBox.Show("Packet size is wrong (bytes = " + bytes + ", size = " + size + ")"); else { if (!waitingForCallReturn && Read != null) Read(this, new MemoryStream(pbuffer, 0, size, false)); else if (waitingForCallReturn) sendcall_Read(this, new MemoryStream(pbuffer, 0, size, false)); socket.BeginReceive(hbuffer, 0, 4, SocketFlags.None, header, this); } } /// <summary> /// Waits for the socket to disconnect and raises the Disconnected event. /// </summary> void DisconnectProc() { while (socket.Connected && active) Thread.Sleep(10); if (active && (Disconnected != null)) Disconnected(this, new EventArgs()); Console.WriteLine("Disconnect Proc Shutting down"); } private bool sendcall_Read(object sender, MemoryStream stream) { returningStream = stream; wait.Set(); return false; } } /// <summary> /// Listens for incoming connections, automatically accepts and fires events when a remote connection is made. /// </summary> public sealed class NetListener { bool active; Socket socket; AsyncCallback callback; public event NetClientHandler Accept; /// <summary> /// Creates a new listener on the specified port. /// </summary> /// <param name="port">The port to listen on.</param> /// <remarks>Does not start listening. Call Start() to begin listening.</remarks> public NetListener(int port) { callback = new AsyncCallback(AcceptHandler); socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); socket.Bind(new IPEndPoint(IPAddress.Any, port)); socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, 1); } /// <summary> /// Starts listening for connections. New connections will raise the Accept event. /// </summary> public void Start() { active = true; socket.Listen(-1); socket.BeginAccept(callback, this); } /// <summary> /// Stops listening for connections. /// </summary> public void Stop() { active = false; } /// <summary> /// The Callback for BeginAccept. Not intended for use outside of this class. /// </summary> /// <param name="iar">Callback parameter info required by BeginAccept.</param> void AcceptHandler(IAsyncResult iar) { if (active) { NetClient nc = new NetClient(socket.EndAccept(iar)); if (Accept != null) Accept(this, nc); socket.BeginAccept(callback, this); } } }}
[edited by - Nypyren on January 3, 2004 5:55:02 AM]