これまで大体ソケット通信は同期処理でやってたんだけど、非同期でのソケット通信がしたくなったのでまとめてみた。とはいえ、前回のTouchenceのサンプルで使っている部分を切り出した感じ。
ソースコードはGitHubに上げてるけど、メインのソースもコピペしてみた。サーバ開始時はStartListeningして、終了時はStopListeningしてる。ReadCallbackでStart/Stopのコマンドが来たら文字列送信を開始するフラグを立てて、SendMessageThreadの中で接続ごとにフラグをチェックして文字列送信をしてる。
接続してきたクライアントにデータを垂れ流しながら、何かコマンドを受け取ったらそのデータの形式を変えたり送受信そのものを止めたりするって言うのが出来ると思うんだけど、他の人たちはどうやってるのかな。送信を別スレッド(SendMessageThread)で回しているのは、ちょっとだけ気持ち悪いんだけど・・・
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 |
public partial class MainWindow : Window { public bool runSendDataThread { get; set; } private Thread sendTouchDataThread { get; set; } private int serverPort { get; set; } private List activeConnections { get; set; } private object lockObjectConnection { get; set; } private Socket listener { get; set;} public MainWindow() { InitializeComponent(); lockObjectConnection = new object(); activeConnections = new List(); serverPort = Properties.Settings.Default.LastPort; textBoxPort.Text = serverPort.ToString(); runSendDataThread = false; } private void textBoxPort_PreviewTextInput(object sender, TextCompositionEventArgs e) { bool tmpParse = false; { float tmpF; var tmp = textBoxPort.Text + e.Text; tmpParse = Single.TryParse(tmp, out tmpF); } e.Handled = !tmpParse; } public class StateObject { public Socket workSocket = null; public const int BufferSize = 1024; public byte[] buffer = new byte[BufferSize]; public StringBuilder sb = new StringBuilder(); public bool sendDataFlag = false; } public void StartListening() { IPAddress ipAddress = IPAddress.Parse(GetIPAddress("localhost")); IPEndPoint localEndPoint = new IPEndPoint(ipAddress, serverPort); listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); try { listener.Bind(localEndPoint); listener.Listen(100); listener.BeginAccept(new AsyncCallback(AcceptCallback), listener); } catch (Exception e) { Console.WriteLine(e.ToString()); } } public void StopListening() { listener.Close(); listener = null; } public void AcceptCallback(IAsyncResult ar) { try { Socket listener = (Socket)ar.AsyncState; Socket handler = listener.EndAccept(ar); StateObject state = new StateObject(); state.workSocket = handler; handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state); activeConnections.Add(state); ChangeConnectionNumber(); Console.WriteLine("there is {0} connections", activeConnections.Count); listener.BeginAccept(new AsyncCallback(AcceptCallback), listener); } catch (System.ObjectDisposedException) { System.Console.WriteLine("Connection closed."); return; } } public void ReadCallback(IAsyncResult ar) { String content = String.Empty; StateObject state = (StateObject)ar.AsyncState; Socket handler = state.workSocket; try { int bytesRead = handler.EndReceive(ar); if (bytesRead > 0) { state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead)); content = state.sb.ToString(); if (content.IndexOf("\n") > -1 || content.IndexOf("") > -1) { string getString = content.Replace("\r", "").Replace("\n", ""); if (getString == "Start") { Console.WriteLine("Start sending data"); state.sendDataFlag = true; } else if (getString == "Stop") { Console.WriteLine("Stop sending data"); state.sendDataFlag = false; } state.sb.Length = 0; ; handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state); } else { handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state); } } if (bytesRead == 0) { lock (lockObjectConnection) { Console.WriteLine("Disconnected?"); activeConnections.Remove(state); ChangeConnectionNumber(); } } } catch (Exception e) { lock (lockObjectConnection) { Console.WriteLine(e.Message); activeConnections.Remove(state); ChangeConnectionNumber(); } } } private void Send(Socket handler, String data) { byte[] byteData = Encoding.ASCII.GetBytes(data); handler.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallback), handler); } private void SendCallback(IAsyncResult ar) { try { Socket handler = (Socket)ar.AsyncState; int bytesSent = handler.EndSend(ar); } catch (Exception e) { Console.WriteLine(e.ToString()); } } private void SendMessageThread() { while (runSendDataThread == true) { try { lock (lockObjectConnection) { foreach (StateObject each in activeConnections) { if (each.sendDataFlag == true) { Send(each.workSocket, "Connected\n"); } } } } catch (Exception e) { Console.WriteLine(e.Message); } Thread.Sleep(1000); } lock (lockObjectConnection) { foreach (StateObject each in activeConnections) { each.workSocket.Close(); } } } private string GetIPAddress(string hostname) { IPHostEntry host; host = Dns.GetHostEntry(hostname); foreach (IPAddress ip in host.AddressList) { if (ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) { return ip.ToString(); } } return string.Empty; } private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) { runSendDataThread = false; Properties.Settings.Default.LastPort = serverPort; Properties.Settings.Default.Save(); } private void buttonServerStart_Click(object sender, RoutedEventArgs e) { int tmpPort = 0; if(int.TryParse(textBoxPort.Text, out tmpPort) == false) { return; } serverPort = tmpPort; if (runSendDataThread == false) { ChangeConnectionNumber(); buttonServerStart.Content = "Stop"; StartListening(); runSendDataThread = true; sendTouchDataThread = new Thread(new ThreadStart(SendMessageThread)); sendTouchDataThread.Start(); } else { runSendDataThread = false; sendTouchDataThread.Join(); sendTouchDataThread = null; StopListening(); lock (lockObjectConnection) { activeConnections.RemoveRange(0, activeConnections.Count); } buttonServerStart.Content = "Start"; ChangeConnectionNumber(); } } public void ChangeConnectionNumber() { var dispatcher = System.Windows.Application.Current.Dispatcher; if (dispatcher.CheckAccess()) { labelNum.Content = "Num. of connection: " + activeConnections.Count.ToString(); } else { dispatcher.Invoke(() => { labelNum.Content = "Num. of connection: " + activeConnections.Count.ToString(); }); } } } |
スポンサーリンク