我正在使用在Windows服务,.NET Framework 4.6.1,Entity Framework 6.1.3和C#上运行的TCPListener开发Socket服务器。作为数据库我正在使用SQL Server Enterprise 2012 SP 2。
我的套接字客户端上出现套接字超时后,我的数据库出现问题。每次我在客户端获得套接字超时(或任何其他问题)时,我的数据库都会出现奇怪的行为。
我有一个套接字服务器,它使用数据库存储过程来更新表:
public TcpListenerServer(int serverPort)
{
port = serverPort;
connectionString =
ConfigurationManager.ConnectionStrings["DbContext"].ConnectionString;
tcpListenerServer = null;
codesReceived = new CodesReceived();
lockCodesUpdate = new System.Object();
locked = false;
}
public void InitTCPServer()
{
// Create server.
IPEndPoint ipAddress = new IPEndPoint(IPAddress.Any, port);
tcpListenerServer = new TcpListener(ipAddress);
// Create cancellation token.
tokenSource = new CancellationTokenSource();
token = tokenSource.Token;
// Start the service
tcpListenerServer.Start(5);
// Begin an asynchronous connection attemp
tcpListenerServer.
BeginAcceptTcpClient(new AsyncCallback(DoAcceptTcpClientCallback),
tcpListenerServer);
}
public void Stop()
{
if (tcpListenerServer != null)
tcpListenerServer.Stop();
}
private void DoAcceptTcpClientCallback(IAsyncResult ar)
{
TcpClient client = null;
try
{
// Get the listener that handles the client request.
TcpListener listener = (TcpListener)ar.AsyncState;
// End the operation
client = listener.EndAcceptTcpClient(ar);
// Handle the data received
Task.Run(() => { HandleCodeReceived(client, token); }, token);
// Begin a new asynchronous connection attemp
tcpListenerServer.
BeginAcceptTcpClient(new AsyncCallback(DoAcceptTcpClientCallback),
tcpListenerServer);
}
catch (SocketException ex)
{
log.ErrorFormat("DoAcceptTcpClientCallback socket error: {0}, Message {1}", ex.ErrorCode, ex.Message);
Stop();
}
catch (ObjectDisposedException)
{
return;
}
}
private void HandleCodeReceived(TcpClient client, CancellationToken token)
{
// client could be null or not be connected when it reach this method?
if ((client != null) && (client.Connected))
{
try
{
// Read input message.
NetworkStream netStream = client.GetStream();
StreamReader reader = new StreamReader(netStream);
string messageReceived = null;
// The while do this: read ALL the lines sent.
while ((messageReceived = reader.ReadLine()) != null)
{
// If we have a valid message
if ((!string.IsNullOrWhiteSpace(messageReceived)) &&
(IsValid(messageReceived)))
{
// Parse message received from client.
ClientMessage message =
new ClientMessage(messageReceived);
// First check if code is valid.
if ((string.IsNullOrWhiteSpace(message.Code)) ||
(message.Code.Equals("0")))
{
SendBackMessage(false, client);
}
else
{
ProcessCodeReceived(client, message);
}
}
else
SendBackMessage(false, client);
}
}
catch (Exception ex)
{
log.Error(ex.Message);
SendBackMessage(false, client);
}
}
}
private bool IsValid(string messageReceived)
{
string[] cadenas = messageReceived.Split(":".ToCharArray());
return ((cadenas != null) && (cadenas.Length == 8));
}
private void ProcessCodeReceived(TcpClient client, ClientMessage message)
{
DateTime begin = DateTime.UtcNow;
log.DebugFormat("ReadCode: {0} - Start", message.Code);
StoredProcedureErrors result;
result = ReadCode(message.Code, message.Nivel);
if (result != StoredProcedureErrors.NoError)
log.ErrorFormat(ReadCodeErrorMsg, result, message.Code);
else
codesReceived.Reset();
DateTime end = DateTime.UtcNow;
log.DebugFormat("ReadCode: {0} - End: {1} - {2}", message.Code, (end - begin).TotalMilliseconds, result);
SendBackMessage(result == StoredProcedureErrors.NoError, client);
}
private void SendBackMessage(bool isCorrect, TcpClient client)
{
if (isCorrect)
client.GetStream().Write(ASCIIEncoding.UTF8.GetBytes(CorrectMsg + Environment.NewLine), 0, 4);
else
client.GetStream().Write(ASCIIEncoding.UTF8.GetBytes(IncorrectMsg + Environment.NewLine), 0, 4);
}
private StoredProcedureErrors ReadCode(
string code,
byte aggregationlevel)
{
StoredProcedureErrors result =
StoredProcedures.ReadCode(
connectionString,
code,
aggregationlevel,
UserName,
Source,
true);
return result;
}
运行存储过程的代码是:
public static StoredProcedureErrors ReadCode(
string connectionString,
string code,
byte codeLevel,
string param1,
string param2,
bool isAuto)
{
GenericRepository<Code> repository = null;
LDbContext dbContext = new LDbContext(connectionString);
repository = new GenericRepository<Code>(dbContext);
string procName = "ReadCode";
List<SqlParameter> parameters = null;
SqlParameter helperCodeParam = null;
SqlParameter codeParam = new SqlParameter();
codeParam.ParameterName = "@code";
codeParam.SqlDbType = System.Data.SqlDbType.NVarChar;
codeParam.Size = 20;
codeParam.Direction = System.Data.ParameterDirection.Input;
codeParam.Value = code;
[ ... ]
parameters = new List<SqlParameter>();
parameters.Add(codeParam);
parameters.Add(aggregationLevelParam);
parameters.Add(param1Param);
parameters.Add(param2Param);
parameters.Add(isAutoParam);
if (helperCodeParam != null)
parameters.Add(helperCodeParam);
parameters.Add(returnValueParam);
repository.Execute(procName, parameters);
return (StoredProcedureErrors)Convert.ToInt32(returnValueParam.Value);
}
repository.Execute
方法是:
public void Execute(
string storedProcedureName,
IEnumerable<SqlParameter> parameters)
{
SqlConnection connection = null;
try
{
connection =
dbContext.Database.Connection as SqlConnection;
connection.Open();
SqlCommand cmd = new SqlCommand();
cmd.CommandText = storedProcedureName;
cmd.CommandType = CommandType.StoredProcedure;
if (parameters != null)
cmd.Parameters.AddRange(parameters.ToArray());
cmd.Connection = connection;
cmd.ExecuteNonQuery();
}
finally
{
if (connection != null)
connection.Close();
}
}
我的问题非常奇怪:ReadCode
只更新表上的一行(该表用于非常密集地添加新行并更新其他行)。我确定在存储过程结束时更新了正确的行,因为我检查了@@ERROR
和@@ROWCOUNT
。并围绕TRY - CATCH
块中的更新语句(请查看我的DB administrator question有关此问题的信息)。
但它没有更新。如果我在SQL Server Management Studio上执行选择,则它不会更新。
我的第一个问题是:
SqlCommand.ExecuteNonQuery
会进行回滚。99.9%的时间它完美运行,但在客户端遇到套接字错误后,它开始在数据库上做一些奇怪的事情。
更新
在客户端,每次我得到一个SocketException
我关闭套接字并创建一个新套接字。这样做之后,我再也不会遇到数据库错误了。
我不认为这是问题的解决方案,但我不确定问题出在哪里。