对于大多数人来说,使用Microsoft SQL Server 或 Mysql 数据库可以方便地完成网络B/S,C/S软件的开发。但是,这是一个潜在的生产成本。Microsoft SQL Server和Mysql 的用于生产的版本不是免费的,更何况SQL Server的安装需要Microsoft Windows Server 系统的支持。
其实,完全可以自己动手、丰衣足食,开发出高效的网络应用开发平台,以便支持同时多用户多线程并发读写,并且只需要本地数据库支持,在这里我用 Access 做例子。
应该注意的是,你应该可以发现,我为了稳定的执行多条命令,采用了将发送数据前加上字节长度的方法。例如 “00000000010BADCOMMAND”。这样可以保证所有数据传输过来后才执行命令,并且多条命令不会被当成一条。而且这种模式是绝对稳定的,如果采用别的方法,不一定稳定。如分隔符,如果用“||”当分隔符,万一命令里有分隔符怎么办?还是一件麻烦事。这样,我认为是简单高效的处理方法。
使用lock技术,多线程可以进行放心的读写,但要保证写操作在一个线程上,防止出错。
参考代码:(节选)
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Net.Sockets; using System.Threading; using System.IO; using System.Data.OleDb; using System.Net; namespace Server { public partial class ServerForm : Form { public Thread[] threads = new Thread[100]; public Thread listenThread = null; public bool[] objectlock = new bool[100]; public object locko = new object(); public object l = new object(); public object lock1 = new object(); public int userNum = 0; public ServerForm() { //此处省略用户界面 InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { //load the server listenThread = new Thread(new ThreadStart(this.listen)); listenThread.Start(); } void listen() { try { TcpListener tcp = new TcpListener(System.Net.IPAddress.Any, 2008); tcp.Start(); while (true) { Thread.Sleep(100); if (tcp.Pending()) { Socket s = tcp.AcceptSocket(); for (int i = 0; i < = 99; i++) { if (threads[i] == null || threads[i].ThreadState != ThreadState.Running) { threads[i] = new Thread(new ParameterizedThreadStart(this.doServerEvents)); threads[i].Start(s); break; } } } } } catch (Exception e) { //todo } finally { } } /// /// 独立服务器组件 /// /// <span> </span>Socket void doServerEvents(object o) { //客户端发送数据的长度最大限度为8位数,如99999999。 //服务器发送数据最大限度为11位数,如99999999999。 /////////////////////////////////////////////////////////////////////////////////////////////////// Socket s = (Socket)o;//连接 byte[] butter = new byte[10240];//缓冲区 StringBuilder str = new StringBuilder();//命令字符串 CConn conn = new CConn(); List lockList = new List(); /////////////////////////////////////////////////////////////////////////////////////////////////// try { lock (lock1) userNum++; conn.Log.WriteToFile = false; conn.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + this.GetAccessFileName() + ";Persist Security Info=False;Jet OLEDB:DataBase Password=123"; conn.Open(); bool login = false; int len = 0; int length = -1; bool bad = false; while (s.Connected && (len = s.Receive(butter, 10240, SocketFlags.None)) > 0) { str.Append(Encoding.Default.GetString(butter, 0, len)); if (length == -1) { if (int.TryParse(str.ToString(0, 8), out length) == false) { s.Send(Encoding.Default.GetBytes("00000000010BADCOMMAND")); s.Close(); break; } str.Remove(0, 8); } while (str.Length >= length) { object ob = doCommand(str.ToString(0, length), ref login, conn, lockList); string r = ""; if (ob != null && ob is string) { r = ob.ToString(); if (r != null) { s.Send(Encoding.Default.GetBytes(r.Length.ToString().PadLeft(11, '0') + r)); } } else { if (ob != null) { byte[] b = (byte[])ob; if (b != null) { s.Send(Encoding.Default.GetBytes(b.Length.ToString().PadLeft(11, '0'))); s.Send(b); } } } str.Remove(0, length); length = -1; if (str.Length < 8) break; if (int.TryParse(str.ToString(0, 8), out length) == false) { s.Send(Encoding.Default.GetBytes("00000000010BADCOMMAND")); s.Close(); bad = true; break; } str.Remove(0, 8); } if (bad) break; } } catch (Exception e) { s.Close(); conn.Close(); return; } finally { lock (this.lock1) userNum--; this.ClearLock(lockList); s.Close(); conn.Close(); } } /// /// 进行服务器端命令操作。发生任何错误会返回以"_"开头的错误信息文本。 /// /// 命令 /// 是否登录 /// 连接 /// 返回值 object doCommand(string commStr, ref bool isLogin, CConn conn, List lockList) { try { string[] comm = commStr.Split((char)9); if (comm.Length == 0) { return null; } if (!isLogin) { if (comm[0] != "login") { return "_您没有执行本命令的权限。错误: (10000) 没有成功登录。"; } } switch (comm[0]) { //////////////////////////// hello 测试命令 ///////////////////////////////////// case "hello": { return "OKTEXT"; break; } //////////////////////////// login 登录 ///////////////////////////////////// case "login": { if (comm[1] != "登录验证信息") { return "F"; } if (comm[2] != "服务器版本") { return "F"; } isLogin = true; return "T"; break; } //////////////////////////// 事务处理 ///////////////////////////////////// case "beginTransaction": { try { OleDbTransaction tran = conn.Connection.BeginTransaction(); conn.Command.Transaction = tran; return "T"; } catch { return "F"; } } case "commitTransaction": { try { if (conn.Command.Transaction != null) { conn.Command.Transaction.Commit(); conn.Command.Transaction = null; } return "T"; } catch { return "F"; } } case "rollBackTransaction": { try { if (conn.Command.Transaction != null) { conn.Command.Transaction.Rollback(); conn.Command.Transaction = null; } return "T"; } catch { return "F"; } } //////////////////////////// bye 退出登录 ///////////////////////////////////// case "bye": { this.ClearLock(lockList); return "OK"; break; } case "onlinecount": { int count = 0; lock (lock1) count = userNum; return count.ToString(); break; } //////////////////////////// lock 锁定 ///////////////////////////////////// case "lock": { if (comm.Length != 2) return "_ERROR"; int li; if (int.TryParse(comm[1], out li) == false) { return "_ERROR"; } lock (locko) { if (objectlock[li]) return "F"; objectlock[li] = true; lockList.Add(li); return "T"; } break; } case "unlock": { if (comm.Length != 2) return "_ERROR"; int li; if (int.TryParse(comm[1], out li) == false) { return "_ERROR"; } lock (locko) { if (objectlock[li] == false) return "F"; objectlock[li] = false; lockList.Remove(li); return "T"; } break; } case "testlock": { if (comm.Length != 2) return "_ERROR"; int li; if (int.TryParse(comm[1], out li) == false) { return "_ERROR"; } lock (locko) { if (objectlock[li] == false) return "F"; return "T"; } break; } //////////////////////////// get 获得记录集 ///////////////////////////////////// case "getfiles": { if (comm.Length != 3) return "_ERROR"; string[] files=null; if (comm[1] == "") comm[1] = Tool.GetPath(); try { files=System.IO.Directory.GetFiles(comm[1], comm[2], SearchOption.TopDirectoryOnly); } catch (Exception e) { return "_" + e.Message; } string ret = "FileNametSizetCreatedDatetChangedDater"; foreach (string str in files) { FileInfo info = new FileInfo(str); ret += str + "t"; ret += info.Length.ToString() + "t"; ret += info.CreationTime.ToString() + "t"; ret += info.LastWriteTime.ToString() + "r"; } return ret.Substring(0, ret.Length - 1); break; } case "getfilesize": { if (comm.Length != 2) return "_ERROR"; try { if (comm[1] == "{}") comm[1] = this.GetAccessFileName(); FileInfo info = new FileInfo(comm[1]); return info.Length.ToString(); } catch (Exception e) { return "_" + e.Message; } } case "backup": { if (comm.Length != 2) return "_请输入文件名。"; try { System.IO.File.Copy(this.GetAccessFileName(), Tool.GetPath() + comm[1], true); } catch(Exception e) { return "_" + e.Message; } return "OK"; } case "restore": { if (comm.Length != 2) return "_请输入文件名。"; try { conn.Close(); System.IO.File.Copy(Tool.GetPath() + comm[1], this.GetAccessFileName(), true); conn = new CConn (); conn.Log.WriteToFile = false; conn.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + this.GetAccessFileName() + ";Persist Security Info=False;Jet OLEDB:DataBase Password=123"; conn.Open(); } catch (Exception e) { return "_" + e.Message; } return "OK"; } case "download": ///////// 下载文件。这儿函数是为了获得单机缓存数据库而设置的。 { if (comm.Length != 4) return "_ERROR"; if (comm[1] == "{}") { comm[1] = this.GetAccessFileName() + "t"; System.IO.File.Copy(this.GetAccessFileName(), comm[1], true); } try { int start = int.Parse(comm[2]); int end = int.Parse(comm[3]); byte[] dst = new byte[end - start + 1]; ServerForm.Copy(System.IO.File.ReadAllBytes(comm[1]), start - 1, dst, 0, end - start + 1); return dst; } catch (Exception e) { return new byte[0]; } } case "get": { if (comm.Length != 2) return "_ERROR"; ICDataSet set = null; try { set = conn.GetDataSet(comm[1]); } catch (Exception e) { return "_" + e.Message; } if (set == null) { return "_ERROR"; } string ret = set.GetString(); set.Close(); return ret; break; } case "getone": { if (comm.Length != 2) return "_ERROR"; object o = conn.GetSqlResult(comm[1]); if (o == null) { return ""; } return o.ToString(); break; } //////////////////////////// exec 执行 ///////////////////////////////////// case "exec": { if (comm.Length != 2) return "_ERROR"; int ret = -1; string err = ""; lock (l) { try { ret = conn.ExecuteSql(comm[1]); } catch (Exception e) { err = e.Message; } } if (err == "") return ret.ToString(); else return "_" + err; break; } } return null; } catch (Exception e) { return "_" + e.Message; } } private string GetAccessFileName() { return @"数据库文件名"; } private void ClearLock(List lockList) { lock (locko) { foreach (int li in lockList) { this.objectlock[li] = false; } } } private void Form1_FormClosing(object sender, FormClosingEventArgs e) { this.CloseServer(); } private void CloseServer() { listenThread.Abort(); for (int i = 0; i < = 99; i++) { if (threads[i] != null) threads[i].Abort(); } static unsafe void Copy(byte[] src, int srcIndex, byte[] dst, int dstIndex, int count) //摘自MSDN的UNSAFE子程序 { if (src == null || srcIndex < 0 || dst == null || dstIndex < 0 || count < 0) { throw new System.ArgumentException(); } int srcLen = src.Length; int dstLen = dst.Length; if (srcLen - srcIndex < count || dstLen - dstIndex < count) { throw new System.ArgumentException(); } // The following fixed statement pins the location of the src and dst objects // in memory so that they will not be moved by garbage collection. fixed (byte* pSrc = src, pDst = dst) { byte* ps = pSrc; byte* pd = pDst; // Loop over the count in blocks of 4 bytes, copying an integer (4 bytes) at a time: for (int i = 0; i < count / 4; i++) { *((int*)pd) = *((int*)ps); pd += 4; ps += 4; } // Complete the copy by moving any bytes that weren't moved in blocks of 4: for (int i = 0; i < count % 4; i++) { *pd = *ps; pd++; ps++; } } } } |