private byte[] GetBytes(SMessage m) { StringBuilder sb = new StringBuilder(); sb.Append("Parameters: "); Para p; string name; string value; for (int i = 0; i < m.Parameters.Count; i++) { p = m.Parameters[i]; name = HttpUtility.UrlEncode(p.name); value = HttpUtility.UrlEncode(p.value); sb.Append(name + "=" + value + "&"); } sb.Append("\r\n"); if (m.Error != null) { sb.Append("Error: " + HttpUtility.UrlEncode(m.Error)); sb.Append("\r\n"); } if (m.Content != null) { if (m.ContentLength <= 0) { throw new RPCException("设置了 Content 属性的情况下 ContentLength 应大于 0 。"); } sb.Append("Content-Length: " + m.ContentLength); sb.Append("\r\n"); } sb.Append("\r\n"); string head = sb.ToString(); return(Encoding.ASCII.GetBytes(head)); }
public RMessage Send(SMessage m) { Socket clientSocket = null; RMessage rMsg; try { clientSocket = this.socketPool.Get(); MessageParser mp = new MessageParser(); mp.Send(ref clientSocket, m, this.socketPool); rMsg = mp.Parse(clientSocket); this.socketPool.Return(clientSocket); } catch { if (clientSocket != null) { this.socketPool.Close(clientSocket); } throw; } if (rMsg.Error != null) { throw new RPCServerException(rMsg.Error); } return(rMsg); }
public void Send(ref Socket s, SMessage m, SocketPool socketPool) { byte[] head = GetBytes(m); if (head.Length > headSize) { throw new RPCException("Head 的 长度不能超过 " + headSize + " Byte , 注意 Header 值会进行 Url Encode , 中文字符经过 Url Encode 之后会变长 。"); } // 如果 socketPool != null 表示是 客户端 在调用 Send() 方法 , 发生异常时需要创建新的 Socket 重新 Send() // 反之 , 则表示是 服务器端 在调用 Send() 方法 , 不需要重新 Send() if (socketPool != null) { // 这里的 try catch 是为了解决 服务器关闭连接 后 客户端 不知道 服务器连接 已关闭 的问题。 // 客户端仍然从 SocketPool 中取出之前的 Socket 来使用 , 但因为服务器连接已经关闭, // 所以这个 Socket 是不能使用的,用了会报错,所以,这里在 catch 里会关闭已失效的 Socket , // 并且新创建一个 Socket ,和 服务器建立新的连接,用新的 Socket 来 Send() , // 返回到 RPC.Send() 方法后 , 这个新的 Socket 会被 Return 到 SocketPool 。 // 所以这里的 Socket s 参数是 ref 参数 。 就是为了将 新的 Socket 返回到 RPC.Send() 方法 。 // 正常来讲,服务器不会主动关闭连接,但在一些意外情况,比如服务器意外关闭时,服务器连接会意外关闭 。 try { s.Send(head); } catch { socketPool.Close(s); // 这里要特别先把 s = null; 是因为如果在 SocketPool.GetNew() 中出现异常, // 没有成功的创建 新 Socket 赋值给 s , 则返回 RPC.Send() 方法后,会在 catch 中去关闭 s , // 但这个时候的 s 是在上面已经被关闭的 s ,于是会报“无法访问已释放的对象”的错误 。 s = null; s = socketPool.GetNew(); s.Send(head); } } else { s.Send(head); } if (m.Content == null) { return; } byte[] b; int bufferSize; int sendCount; int readCount; long totalSendCount = 0; while (true) { bufferSize = totalSendCount + sendContentBufferSize <= m.ContentLength ? sendContentBufferSize : (int)(m.ContentLength - totalSendCount); b = new byte[bufferSize]; readCount = m.Content.Read(b, 0, bufferSize); sendCount = s.Send(b, 0, readCount, SocketFlags.None); totalSendCount += sendCount; if (totalSendCount >= m.ContentLength) { break; } } // 如果 Send 的 Content 长度未达到 Content-Length 指定的长度 , 则发送 空字符 \0 来补齐 // 直到达到 Content-Length 的长度 long vacancyLength = m.ContentLength - totalSendCount; if (vacancyLength > 0) { SendVacancy(s, vacancyLength); } }
private void Receive(object clientSocket) { Socket s = (Socket)clientSocket; MessageParser mp = new MessageParser(); RMessage m; SMessage sMsg; SMessage errorMsg; Exception error; while (true) { try { m = null; sMsg = null; errorMsg = null; error = null; m = mp.Parse(s); try { sMsg = OnMessageArrived(m); } catch (Exception e) { error = e; } // 如果 在 OnMessageArrived 中没有读取完 m.Content ,则需要继续读完 , // 否则没有读完的内容会被当成下一次 Request 的 Head , 导致请求错误 。 if (m.Content != null) { mp.ReadToEnd(m.Content); } if (error != null) { errorMsg = new SMessage(); errorMsg.Error = error.Message; mp.Send(ref s, errorMsg, null); } else { mp.Send(ref s, sMsg, null); } } catch (Exception ex) { Console.WriteLine(DateTime.Now.ToString("HH:mm:ss") + " " + ex.ToString()); s.Shutdown(SocketShutdown.Both); s.Close(); break; } } }