سرور TCP/IP انعطاف پذیر با وب سرویس توسط DLL
یکشنبه 27 دی 1394سرور انعطاف پذیر از یک DLL ایجاد شده برای مدیریت درخواست های ورودی و خروجی کلاینت استفاده می کند. در این نمونه برنامه می خواهیم از نوشتن مجدد یک سرور پایه در هر بار جلوگیری کرده و فقط با نوشتن بسته متدهای از قبل ایجاد شده، یک DLL اضافه کنیم.
تصور کنید می خواهیم از یک سرور چت به کنترل از راه دور یک موس سوئیچ کنیم و این کار باید در کمتر از یک دقیقه انجام شود. به نظرتون عالی نیست؟ برای این کار فقط یک DLL را جایگزین کرده یا نام DLL را در تنظیمات سرور با یک DLL جدید تغییر می دهیم.
این پروژه شامل موار زیر است:
سرور انعطاف پذیر- سرور
چت سمت کلاینت- یک چت ساده برای تست این که سرور کار می کند یا خیر ایجاد می کنیم.
Client Utils- که شامل کلاس هایی برای کمک به اتصال به سرور می باشد.
ورود به سیستم(Logging)- کلاسی برای خوانایی بهتر کنسول با استفاده از سطح ورود به سیستم می باشد.
MethodResponse- کلاسی است که در سرور و DLL ایجاد شده تا با یکدیگر ارتباط برقرار کنند.
TestDLL- یک DLL ساده برای مدیریت ورودی ها به چت کلاینت می باشد.
MysqlConnector(اختیاری)- شامل کلاسی برای کمک به اتصال به سرور mysql می باشد و Query ها را انجام می دهد.
نحوه عملکرد پروژه
و خروجی کار به صورت زیر می باشد:
معرفی مزایا و معایب استفاده از سرور انعطاف پذیر یا Felexible Server
مزایا
- استفاده از آن آسان تر است.
- برای استفاده از آن کافیست DLL های خود را با متدهایی که نیاز دارید بنویسید.
- شما می توانید هدف سرور را در کمتر از یک دقیقه با جایگزینی DLL مورد نظر تغییر دهید.
معایب
- کمی کند عمل می کند.
هدف ما در این برنامه چیست؟
ما می خواهیم یک چت کوچک بین کلاینت/سرور ایجاد کنیم تا ببینیم این سرور چگونه کار می کند.
سرور
بارگذاری DLL
این سرور مونتاژی از DLL مشخص را بارگذاری می کند( در این مثال DLL مورد نظر TestDLL.dll می باشد.)
این DLL باید شامل کلاس PacketHandler باشد. PacketHandler باید شامل متدهای OnClientConnect و OnClientDisconnect باشد، و موقعی از آنها استفاده می کنیم که بخواهیم برخی کارها را در زمانی که اتصال یک کلاینت وصل یا قطع است، انجام دهیم.
این سرور فقط متدهای عمومی(Public) را با نوع بازگشتی MethodResponse بارگذاری می کند.
MethodResponse هم توسط سرور و هم dll برای اتصال با یکدیگر استفاده می شود. در نهایت سرور اطلاعات متدهای داخل یک لیست برای فراخوانی آنها ذخیره می کند:
//Get User Created DLL string handlerDLL = GetConfig().data["packetHandlerDLL"]; Assembly packetHandlerDllAssembly = null; //Check if User Created DLL exists else close Server if (File.Exists(handlerDLL)) { //Load User Created DLL Assembly packetHandlerDllAssembly = Assembly.LoadFrom(AppDomain.CurrentDomain.BaseDirectory + handlerDLL); Logging.WriteLine("Loading Packet Handler DLL", LogLevel.Information); //Get PacketHandler Class Type Class = packetHandlerDllAssembly.GetType("PacketHandler"); try { //Create a instance of PacketHandler Class dllInstance = Activator.CreateInstance(Class); } catch (Exception e) { Logging.WriteLine("User Created DLL must have " + "PacketHandler Class. Closing..", LogLevel.Error); Thread.Sleep(5000); Environment.Exit(0); } int MethodsCount = 0; //Create a list of methods RegisteredMethods = new List<Method>(); bool OnClientConnectMethodFound = false; bool OnClientDisconnectMethodFound = false; //Get methods created by user foreach (MethodInfo MethodInfo in Class.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance)) { //Check if OnClientConnect and OnClientDisconnect methods exist if (MethodInfo.Name == "OnClientConnect") { OnClientConnectMethodFound = true; continue; } if (MethodInfo.Name == "OnClientDisconnect") { OnClientDisconnectMethodFound = true; continue; } //Only load methods with MethodResponse return type if (MethodInfo.ReturnType != typeof(MethodResponse)) { Logging.WriteLine("Method: " + MethodInfo.Name + " must return MethodResponse currently: " + MethodInfo.ReturnType.Name, LogLevel.Error); Logging.WriteLine("Method: " + MethodInfo.Name + " not registered", LogLevel.Error); continue; } string param = ""; //Create a new method class. MethodInfo is necessary for future invokes of DLL Methods Method Method = new Method(MethodInfo.Name, MethodInfo); //Method must have connID(int) Param bool connIDParameterFound = false; //Get method parameters foreach (ParameterInfo pParameter in MethodInfo.GetParameters()) { //Add Parameter Method.AddParameter(pParameter.Name, pParameter.ParameterType); param += pParameter.Name + " (" + pParameter.ParameterType.Name + ") "; if (pParameter.Name.ToLower() == "connid" && pParameter.ParameterType == typeof(int)) { connIDParameterFound = true; } } if (!connIDParameterFound) { Logging.WriteLine("Method: " + MethodInfo.Name + " must have a connID(int) param", LogLevel.Error); Logging.WriteLine("Method: " + MethodInfo.Name + " not registered", LogLevel.Error); continue; } if (param == "") param = "none "; //Add method to the registered methods list RegisteredMethods.Add(Method); Logging.WriteLine("Method name: " + MethodInfo.Name + " parameters: " + param + "registered", LogLevel.Information); MethodsCount++; } if (!OnClientConnectMethodFound || !OnClientDisconnectMethodFound) { Logging.WriteLine("PacketHandler must contain OnClientConnect and " + "OnClientDisconnect methods. Closing..", LogLevel.Error); Thread.Sleep(5000); Environment.Exit(0); } //Close server if there is any registered method if (MethodsCount == 0) { Logging.WriteLine("Any method loaded. Closing..", LogLevel.Information); Thread.Sleep(5000); Environment.Exit(0); } Logging.WriteLine("Registered " + MethodsCount + " Methods", LogLevel.Information); Logging.WriteLine("Loaded Packet Handler DLL", LogLevel.Information); } else { Logging.WriteLine("Unable to locate Packet Handler DLL named: " + handlerDLL + ". Closing..", LogLevel.Error); Thread.Sleep(5000); Environment.Exit(0); }
پیام جدید از کلاینت
زمانی که سرور یک پیام جدید از کلاینت دریافت میکند، اولین بار پیام را به کلاس Packet تجزیه کرده و سپس این بسته را به متد HandlePacket ارسال می کند:
/// <summary> /// On Packet received callback</summary> /// <param name="result">Status of asynchronous operation</param> /// </summary> private void ReceiveCallback(IAsyncResult result) { //get our connection from the callback Connection conn = (Connection)result.AsyncState; try { //Grab our buffer and count the number of bytes receives int bytesRead = conn.socket.EndReceive(result); if (bytesRead > 0) { HandlePacket(ParseMessage(Encoding.ASCII.GetString(conn.buffer, 0, bytesRead), conn), conn); //Queue the next receive conn.socket.BeginReceive(conn.buffer, 0, conn.buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), conn); } else //Client disconnected { Core.GetLogging().WriteLine("[" + conn.connID + "] Connection lost from " + ((IPEndPoint)conn.socket.RemoteEndPoint).Address, LogLevel.Debug); OnClientDisconnect(conn); conn.socket.Close(); lock (_sockets) { _sockets.Remove(conn); } } } catch (SocketException e) { Core.GetLogging().WriteLine("[" + conn.connID + "] Connection lost from " + ((IPEndPoint)conn.socket.RemoteEndPoint).Address, LogLevel.Debug); OnClientDisconnect(conn); if (conn.socket != null) { conn.socket.Close(); lock (_sockets) { _sockets.Remove(conn); } } } }
متدهای OnClientConnect و OnClientDisconnect
متدهای OnClientConnect/OnClientDisconnect با ارسال id اتصال کلاینت در DLL آن ها را فراخوانی می کنند:
/// <summary> /// Invoke OnClientConnect on User Created Dll</summary> /// <param name="conn">Client connection</param> /// </summary> private void OnClientConnect(Connection conn) { Core.dllInstance.GetType().GetMethod("OnClientConnect").Invoke( Core.dllInstance, new object[] { conn.connID }); } /// <summary> /// Invoke OnClientDisconnect on User Created Dll</summary> /// <param name="conn">Client connection</param> /// </summary> private void OnClientDisconnect(Connection conn) { Core.dllInstance.GetType().GetMethod("OnClientDisconnect").Invoke( Core.dllInstance, new object[] { conn.connID }); }
تجزیه پیام های وارده از سمت کلاینت
پیام های دریافتی از سمت مشتری به کلاس Packet تجزیه می شوند.
در ابتدا ما یک نام یا هدر بسته(Packet) را دریافت می کنیم، سپس باید نوع مقادیر بدنه Packet را به نوع درست و صحیح تجزیه کنیم(به طور مثال Int، float و غیره). این کار لازم است، زیرا در غیر این صورت یک استثنا به صورت " عدم تطابق نوع پارامتر " یا به صورت انگلیسی" parameter type mismatch"دریافت می کنیم.
/// <summary> /// Parse message string to Packet class</summary> /// <param name="message">Packet string</param> /// <param name="conn">Client connection</param> /// </summary> private Packet ParseMessage(string Message, Connection conn) { string PacketHeader = Message.Split(Delimiter)[0]; Packet Packet = new Packet(PacketHeader); Message = Message.Substring(Message.IndexOf(Delimiter) + 1); //Only Packet Body //Parse type from incoming packet body values foreach (string Parameter in Message.Split(Delimiter)) { //TO-DO more type parsing int intN; bool boolN; if (int.TryParse(Parameter, out intN)) { Packet.AddInt32(intN); } else if (Boolean.TryParse(Parameter, out boolN)) { Packet.AddBoolean(boolN); } else { Packet.AddString(Parameter); } } //Always add connID to Packet to get client id on User Created DLL Packet.AddInt32(conn.connID); return Packet; }
Handle Packet یا بسته مدیریتی
مدیریت بسته های تجزیه شده
سرور متد Packet دریافتی یک DLL را که از قبل تجزیه شده، با پارامترهای مشخص فراخوانی می کند. در DLL به بسته مربوط استناد کرده و پارامترهایی که از قبل تجزیه شدند را دریافت می کند. متد فراخوانی شده یک شیئ برمی گرداند که مقدار برگشتی این متد را نگه می دارد که نیاز دارد به نوع MethodResponse تجزیه شود.
در نهایت با Packet ها حلقه ای را تشکلیل می دهند که شامل MethodResponse بوده و یک پیام را به کلاینت یا کلاینت ها می فرستند.
/// <summary> /// Invoke the packet-associated method and send response packets contained in MethodResponse</summary> /// <param name="Packet">The incoming packet</param> /// <param name="conn">Client connection</param> /// </summary> private void HandlePacket(Packet Packet, Connection conn) { Core.GetLogging().WriteLine("Received Packet: " + Packet.GetPacketString(), LogLevel.Debug); //Get associated Packet method using packet header/name Method Method = Core.GetMethodByName(Packet.Header.ToLower()); if (Method != null) { //Packet body values count must match with method parameters count if (Method.GetParametersCount() != Packet.bodyValues.Count) { Core.GetLogging().WriteLine("Method: " + Method.GetName() + " has " + Method.GetParametersCount() + " params but client request has " + Packet.bodyValues.Count + " params", LogLevel.Error); } else { MethodResponse result = null; try { //Try invoke associated method given packet body values as parameters result = (MethodResponse)Method.GetMethodInfo().Invoke( Core.dllInstance, Packet.bodyValues.ToArray()); } catch (Exception e) { Core.GetLogging().WriteLine("Error handling Method: " + Method.GetName() + " Exception Message: " + e.Message, LogLevel.Error); } if (result != null) { Core.GetLogging().WriteLine("Handled Method: " + Method.GetName() + ". Sending response..", LogLevel.Information); //Invoke succeed! now read Packets contained //in MethodResponse and send them to the specified clients foreach (Packet PacketToSend in result.Packets) { string PacketString = PacketToSend.GetPacketString(); if (PacketToSend.sendToAll) //Send to all clients { sendToAll(StrToByteArray(PacketString)); Core.GetLogging().WriteLine("Sent response: " + PacketString + " to all clients", LogLevel.Debug); } else if (PacketToSend.sendTo != null) //Only send to clients specified in a list { foreach (int connID in PacketToSend.sendTo) { Send(StrToByteArray(PacketString), _sockets[connID]); Core.GetLogging().WriteLine("Sent response: " + PacketString + " to client id: " + connID, LogLevel.Debug); } } else //Send to sender { Send(StrToByteArray(PacketString), conn); Core.GetLogging().WriteLine("Sent response: " + PacketString + " to client id: " + conn.connID, LogLevel.Debug); } } } } } else Core.GetLogging().WriteLine("Invoked Method: " + Packet.Header + " does not exist", LogLevel.Error); }
DLL
این DLL ای است که توسط آن می توانید سرور خود را به صورتی که نیاز دارید سفارشی کنید. در اینجا می توانید متدهای سفارشی خود را بنویسید.
اگر می خواهید سرور بارگذاری نشود، می توانید از یک Flag خصوصی در این متدها استفاده کنید.
PacketHandler باید شامل هردو متد OnClientConnect و OnClientDisconnect باشد
هر متد عمومی باید یک نوع بازگشتی از نوع MethodResponse داشته باشد.
//PacketHandler class must be public to let server reads methods public class PacketHandler { public static List<User> Users; private Logging Logging; /// <summary> /// Initialize variables /// </summary> public PacketHandler() { Users = new List<User>(); Logging = new Logging(); Logging.MinimumLogLevel = 0; } /// <summary> /// Return Chat User by Connection ID /// <param name="connID">Connection ID</param> /// </summary> //Prevent server to load these methods using private flag private User GetUserByConnID(int connID) { foreach (User u in Users) { if (u.connID == connID) return u; } return null; } /// <summary> /// Return Chat User by Name /// <param name="Name">User Name</param> /// </summary> private User GetUserByName(string Name) { foreach (User u in Users) { if (u.Name == Name) return u; } return null; } /// <summary> /// Handle Chat User Login /// <param name="username">Username given by Chat User</param> /// <param name="password">Password given by Chat User</param> /// <param name="connID">Connection ID provided by server</param> /// </summary> //Public method must return a result of type MethodResponse public MethodResponse Login(string username, string password, int connID) { //Create a new MethodResponse MethodResponse MethodResponse = new MethodResponse(); bool loginFailed = true; if (password == "password") loginFailed = false; if (loginFailed) { //Create a new Packet LOGIN_RESPONSE and send Packet to the sender Packet LoginResponse = new Packet("LOGIN_RESPONSE"); //Add a boolean value to Packet. It means login failed LoginResponse.AddBoolean(false); //Add Packet to MethodResponse MethodResponse.AddPacket(LoginResponse); } else { Packet LoginResponse = new Packet("LOGIN_RESPONSE"); LoginResponse.AddBoolean(true);//It means successful login //Add a int value to Packet. It provides client the connection ID for future use LoginResponse.AddInt32(connID); //Announce to all clients a new user joined //Set sendToAll parameter to true (default false) //if you want to send Packet to all clients Packet UserJoin = new Packet("USER_JOIN", true); //Add the name of the Chat User joined UserJoin.AddString(username); //Add Packets to MethodResponse MethodResponse.AddPacket(LoginResponse); MethodResponse.AddPacket(UserJoin); Users.Add(new User(connID, username)); //Add the Chat User to a List //Write on server console from dll Logging.WriteLine("User: " + username + " has joined the chat", LogLevel.Information); } return MethodResponse; //Return MethodResponse to Server } ...Other chat methods... /// <summary> /// Must always be declared. it will be called when a client disconnect /// <param name="connID">Connection ID provided by server</param> /// </summary> public void OnClientDisconnect(int connID) { if (GetUserByConnID(connID) != null) { Logging.WriteLine("User: " + GetUserByConnID(connID).Name + " has left the chat", LogLevel.Information); Users.Remove(GetUserByConnID(connID)); } } /// <summary> /// Must always be declared. it will be called when a client connect /// <param name="connID">Connection ID provided by server</param> /// </summary> public void OnClientConnect(int connID) { } }
کلاینت
این یک کلاینت از برنامه چت ساده است. شما می توانید به سرور متصل شده و Login را انجام دهید. سپس قادر خواهید بود که یک پیام به تمام کاربران متصل بفرستید.
static void Main(string[] args) { conn = new ServerConnection(); //Connect to the server on 127.0.0.1:8888 conn.Connect(IPAddress.Parse("127.0.0.1"), 8888); //Handle received Packet conn.onPacketReceive += new ServerConnection.onPacketReceiveHandler(HandlePacket); ... } /// <summary> /// Ask Login /// </summary> static void Login() { Console.WriteLine("Write username"); username = Console.ReadLine(); //Read user input Console.WriteLine("Write password"); string password = Console.ReadLine(); //Read user input Packet Login = new Packet("LOGIN"); //Create a new Packet LOGIN Login.AddString(username); //Add the username to Packet Login.AddString(password); //Add the password to Packet conn.Send(Login); //Send the Packet to the server } /// <summary> /// Handle the received Packet /// <param name="sender">Class on which the event has been fired</param> /// <param name="Packet">The received packet</param> /// </summary> static void HandlePacket(object sender, Packet Packet) { switch (Packet.Header) { case "LOGIN_RESPONSE": //Received LOGIN_RESPONSE Packet { bool loginResponse = Convert.ToBoolean(Packet.bodyValues[0]); //Get Login Response from Packet Body if (!loginResponse) { Console.WriteLine("Login failed"); Login(); //Ask login until logged } else { id = int.Parse(Packet.bodyValues[1].ToString()); //Get Connection ID from Packet Body Console.WriteLine("Login Successful"); Logged = true; //User has logged in } } break; ... } }
وب سرویس
ما همچنین یک سرویس وب منعطف را پیاده سازی کرده تا یک نوع از پنل وب سرور مرتب سازی شده ایجاد کنیم.
یک پنل وب سرور ساده برای چت
webServiceConnector.php
این اسکریپت یک ورود به وب سرویس را انجام می دهد و متدهای GetUserCount و GetUserList را فراخوانی می کند.
پاسخ در قالب JSON بیان می شود.
<?php if (isset($_GET['readData'])) { //Connect to WebService $client = new SoapClient("http://localhost:8000/FlexibleServer/?wsdl",array( 'login' => "admin", 'password' => "password")); try { //Get user count $response = $client->__soapCall("GetUserCount",array()); $arr=objectToArray($response); //Get user list $response2 = $client->__soapCall("GetUserList",array()); $arr2=objectToArray($response2); //Merge results $result = array_merge($arr,$arr2); //Encode array to json echo json_encode($result); } catch (SoapFault $exception) { trigger_error("SOAP Fault: (faultcode: {$exception->faultcode}, faultstring: {$exception->faultstring})"); var_dump($exception); } } ?>
تابع Javascript
این تابع یک درخواست ajax را انجام داده و webServiceConnector.php را برای در یافت پاسخ Json فراخوانی می کند.
function read() { var xmlhttp; xmlhttp = GetXmlHttpObject(); if(xmlhttp == null) { alert("Boo! Your browser doesn't support AJAX!"); return; } xmlhttp.onreadystatechange = stateChanged; //Get page source xmlhttp.open("GET", "http://127.0.0.1/webServiceConnector.php?readData", true); xmlhttp.send(null); function stateChanged() { if(xmlhttp.readyState == 4) { //Parse json from source var obj = jQuery.parseJSON(xmlhttp.responseText); //Refresh gage with user count value g1.refresh(obj["GetUserCountResult"]); //Get textarea element var txtarea = document.getElementById("txtarea"); if (obj["GetUserListResult"]["string"] != null) { var length = obj["GetUserListResult"]["string"].length; var s = ""; //Append Users' Names for (var i = 0; i < length; i++) { s += obj["GetUserListResult"]["string"][i]; } //Display names txtarea.innerHTML = s; txtarea.scrollTop = txtarea.scrollHeight; } else { txtarea.innerHTML = ""; txtarea.scrollTop = txtarea.scrollHeight; } //Refresh every second setTimeout("read()",1000); } } function GetXmlHttpObject() { if(window.XMLHttpRequest){ return new XMLHttpRequest(); } if(window.ActiveXObject){ return new ActiveXObject("Microsoft.XMLHTTP"); } return null; } }
داخل DLL
متدهایی که از یک اسکریپت فراخوانی می شود.
public class WebserviceHandler : IWebservice { public string[] GetUserList() { List<string> Names = new List<string>(); foreach (User User in PacketHandler.Users) Names.Add(User.Name + "\n"); return Names.ToArray(); } public int GetUserCount() { return PacketHandler.Users.Count; } } [ServiceContract] public interface IWebservice { [OperationContract] string[] GetUserList(); [OperationContract] int GetUserCount(); }
کد سرور برای شروع کار با وب سرویس
/// <summary> /// Start webService /// </summary> public void Start() { Uri baseAddress = new Uri("http://" + IP.ToString() + ":" + Port + "/FlexibleServer/"); //Get WebserviceHandler from User Created DLL Type Webservice = packetHandlerDllAssembly.GetType("WebserviceHandler"); //Get Webservice interface from User Created DLL Type Interface = packetHandlerDllAssembly.GetType("IWebservice"); //Get webService methods created by user foreach (MethodInfo m in Interface.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance)) { string param = ""; //Get method parameters foreach (ParameterInfo pParameter in m.GetParameters()) { param += pParameter.Name + " (" + pParameter.ParameterType.Name + ") "; } if (param == "") param = "none "; Core.GetLogging().WriteLine("webService Method name: " + m.Name + " parameters: " + param + "registered", LogLevel.Information); } // Create the ServiceHost. Bind on http://ip:port/FlexibleServer/ ServiceHost selfHost = new ServiceHost(Webservice, baseAddress); //Binding to configure endpoint BasicHttpBinding http = new BasicHttpBinding(); //Set a basic username/password authentication http.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly; http.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic; try { //Add the endpoint to the service host ServiceEndpoint endpoint = selfHost.AddServiceEndpoint( Interface, http, "RemoteControlService"); //Add the Custom webService Behavior to endpoint endpoint.Behaviors.Add(new webServiceEvent()); //Set the custom username/password validation selfHost.Credentials.UserNameAuthentication.UserNamePasswordValidationMode = UserNamePasswordValidationMode.Custom; selfHost.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = new LoginValidator(); // Enable metadata publishing. ServiceMetadataBehavior smb = selfHost.Description.Behaviors.Find<ServiceMetadataBehavior>(); if (smb == null) { smb = new ServiceMetadataBehavior(); smb.HttpGetEnabled = true; selfHost.Description.Behaviors.Add(smb); } try { //Start webService selfHost.Open(); Core.GetLogging().WriteLine("webService is ready on http://" + IP.ToString() + ":" + Port + "/FlexibleServer/", LogLevel.Information); } catch (Exception e) { if (e is AddressAccessDeniedException) { Core.GetLogging().WriteLine("Could not register url: http://" + IP + ":" + Port + ". Start server as administrator", LogLevel.Error); } if (e is AddressAlreadyInUseException) { Core.GetLogging().WriteLine("Could not register url: http://" + IP + ":" + Port + ". Address already in use", LogLevel.Error); } Core.GetLogging().WriteLine("webService aborted due to an exception", LogLevel.Error); } } catch (CommunicationException ce) { Console.WriteLine("An exception occurred: {0}", ce.Message); selfHost.Abort(); } }
چگونگی استفاده از MysqlConnector (اختیاری)
یک رفرنس به MysqlConnector.dll و MySql.Data.dll در TestDLL اضافه کنید.
اضافه کردن
using System.Data; using MysqlConnector;
مثال: مقدار دهی اولیه اتصال mysql
public class PacketHandler { public static List<User> Users; private Logging Logging; public Mysql MysqlConn; /// <summary> /// Initialize variables /// </summary> public PacketHandler() { Users = new List<User>(); Logging = new Logging(); Logging.MinimumLogLevel = 0; MysqlConn = new Mysql(); MysqlConn.Connect("127.0.0.1", 3306, "root", "password", "databasename"); MysqlConn.GetClient(); } ... }
متد Chat login با استفاده از MySql
/// <summary> /// Handle Chat User Login /// <param name="username">Username given by Chat User</param> /// <param name="password">Password given by Chat User</param> /// <param name="connID">Connection ID provided by server</param> /// </summary> //Public method must return a result of type MethodResponse public MethodResponse Login(object username, object password, int connID) { //Create a new MethodResponse MethodResponse MethodResponse = new MethodResponse(); //Check if user exists from mysql DataRow Row = MysqlConn.ReadDataRow("SELECT * FROM users where username = '" + username + "' AND password = '" + password + "'"); bool loginFailed = true; if (Row != null) { loginFailed = false; } if (loginFailed) { //Create a new Packet LOGIN_RESPONSE and send Packet to the sender Packet LoginResponse = new Packet("LOGIN_RESPONSE"); //Add a boolean value to Packet. It means login failed LoginResponse.AddBoolean(false); //Add Packet to MethodResponse MethodResponse.AddPacket(LoginResponse); } else { Packet LoginResponse = new Packet("LOGIN_RESPONSE"); LoginResponse.AddBoolean(true);//It means successful login //Add a int value to Packet. It provides client the connection ID for future use LoginResponse.AddInt32(connID); //Announce to all clients a new user joined //Set sendToAll parameter to true (default false) if you want to send Packet //to all clients Packet UserJoin = new Packet("USER_JOIN", true); //Add the name of the Chat User joined UserJoin.AddString(username.ToString()); //Add Packets to MethodResponse MethodResponse.AddPacket(LoginResponse); MethodResponse.AddPacket(UserJoin); Users.Add(new User(connID, username.ToString())); //Add the Chat User to a List //Write on server console from dll Logging.WriteLine("User: " + username + " has joined the chat", LogLevel.Information); } return MethodResponse; //Return MethodResponse to Server }
TestDLL را کامپایل کنید.
به TestDLL.dll, MysqlConnector.dll, MySql.Data.dll در دایرکتوری سرور بروید.
چگونه پروژه را تست کنیم؟
FlexibleServer.exe واقع شده در /Flexible Server/bin/Debug را به عنوان مدیر(Admin) باز کنید.
یک یا بیشتر ChatClient.exe قرار گرفته در /Chat Client/bin/Debug را نیز باز کنید.
یک نام کاربری وارد کنید.
کلمه عبور به صورت "password" است.
برای ارسال یک whisper عبارت " whisper target message" را بنویسد (تارگت را با یک نام کاربر که می خواهید به آن پیام ارسال کنید جایگزین کنید).
ایجاد DLL
یک پروژه DLL جدید در Visual Studio ایجاد کنید.
رفرنس را به System.ServiceModel اضافه کنید
برای نوشتن روی کنسول با استفاده از LogLevel ، رفرنس Logging.dll را اضافه کنید.
(اختیاری) اگر می خواهید پشتیبانی Mysql را استفاده کنیدرفرنس MysqlConnector.dll و MySql.Data.dll را اضافه کنید.
کد پایه را بنویسید.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; //using MysqlConnector; //PacketHandler class must be public to let server reads methods public class PacketHandler { /// <summary> /// Initialize variables/mysql connection etc.. /// </summary> public PacketHandler() { } public MethodResponse Yourincomingpacketname(string incomingpacketparameter, etc) { MethodResponse MethodResponse = new MethodResponse(); ... Your code .... return MethodResponse(); } /// <summary> /// Must always be declared. it will be called when a client disconnect /// <param name="connID">Connection ID provided by server</param> /// </summary> public void OnClientDisconnect(int connID) { } /// <summary> /// Must always be declared. it will be called when a client connect /// <param name="connID">Connection ID provided by server</param> /// </summary> public void OnClientConnect(int connID) { } }
DLL را کامپایل کنید.
به DLL رفته و آن را به دایرکتوری سرور رفرنس دهید.
مقدار packetHandlerDLL را داخل flexibleserver-config.conf با نام DLL خود تغییر دهید.
نکات مورد توجه
فایل پیکربندی(Configuration file)
این فایل شامل تمام تنظیمات سرور می باشد.
## Flexible Server Configuration File ## Must be edited for the server to work ## Server Configuration MinimumLogLevel=0 tcp.bindip=127.0.0.1 tcp.port=8888 packetHandlerDLL=TestDLL.dll enableWebService=1 webservice.bindip=127.0.0.1 webservice.port=8000 webservice.username=admin webservice.password=password
MethodResponse
این کلاس به سرور و DLL اجازه می دهد که با یکدیگر ارتباط برقرا ر کنند.
using System; using System.Collections.Generic; using System.Linq; using System.Text; public class Packet { public string Header; //Name of the Packet public List<object> bodyValues; //List of Values public bool sendToAll; //Send to all Clients? //List of Connection ID to which the server will send the Packet public List<int> sendTo; //Delimit Packet header and Packet body values using char(1) private char Delimiter = (char)1; /// <summary> /// New Packet</summary> /// <param name="Header">The Packet header/name</param> /// <param name="sendToAll">Send to all Clients?</param> /// <param name="sendTo">List of Connection ID to which the server will send the Packet</param> /// </summary> public Packet(string Header, bool sendToAll = false, List<int> sendTo = null) { this.Header = Header; this.bodyValues = new List<object>(); this.sendToAll = sendToAll; this.sendTo = sendTo; } /// <summary> /// Add integer value to Packet body</summary> /// <param name="Value">Integer value</param> /// </summary> public void AddInt32(int Value) //Add a integer to Packet body { bodyValues.Add(Value); } /// <summary> /// Add string value to Packet body</summary> /// <param name="Value">String value</param> /// </summary> public void AddString(string Value) //Add a string to Packet body { bodyValues.Add(Value); } /// <summary> /// Add boolean value to Packet body</summary> /// <param name="Value">Boolean value</param> /// </summary> public void AddBoolean(bool Value) //Add a boolean to Packet body { bodyValues.Add(Value); } /// <summary> /// Return the final string value to be sent to client /// </summary> public string GetPacketString() { string PacketString = Header; foreach (object o in bodyValues) { //Add delimiter to each Packet body value PacketString += Delimiter.ToString() + o.ToString(); } return PacketString; } } public class MethodResponse { public List<Packet> Packets; //List of Packets public MethodResponse() { Packets = new List<Packet>(); } /// <summary> /// Add new Packet to MethodResponse</summary> /// <param name="Packet">Packet</param> public void AddPacket(Packet Packet) { Packets.Add(Packet); } }
- C#.net
- 2k بازدید
- 1 تشکر