Personal C Sharp                                                         By  famsoft.org
HomeHow To StartExamples-DesktopExamples-WebPC# MethodsReference-DesktopReference-Web


EXAMPLES ON NETWORKING ====================== Networking is a very wide field. When we think about network programming for personal use, we think about very exciting projects. Accessing the internet to get the latest stock prices and automatically update your files and calculate the total value of your investment, all as one program is one exciting project. Accessing your personal web site using the FTP protocol to download files, update them then upload them back automatically without manual operations is also an exciting project. Writing a program which checks your e-mail, file saves the useful items and deletes the rest is also nice to do. Writing a program which sends e-mail to your friends automatically when some event takes place can also be a great project. So, let us see how we can do some of these exciting programming projects. ========================================================================================= EXAMPLE 1: We'll write a program which we can run at any time to get the latest stock price of Microsoft from CNN's Financial website. ========================================================================================= // ----------------------- FIRST STEP: Getting the html file --------------------------- To get the HTML file, you need to know the URL of the page which tells us the stock price. So access CNN's Financial website and get the stock price manually. The URL you need is the one displayed at the address line of your browser which is "http://money.cnn.com/quote/quote.html?symb=MSFT"; ========================================================================================== public class a:pcs { public override void init() { tia=toa="t"; // Select "text screen" display base.init(); // Initialize PC# class } public override void run() { urs="http://money.cnn.com/quote/quote.html?symb=MSFT"; jb=true;kb=true;nm("hg");tm(); //Get html file replacing {}<> with |'s } } ========================================================================================= TUTORIAL: The address at CNN's financial web site for quotes is only: "http://money.cnn.com/quote/quote.html" The rest of the URL is the query string. It starts with '?' followed with a number of "parameter=value" strings seperated with '&' char's. We can't tell for sure what CNN means by their parameters, but we can guess that "symb=MSFT" means Symbol=Microsoft's stock symbol. Calling the Networking method nm() at mode "hg" gets the html file. The html file is huge in size, so we must think of eliminating some of its unnecessary text in order to reach where Microsoft's stock price is fast. We know that HTML is full of tags enclosed in brackets like these < > which we have no need for. Also there are plenty of script data between braces { } which we don't need. We can eliminate both types if we supply the method with (jb=true;kb=true;) Instead of just removing them, the method replaces each pair of them and the data in between with one '|' since this may help us when we search for the necessary data. After this elimination, the file we get may have lost 2/3 of its size, so we can at least look through it to know where the wanted data is. Here is what we see: =========================================================================================


// ------------------------- SECOND STEP: Searching the text --------------------------- The "PC# Reference, Desktop" discusses how to search a string in details. Each search job requires a unique strategy. The wanted data is the "63.24" Look at the text around it: Microsoft Corp |(NASDAQ:MSFT)|||||Add to Watch List||||Set Alert|||||||||63.24|| Delayed Data|As of The word "MSFT" which is in upper cased letters and surrounded with a ':' and a ')' is unique in the file, so we can use it for the search. We can get the string which starts immediately after ":MSFT)" and ends immediately before the phrase "As of" by calling the search method as follows: js=":MSFT)";ks="As of";tm("s"); The resulting string comes assigned to (os), so: os="|||||Add to Watch List||||Set Alert|||||||||63.24||Delayed Data|". How can we extract the stock price from this string? Notice the following: (1) If we scan the string backward, the first digit we meet is the last char of the wanted data. (2) The wanted data is made of numbers, one decimal point and may contain commas. The char which immediately precedes wanted data could not be any of those characters. Using those two features we can extract the stock price. Here is the complete program to get the data, search it and display the stock price: ========================================================================================== public class a:pcs { public override void init() { j=1000;k=550;dm("s"); tia=toa="t"; // Select "text screen" display base.init(); // Initialize PC# class } public override void run() { //------ Get the page ------ urs="http://money.cnn.com/quote/quote.html?symb=MSFT"; jb=true;kb=true;nm("hg");//tm(); //Get html file replacing {}<> with |'s //------ Pick data around the price ------ txs=os; // Assign file to search string (txs) js=":MSFT)";ks="As of";tm("s"); // Get the string between js & ks and assign it to (os) //------ Remove chars following the price ------ for(c=os.Length-1;c>-1;c--) { // Scan (os) char's backward. Stop at first numeric char if("0123456789".IndexOf(os.Substring(c,1))>-1) break; } os=os.Substring(0,c+1); // Remove all chars after that one from (os) //------ Remove chars preceding the price ------ for(c=os.Length-1;c>-1;c--) { // Scan (os) char's again. Stop at first char which if("0123456789.,".IndexOf(os.Substring(c,1))<0) break; } // is not a digit, decimal point or comma. os=os.Substring(c+1); // Remove all char's upto that one from (os) os="Latest stock price for Microsoft: $"+os;tm(); } // Now (os) contains nothing but the price. Display it. } ====================================================================================


========================================================================================= ACCESSING FTP SITES =================== How good are our FTP access methods? ==================================== Our FTP access methods are faster and more reliable than many of the commercial FTP access softwares available. This is one reason for you to write your own software to upload your files and directories to your FTP site by yourself. The major advantage in writing your own FTP software is the elimination of manual operations which are subject to human errors. Your ftp site address, your username, your password and the pathnames of all the files to be uploaded can be stated into your software instead of being asked for by the software each time you run it. This should make your upload operation always accurate and error free. Let us have an example. ========================================================================================= EXAMPLE 2: In this example, you will access your web site using FTP protocol and do the following in order: (1) Create new direcory named "dir1". (2) Upload the local file "test.txt" from your working directory to the new directory at your website. Before you start, you need to create the "test.txt" file at your working directory and write some text into it using Notepad. (3) Download the same file from the server back to your working directory using the new name "test1.txt" for the received file. (4) Delete the file "test.txt" at your website. (5) Delete the directory "dir1" at your website. So at the end your website will be restored to original condition and your working directory will contain the two files "test.txt" and "test1.txt" containing the same data. ========================================================================================= We'll display a dialog box before the execution of each operation to give you the choice to either perform it or skip it. This will also allow you the time to access your website manually to cofirm that the previous operation has been executed properly. You need to modify some lines, replacing "Your user ID", "Your Password" and "Your server" with your actual data. The lines which need modifications are marked with "***". ========================================================================================= public class a : pcs { // Always remember, class name = file name public override void init() { tia=toa="t"; // Use Text Screen for text display base.init(); // Should be last statement in init() } public override void run() { ids="your user ID";pss="Your Password";// *** You need to modify this line so it // contains your actual user ID & Password. // --------------------------------- CREATING DIRECTORY --------------------------------- cls="r0";os="CREATING NEW DIRECTORY:";tm(); // Display in red on Text Screen cls="S9";os="Create new directory (dr1)?";ks="yn";cm("d"); // Display choice dialog with "yes/no" buttons if(os.IndexOf("y")>-1) { // If user clicked on "Yes" urs="ftp://your host.com/dir1";nm("fmd");// *** You need to modify this line so it // contains the actual name of your host tm(); // Display the FTP status returned. } // ----------------------------------- UPLOADING A FILE ---------------------------------- cls="r0";os="\nUPLOADING A FILE:";tm(); cls="S9";os="Click OK to upload file (test.txt)";ks="yn";cm("d"); if(os.IndexOf("y")>-1) { fls="test.txt"; urs="ftp://your server.com/dir1/test.txt";// *** Modify nm("fpf"); tm(); } // ---------------------------------- DOWNLOADING A FILE ---------------------------------- cls="r0";os="\nDOWNLOADING A FILE:";tm(); cls="S9";os="Click OK to download file (test.txt)";ks="yn";cm("d"); if(os.IndexOf("y")>-1) { urs="ftp://your server.com/dir1/test.txt";// *** Modify fls="test1.txt"; nm("fgf"); tm(); } // ---------------------------------- DELETING THE FILE ----------------------------------- cls="r0";os="\nDELETING THE FILE:";tm(); cls="S9";os="Click OK to delete the file (test.txt)";ks="yn";cm("d"); if(os.IndexOf("y")>-1) { urs="ftp://your server.com/dir1/test.txt";// *** Modify nm("fdf"); tm(); } // ------------------------------- DELETING THE DIRECTORY --------------------------------- cls="r0";os="\nDELETING DIRECTORY:";tm(); cls="S9";os="Click OK to delete new directory (dr1)";ks="yn";cm("d"); if(os.IndexOf("y")>-1) { urs="ftp://your server.com/dir1"; // *** Modify nm("fdd"); tm(); } } } ========================================================================================= TUTORIAL: --------- The ftp address always starts with "ftp://" since this part represents the protocol. Additionally, some ftp host names also start with "ftp" like "ftp://ftp.microsoft.com. Authentication is a must for ftp servers. The ones which accept anonymous caller expect you to use the word "anonymous" as your user ID and your e-mail address as your password. There is nothing else to explain in this section, we hope you have had no problem with this example. Giving your FTP program administrator privilege: ------------------------------------------------ If you are using Windows 10, your FTP program may not be allowed to save files unless it has the administrator privilege. So, you need to do to your ".exe" file what you have done to Notepad to correct the same problem. Read "Reference-Desktop" under the title "GETTING THE MESSAGE 'ACCESS IS DENIED' WHEN ATTEMPTING TO SAVE A FILE". =========================================================================================


========================================================================================= SENDING AND RECEIVING E-MAIL ============================ ========================================================================================= EXAMPLE 3: We are going to send an e-mail with an attachment file programatically. If your outgoing mail server requires authentication, you need to supply your User ID and Password assigned to (ids,pss) respectively. If it does not, supply (ids=pss="";) You need to supply the e-mail address of both sender and recipient. If you decide to send the message to yourself, use your e-mail address for both. We are going to be using "pix.jpg" as the attachment file. Make sure it is available at your working directory. It will be considered your personal picture which you send as a gift to your friend. The lines which require your modifications are marked with "***". ========================================================================================= public class a:pcs { public override void init() { tia=toa="t"; base.init(); } public override void run() { gm("dn"); // Stop display to avoid flickering while // message is sent uhs="Your SMTP Server Name.com"; // *** Replace with your SMTP server address. ids="Your User ID";pss="Your Password";// *** Replace with actual ID, Password. js="YourID@YourHost.com"; // *** Sender E-Mail address ks="HerID@HerHost.com"; // *** Recipient E-Mail address os="My gift to you."; // Message Subject OS[0]="Dear Friend"; // Message Body made of 2 lines. OS[1]="My picture to you with love!"; fls="pix.jpg"; // File to be attached ib=true;nm("ms"); // Send the message with attachment gm("dn"); // Restore display os="Message Sent.";tm(); // Inform user } } ========================================================================================= Sending e-mail messages in full color: -------------------------------------- The body of the message you send does not have to be in "plain text", it can contain HTML. This allows you to send messages in different colors and fonts and to include pictures within them. When you get to the "Demonstrative Examples - Web Applications", you'll learn how to modify example 3 so that the body's two lines show in different colors and the picture becomes part of the message body. =========================================================================================


EXAMPLE 4: This example will show you how to obtain a list of all new messages which your incoming mail server has received for you, how to retrieve any message on the list and how to delete a message. Before we get to the example, you need to know what the "Outlook Express" or similar mail utilities do when they receive your mail. They obtain the list of new messages from the server, retrieve a copy of each message, store it locally then instruct the server to delete the message there. They do this operation each specific number of minutes while the computer is on depending on your settings. The messages which you see when you check the inbox of the "Outlook Express" have already been deleted at the server. So don't expect to find them when you run your program to check for new messages. Your program can only find the few messages which have been received at the server and the "Outlook Express" has not received them yet. So, you may like to temporarely disable the Outlook Express's automatic message retrieval feature in order to give your program a chance to find enough new messages. To do so, run the "Outlook Express", click on [Tools] then [Options]. At the "General" page of the "Options" window, uncheck the Checkbox labled "Check for new messages every.." then click on [Apply], [OK]. You may also send yourself some messages to add to the list of messages which your program is going to find. The lines which require your modifications are marked with "***". ========================================================================================= public class a:pcs { public override void init() { tia=toa="t"; bli=0; base.init(); } public override void run() { if (blp==0) { // Obtaining the list cls="r0";os="GETTING NEW MESSAGES LIST:";tm(); uhs="Your POP Server Name.com";ids="Your User ID";pss="Your Password";// *** // *** Replace with your actual data. nm("mc");cls="S9";tm(); // Get new msg's list and display cls="b0";os="Enter the number of the message which you like to retrieve."; bli=1;tm("i");return; // Get info from user then goto blk 1 } if (blp==1) { // Retrieving a message om("ti");n=o; // Convert msg no. to int, assign to (n) cls="r0";os="RETRIEVING A MESSAGE:";tm(); i=n;nm("mr"); // Retrieve message cls="S9";tm(); // Display message cls="b0";os="Enter the number of the message which you like to delete."; bli=2;tm("i");return; // Get info from user then goto blk 2 } if (blp==2) { // Deleting a message om("ti");n=o; // Convert msg no. to int, assign to (n) cls="r0";os="DELETING A MESSAGE:";tm(); i=n;nm("md"); // Delete message cls="S9";tm(); // Display server cofirmation msg. cls="b0";os="Enter 'r' to repeat program or 'e' to exit."; bli=3;tm("i");return; // Get info from user then goto blk 3 } if (blp==3) { // Executing what to do next om("l"); // Convert user's choice to lower case if (os.Equals("e")) sm("e"); // Exit prog if "e" was selected else bli=0;um("b");return; // Jump to block 0 if "r" was selected. } } } ========================================================================================= TUTORIAL: The new message list which you see on display shows the message number followed with a number indicating the message size. The message number which starts by "1" is the number to use to tell the server which message you like to retrieve or delete. The messages which you receive from the mail server contain very large amount of information which you don't need. So you need to search the data and obtain the data which you need. =========================================================================================


EXAMPLE 5: So far, all the networking examples which you have seen, did not show the actual communication between client and server. This example will get deeper and show the actual dialogue which happens when we call the incoming mail server and ask for new messages list. The lines which require your modifications are marked with "***". ========================================================================================= public class a:pcs { public override void init() { tia=toa="t"; bli=0; base.init(); } public override void run() { if (blp==0) { cls="r0";fns="trb14";tm("c"); // Set font,clor, clear screen. os=" COMMUNICATING WITH INCOMING MAIL SERVER\n";tm(); fns="trb12"; uhs="Your POP Server Name.com";ids="Your User ID";pss="Your Password";// *** // *** Replace with actual data cls="b0";os="CLIENT: Connecting ...";tm(); // Announce next action upi=110;nm("to"); // Open connection nm("tr"); // Read server response cls="S9";os="SERVER: "+os;tm(); // Display server response cls="b0";os="CLIENT: user 'Your User ID'";tm(); // Announce next action os="user "+ids+'\n';nm("tw"); // Send "user" command nm("tr"); // Read server response cls="S9";os="SERVER: "+os;tm(); // Display server response cls="b0";os="CLIENT: pass 'Your Password'";tm();// Announce next action os="pass "+pss+'\n';nm("tw"); // Send "Pass" Command nm("tr"); // Read server response cls="S9";os="SERVER: "+os;tm(); // Display server response cls="b0";os="CLIENT: list";tm(); // Announce next action os="list"+'\n';nm("tw"); // Send "list" conmmand nm("tr"); // Read server response cls="S9";os="SERVER: "+os;tm(); // Display server response cls="b0";os="CLIENT: quit";tm(); // Announce next action os="quit"+'\n';nm("tw"); // Send "quit" command nm("tc"); // Close connection } } } ========================================================================================= You may be wondering why the variable we use to represent a host (uhs) starts with "u". The reason is that it's a part of the URL. Here are all var's related to (URL): ups: String which represents the Protocol. uhs: String which represents the Host name. ufs: String which represents a file included in the URL. upi: Integer which represents the Port number. urs: String which represents the entire URL.


=============================================================================================== Resolving IP-Addresses: ======================= We all know that an IP-Address can be expressed in a numeric format like "207.46.232.182" and in a friendly string format like "microsoft.com". Method nm() at mode "r" can resolve an IP Address from the string format to the numeric one and vice versa. It expects the address to be resolved to be assigned to (urs) and it also sends the result assigned to (urs) One problem is that a server could have one string address which resolves to more than one numeric address. For this reason, when you resolve string format to numeric one, the method returns two items, OS[] containing all results and (urs) containing the first numeric address in OS[] EXAMPLES: --------- (1) urs="google.com";nm("r");os=urs;tm(); displays: 72.14.207.99 (2) urs="google.com";nm("r");os=OS[0];tm(); displays: 72.14.207.99 (3) urs="google.com";nm("r");os=OS[1];tm(); displays: 64.233.187.99 (4) urs="google.com";nm("r");os=OS[2];tm(); displays: 64.233.167.99 (5) urs="72.14.207.99";nm("r");os=urs;tm(); displays: eh-in-f99.google.com (6) urs="64.233.187.99";nm("r");os=urs;tm(); displays: jc-in-f99.google.com (7) urs="64.233.167.99";nm("r");os=urs;tm(); displays: py-in-f99.google.com Notice that if you use your browser to retrieve the web pages whose addresses are displayed in (5):(7), you'll always get the same "www.google.com" page. If you are wondering where the DNS (Domain Naming Service) database which we are accessing came from, it came with the ".NET framework" software which you have downloaded from Microsoft. =============================================================================================== DEVELOPING SERVERS ================== For version 1.6 or higher We know it has been fun to communicate with the mail server, except that we have had only half of the fun. The people who developed the server had the other half. Can we write our own server software to have it all? Yes we can. DEVELOPING A TCP SERVER: ======================== We are going to start with the TCP server. The TCP server works at the transport layer and this means that it is protocol dependant. If both server and client send data at the same time, communication will break down. So a protocol must be set and both server and client must abide with it. The server communication software is also as crtical as the protocol. It requires extra care. For this reason we are taking a different approach with the server than we have taken with the client. The TCP server runs in one piece. You start it using nm("tss") At the mean time you prepare a method which tells the server how to answer the messages received and instructs it to disconnect a call or to stop whenever necessary. For the more advanced, the three main objects which the server operates on are made public, so you can also work on them if you find it necessary. The three objects are: tsp: Present TcpListener object; tcp: Present TcpClient object. nsp: Present NetworkStream object. If all I have is one personal computer, can I use it to test server-client communication? ----------------------------------------------------------------------------------------- Yes. You can have both server and client software on the same computer. The host name you will be connecting to in this case is "localhost". You'll see how to do this in the following examples. Starting the server: -------------------- You start the server by calling nm("tss") supplying it with the following parameters: urs: Local IP Address to listen to. A server can have more than one IP Address if it has more than one network card. The address supplied must be in dotted numeric form like (urs="196.50.25.1") You can resolve an address in a string format to numeric format by calling nm("r") If you keep (urs="") Server will listen to all IP Addresses available. upi: Port number to listen at. If you like to select one which is guaranteed not to interfer with anybody else's, select one in the range (50,000:60,000) i : Number of the method which you have prepared that contains all the necessary logic. For example if you have used method m5(int t), make the assignment (i=5) These methods have been discussed in the chapter of "Handling Threads". k : Receive Timeout. Should be set unless you accept default. Timeout is important since this server runs synchronously, It cannot serve next call unless current call has been terminated. Deciding upon a Protocol: ------------------------- You are the one who writes the code which analyzes client messages, so make sure the protocol allows you to get what client wants easily and error free. Make it simple for the client too. Your code must correct as many client errors as possible and must inform him/her of the error if correction was not possible. It will be nice if you allow clients to ask for help by sending the message "Help", "?" or "Commands" which can get them instruction for what to do. Making the logic method: ------------------------ As explained above, you should use one of the 10 methods "m0() to m9()" which have been made originally for threads. The methods expect a thread number which is unnecessary for this application, but it must be there. So if you have decided to use method m5(), your code should look like this: public override void m5(int t) { ......... } and the server will be calling m5(0) The server will be calling your method each time its status changes and each time it receives a message. It will supply your method with two items: 1. (os) containing the data regardless if it was a message or a new status code. 2. (ib) The status flag. When you find that (ib) is set, you know that the data in (os) is a status code. If reset, the data is a received message. The status codes can be "l", "c", "s" or "e" meaning "Listening", "Connected", "Stopped" or "Ended with an error". =============================================================================================== EXAMPLE 6: Write two classes, a TCP client class named "tc" which will be similar to the one of Example 5 and a TCP server class named "ts" which will start the PC# internal TCP sever. The second class should contain a logic method as explained above. The server will be called "Math server" it will be serving answers to trigonometric functions questions. The client will be sending commands like "Function:cos,Operand:45" to expect the answer for (cos 45). The protocol also includes sending one's name to the server with "Name:AnyName". The command "Bye" will be used for logging off. =============================================================================================== THE SERVER CLASS: ----------------- public class ts:pcs { //------------------------------------ Starting the Server ----------------------------------- public override void run() { cm("fe"); // Eliminate form. cls="r0";dm("ccf"); // Change Console color to red os="To Stop Server Press [Control][Break]";tm(); cls="S9";dm("ccf"); // Display message. Restore black color. upi=55000;urs="";i=9;k=1000;nm("tss"); // Stsrt TCP server using port 55000. Listen to all } // available IP addresses. use m9(int t) for logic // and set Receive Timout to 1000 ms. //--------------------- Method m9() (Contains logic to be used by server) -------------------- public override void m9(int t) { // IN:os=Message received or status code ib=Status flag ib=true:status code in (os), // ib=false:message received in (os) Status code can be: l/c/s/e meaning Listening/ // Connected/Stopped/Ended with Error. // OUT:For status change calls os="" must be returned except for code "c", you may assign // a message in (os) to inform client that a connection has been established. // For message received calls, you assign to (os) a reply message to be sent to client. // Returning (ob=true) instructs server to disconnect client. // Returning (dnb=true) instructs server to stop. // PROTOCOL: (1) Server informs client when connection has been successfully made. // (2) Client may send his/her ID's with [Name:his/her_name] and expect reply. // (3) Client may send trigonometric function question as [Function:fn,Operand:op] // where fn=Any trigonometric function name and op=Angle in degrees (0:360) // and expect one message reply. // (4) Client can close connection by sending [Bye] and expect no reply. // (5) When client sends invalid command, server sends back one informing message. // REMARK: (n) will be used to count connections. try { string fn="",op="";bool er=false; // Define var's for function, operand & errors string o1s=os; // Store (os) temporarely //---------------------------------- Status change calls -------------------------------- if (ib) { // If this was a status change call: if (o1s=="c") { // If status=Connected: n++; // Increment connection counter. os="Server Connected... Calls received so far: "+n;tm(); // Display a message on local console. os="Math Server Ready."; // Return this message to be sent to client. } else if (o1s=="l") { // If status=Listening: os="Ready for calls.... Calls received so far: "+n;tm(); // Display a message on local console. os=""; // You must return nothing into (os) } else if (o1s=="s") { // If status=Stopped: os="Server Stopped..... Calls received so far: "+n;tm(); // Display a message on local console. os=""; // You must return nothing into (os) } else if (o1s=="e") { // If status=Ended with error: os="Error encountered.. Calls received so far: "+n;tm(); // Display a message on local console. os=""; // You must return nothing into (os) } return; // Return back to server. } //--------------------------------- "Message received" calls ------------------------------ //--- Correcting client's errors--- Any "=" will be replaced with the expected ":" and // any ";" will be replaced eith the expected ",". All spaces will be removed and the // string will be converted to all lower cased char's before analysing commands. os=""; // Start with empty (os) char[] C=o1s.ToCharArray(); // Copy (o1s)'s chars to array rows for (int i=0;i< o1s.Length;i++) { // Scan (o1s)'s char's t=(int)C[i]; // t=ASCII code of each char if (t<33) continue; // Skip spaces and all lower char's if (C[i]=='=') C[i]=':'; // Change any '=' char with ':' if (C[i]==';') C[i]=','; // Change any ';' char with ',' os+=C[i]; // Add char to (os) } om("l"); // Change all char's to lower case. o1s=os; // Reassign string to (o1s) //--- The "Bye" command --- if (o1s.IndexOf("bye")>-1) { // If message received was "Bye" ob=true;return; // Send instruction to disconnect client. } //--- Analysing first term (left of ",") --- The "Name" command will be done here. c=o1s.IndexOf(","); // c=position of "," if (c>-1) { // If "," was available: fn=o1s.Substring(0,c); // Assign text on its left side to (fn) op=o1s.Substring(c+1); // and text on its right side to (op) } else fn=o1s; // If "," was not available fn=full string if (fn.Length<1) er=true; // if (fn) was smaller than expected, quote error else { // Else if length of (fn) was OK: c=fn.IndexOf(":"); // c=Position of ":" in (fn) if (c>-1) { // If ":" found: os=fn.Substring(c+1);ns=os; // os=Text at its right side, save it temporarely if (fn.IndexOf("name")>-1) { // If was the "Name" command, Make a welcome os="Welcome to the Math Server "+os;return; } // message to caller and return it to server else fn=ns; // Reduce (fn) to text on right side of ":" only } // which is the name of the function wanted. else er=true; // Else if no ":" found, quote error. } //--- Analysing second term (right of ",") --- The "Math question" will be done here. if (op.Length<9 || op.IndexOf("operand")<0) er=true; // If 2nd term was too short or no "Operand" found, else { // quote error. Else: c=op.IndexOf(":"); // c=Position of ":" in (op) if (c>-1) { // If ":" was available: os=op.Substring(c+1);op=os; // Reduce (op) to text on right side of ":" only om("td");js=fn;um("mt"); // Convert (os) to double. Call um() to get the os=fn+" "+op+" = "+od;return; // answer (comes in od). Assign reply to (os) } // and send it back to the server. else er=true; // Else if no ":" found, quote error. } if (er) { // If any error was found: os="Invalid command.";return; // Return this reply message to server. } } catch { os="Error has been detected while processing your command."; return; } } } THE CLIENT CLASS: ----------------- public class tc:pcs { public override void init() { tia=toa="t"; base.init(); } public override void run() { cls="r0";fns="trb14";tm("c"); // Set font,color, clear screen. os=" COMMUNICATING WITH OUR MATH SERVER\n";tm(); fns="trb12"; // Display title, Change font cls="b0";os="CLIENT: Connecting ...";tm(); // Announce next action uhs="localhost";upi=55000;nm("to"); // Open connection with local server listening // at port 55000 nm("tr"); // Read server response cls="S9";os="SERVER: "+os;tm(); // Display server response cls="b0";os="CLIENT: Name: John";tm(); // Announce next action os="Name: John";nm("tw"); // Send "Name" command nm("tr"); // Read server response cls="S9";os="SERVER: "+os;tm(); // Display server response cls="b0";os="CLIENT: Function:sin, Operand:30";tm(); // Announce next action os="Function:sin, Operand:30\n";nm("tw"); // Send "math inquiry" Command nm("tr"); // Read server response cls="S9";os="SERVER: "+os;tm(); // Display server response cls="b0";os="CLIENT: Function:tan, Operand:45";tm(); // Announce next action os="Function:tan, Operand:45\n";nm("tw"); // Send another "math inquiry" Command nm("tr"); // Read server response cls="S9";os="SERVER: "+os;tm(); // Display server response cls="b0";os="CLIENT: Bye";tm(); // Announce next action os="Bye\n";nm("tw"); // Send "Bye" command nm("tc"); // Close connection } } =============================================================================================== Save the server class into file "ts.cs" and the client class into file "tc.cs". Compile the two classes with: pcp ts and pcp tc To run the two classes on the same computer, open two Command Mode windows. one for the server and one for the client. Both should point to your working directory if the ".exe" files of both the server and client were there. Start the server first by: ts [ENTER] then move to the second window and start the client by: tc [ENTER]. RESULTS: This is what you see on server's console: C:\pcs>ts To Stop Server Press [Control][Break] Ready for calls.... Calls received so far: 0 Server Connected... Calls received so far: 1 Ready for calls.... Calls received so far: 1 Server Connected... Calls received so far: 2 Ready for calls.... Calls received so far: 2 Server Connected... Calls received so far: 3 Ready for calls.... Calls received so far: 3 And this is what you see at the client:


=============================================================================================== DEVELOPING A MULTI-THREAD TCP SERVER: ===================================== The single-thread server which we have worked with in example 6 is fast and reliable. It allowed you to include your logic method into your server class which extends (pcs) This has made things easy for you. All necessary objects have been accessable to you which was also an advantage. However, one major problem with the single-thread server is that it can help one client at a time. If one client did his job slowly, all the clients who come after him suffer. We can cut the slow callers off by setting the timeout at a low value, but this will not be the solution which makes everyone happy. So, the solution is multi-thread server which helps more than one client simultaneously, so the slow client cannot affect the rest. We have developed a multi-thread server which is reliable, can handle any protocol and runs at great speed. Interfacing with it is not the same as with the single-thread server. Let us see how to start it first. The Personal C Sharp Utilities class (pcsut): --------------------------------------------- Initially, the servers have been included into class (pcs) Now, class (pcs) is signed and a signed class is not allowed to reference an unsigned one. The servers need to reference your Logic classes, so to solve this problem we have moved the server to the utilities class which is unsigned. The utilities class (pcsut) extends class (pcs) Your server class extends (pcsut), this allows it to access all (pcs) methods. Any class which references class (pcsut) or any inner class which it contains must include this commented statement above the class declaration statement: //assembly pcsut.exe; The PC# tools which you compile your class with, reads this commented statement and learns that class (pcsut) can be found into file "pcsut.exe" which is located into current directory. Make sure to leave no space between the "//" and the letter "a". Starting the server: -------------------- You start the server by calling nm("tsm") supplying it with the following parameters: REMARK: The only necessary par's are (urs,upi), there are defaults for all others. urs: Local IP Address to listen to. A server can have more than one IP Address if it has more than one network card. The address supplied must be in dotted numeric form like (urs="196.50.25.1") You can resolve an address in a string format to numeric format by calling nm("r") If you keep (urs="") Server will listen to all IP Addresses available. upi: Port number to listen at. If you like to select one which is guaranteed not to interfer with anybody else's, select one in the range (50,000:60,000) k : Receive Timeout. Should be set unless you accept default. jf,kf: Min number of Worker Threads and completion Port Threads respectively. Default (1,1) lf,of: Max number of Worker Threads and completion Port Threads respectively. Default (25,1000) Making the logic method: ------------------------ In order to allow the server to run at the maximum possible speed, we had to change the way you supply your logic method to the server. The logic method is not included into the server class; instead it's part of another class named "TLogic". Class "TLogic" implements an interface with the name "ITLogic" which is included internally into class (pcsut) So, the interface's name is "pcsut.ITLogic". TLogic handles logic for TCP servers. Later you'll learn about class HLogic which is a similar class that handles logic for HTTP servers. The logic method name is "ServerLogic". It expects three parameters, a string, an integer and an object. The string supplies the data which can be a message received from the client or a status code. The integer is the "Connection counter". The counter, this time is incremented by the server in a thread safe manner and supplied to your logic method as a parameter. The object is of class "RxTx" which is included into class (pcsut) internally. It handles the reception and transmission of data for the TCP servers. This object is necessary only for the advanced programmers who like to do additional operations which are not included into PC# software. We are going to get into that later. This object will be neglected in the next example. Method "ServerLogic" returns a string value. This value can be a reply to be sent to client or an instruction to the server to disconnect current client or to stop. When the string sent by the server to the logic method is a status code, it should contain two char's, a '*' symbol followed with the status code. So it should be as follows: *l meaning "Listening". *c meaning "Connected". *s meaning "Stopped". *e meaning "Ended with error". When the string returned by the logic method to the server is an instruction code, it should also contain two char's, a '#' symbol followed with the instruction code. So it should be as follows: #d meaning "Disconnect current client". #s meaning "Stop". #n meaning "No reply to be sent". REMARK: You can return "#n" only if you have sent the reply to client by yourself. Will see how to do that in a coming example. Writing the logic code: ----------------------- When you have been writing the logic method for a single-thread server, it was done in a class which extends (pcs), and you have had no worry about threads, so you have been using PC# methods. Now, PC# methods are unavailable. You should write in C# directly. Each thread will be receiving an instance of the "Logic" class which contains your method. Class "TLogic" is compiled to a "dll" file using tool "pcl". Both the "cs" and the "dll" files must be available in your working directory all the time since class (pcsut) expects them to be there. Do not delete them. If you lose them accidentally run (pcs) again to recreate all PC# tools as you did when you started with PC#. The files will be created with the minimum required code which is as follows: public class TLogic:pcsut.ITLogic { public string ServerLogic (string os,int counter,pcsut.RxTx rt) { return os; } } You can also create the "TLogic.cs" and "TLogic.dll" by calling nm("lc") Finally, the multi-thread server will be running so fast that the messages which you display on the server's console will not be able to follow. This could make the messages meaningless. So make them as few and as short as possible. Since this class is implementing an interface which is inside class (pcsut) it must include this commented statement above its class declaration statement as explained before: //assembly pcut.exe; About the next example: ----------------------- This time, we are concerned mainly about server speed. So, we are going to simplify the protocol, reduce the displayed messages and eliminate the TextScreen. Each client will send three commands: (1) After connecting to the server, the client Will pick a random number, send it to the server as is; then display the result received (which is the square of the number) on the console. (2) The second command will be a repitition of (1) using another random number. (3) The third command will be the "Bye" command to logoff then closes connection. The client will then repeat the same procedures 100 times. =============================================================================================== EXAMPLE 7: Redo example 6 using the multi-thread server instead of the single thread one. This requires modified versions of classes (ts) and (tc) in addition to the new class (TLogic) The client will be connecting, sending a random number to the server twice and displaying the server's reply on console, then it will send the "Bye" command to logoff. The procedure will be repeated 100 times in order to check server's speed. =============================================================================================== THE SERVER CLASS: ----------------- //assembly pcsut.exe; public class ts:pcsut { // Server class extends (pcs) public override void run() { cm("fe"); // Eliminate form. cls="r0";dm("ccf"); // Change Console color to red os="To Stop Server Press [Control][Break]";tm(); cls="S9";dm("ccf"); // Display message. Restore black color. upi=55000;urs="";nm("tsm"); // Start TCP server using port 55000. Listen to all } // available addresses. } THE CLIENT CLASS: ----------------- public class tc:pcs { // Client class extends (pcs) public override void run() { for (n=0;n<100;n++) { // Repeat contacting server 100 times cm("fe"); // Eliminate form uhs="localhost";upi=55000;nm("to"); // Open connection with localhost at port 55000 i=9;um("mr"); // Get a random number in the range 0:9 os=""+o;nm("tw"); // Send the number to server nm("tr"); // Get server's reply os="Receiving: "+os;tm(); // Display it. // i=300;um("s"); // 300 ms delay (See below) i=9;um("mr"); // Get another random number in the range 0:9 os=""+o;nm("tw"); // Send the number to server nm("tr"); // Get server's reply os="Receiving: "+os;tm(); // Display it. os="Bye";nm("tw"); // Logoff. nm("tc"); // Close connection. } // Repeat. } } THE LOGIC CLASS: ---------------- //assembly pcsut.exe; public class TLogic:pcsut.ITLogic { // Logic class implements interface pcs.ITLogic //----------------- Method ServerLogic (Contains logic to be used by server) ---------------- public string ServerLogic (string os,int counter,pcsut.RxTx rt) { // IN:os=Message received or status code If call was for status change, (os) will contain: // "*l", "*c", "*s" or "*e" meaning Listening, Connected, Stopped or Ended with Error. // counter: Connection counter updated by server. // Object of class RxTx. // OUT:os=Message to be sent or instruction code. If instructions, (os) will contain: // "#d", "#s" or "#n" meaning "disconnect caller", "stop" or "Send no reply". // PROTOCOL: (1) Server does not inform client when connection has been made. // (2) Client may send any number alone and expect to receive the square of that // number. // (3) Client can close connection by sending [Bye] and expects no reply. if (os.Length<1) return os; // If received empty string return it as is string o1s=os; // Keep a copy of original (os) //----------------------------------- Status change calls --------------------------------- if (os.Length==2 && os.Substring(0,1)=="*") { os=os.Substring(1,1); // If this was a status change call get status code if (os=="c") { // If status=Connected: Console.WriteLine("Connections: "+counter); // Display a message on local console. os=""; // You must return nothing into (os) } else if (os=="l" || os=="s" || os=="e") { os=""; // Return nothing for all other 3 status codes. } return os; // Return back to server. } //-------------------------------- "Message received" calls ------------------------------- os=os.ToLower(); // Convert string to l/c //--- The "Bye" command --- if (os.IndexOf("bye")>-1) { // If message received was "Bye" os="#d";return os; // Send instruction to disconnect client. } // Else we expect (os) to contain a number. os=""+Math.Pow(Convert.ToDouble(os),2); // Get the square of the number into (os) os=o1s+"^2 = "+os; return os; // Return back to server. } } =============================================================================================== Save the server class into file "ts.cs" and the client class into file "tc.cs". Compile the two classes with: pcp ts and pcp tc Save the Logic class into file "TLogic.cs". Compile it to a library file (.dll) with: pcl TLogic To run the two classes on the same computer, open two Command Mode windows. one for the server and one for the client. Both should point to your working directory if the ".exe" files of both the server and client were there. Start the server first by: ts [ENTER] then move to the second window and start the client by: tc [ENTER]. Demonstrating the advantage in using multi-thread server: --------------------------------------------------------- If every thing worked fine, go back to the client class. Uncomment the line: // i=300;um("s"); // 300 ms delay (See below) Change the class name to "tc1" and save it into a new file named "tc1.cs". Compile the file with: pcp tc1 Now you have one server file and two client ones. Client (tc1) runs slower than client (tc) Open a third Command Mode window, run the server (ts) in the first window as you have done before. Run the slow client (tc1) into the second window and immediately go to the third window and run the faster client (tc) Notice that the fast class runs fast and the slow class runs slow. This means that they don't affect each other as they could have done if single thread server was used. Configuring the multi-thread server to run on single thread: ------------------------------------------------------------ To complete this demo, we are going to set the server for one thread operation. To do that, we set the maximum threads to be used to 1. so modify the server code as follows: Add the statement (lf=of=1;) to the line of class (ts) which makes the call nm("tsm") Here is how it should be: lf=of=1;upi=55000;urs="";nm("tsm"); // Start TCP server using port 55000. Listen to all // available addresses. Now compile the (ts) file again and repeat what you did before and notice that the two clients have become equally slow.


=============================================================================================== DEVELOPING A MULTI-THREAD HTTP SERVER: ====================================== It is now the time for the HTTP sever. HTTP servers serve files only. They use a restrict protocol which is the HTTP. They communicate with web browsers which know the HTTP protocol very well, so they don't expect the same problems which general TCP servers expect. The problem with HTTP server design is that the HTTP protocol requirements are many. Servers are required to supply the browsers with plenty of information. However, browsers normally use defaults for any missing information and also try to figure out the missing items by their own. At present time, our HTTP server which you'll be accessing in the next example, supplies three items to the browser when it receives a request: (1) The status which tells the browser why it could not fill the request when it could not. (2) The content type of the requested file, so the browser knows how to execute the file. (3) The length of the file in bytes, so the browser can tell if it has downloaded the file properly. The HTTP server we have developed requires operating systems not earlier than Windows XP SP2 or Windows Server 2003. It will not work with earlier versions. It's still simple, however it's a multi-thread server which is reliable and runs fast. The status code: ---------------- There are many of them. At present time we only use the following: " ": Send nothing. File has already been sent. "ok": OK "nf": NotFound "fr": Forbidden "ua": Unauthorized "nm": NotModified "mv": Moved "rd": Redirect "br": BadRequest The Content Type: ----------------- Content types are too many. We have included only few of them. However, whenever you leave the content type unassigned, the browser figures it out by inspecting the file extension. The content types which our HTTP server can send at present time are: " ": Unknown. Left for the browser to determine "th": text/html "tp": text/plain "tx": text/xml "tc": text/css "ax": application/xml "ig": image/gif "ip": image/png "ij": image/jpeg "vx": video/x-mng The complete IP address: ------------------------ The default port number for HTTP is (80) The complete IP address of a website should include the port number at which the HTTP server listens. For example, the complete IP address of "famsoft.org" is: http://www.famsoft.org:80 Since port 80 is the default, we don't care to include it when we request web pages. We are going to be using port 55000 for the next example, so make sure to include the port number when you test your software. Prefix URL's: ------------- The new versions of windows allow more than one program to be accessing the same port at the same time using HTTP. In order to prevent interference, each program should request serving files from one or more specific folders or to serve requests coming to one or more specific ports. No other program should request serving any of those folders or ports. The requested folders and ports are called Prefixes. When you start the server, you'll supply method nm("hsm") with the array URS[] loaded with all the URL prefixes which your program will like to serve. If the site the server runs on was "famsoft.org" and we like it to serve files from the subfolder "images" only using port 8080, our prefix should be: URS[0]="http://www.famsoft.org:8080/images/" The last "/" is mandatory. The prefix "http://*:8080/" will receive all requests sent to port 8080 and the prefix: "http://localhost/" will serve all requests on the localhost. Remember to include port numbers in all your prefixes unless you are using port 80. Now let's talk about your programs. Starting the server: -------------------- You start the HTTP server by calling nm("hsm") supplying it with the following parameters: URS[]: Loaded with all the prefixes which you like to serve. Port number must be included unless you are using port 80. jf,kf: Min number of Worker Threads and completion Port Threads respectively. Default (1,1) lf,of: Max number of Worker Threads and completion Port Threads respectively. Default (25,1000) Making the logic method: ------------------------ This time, the name of the logic class is "HLogic" and the name of the method which the server calls is still ServerLogic() Class HLogic emplements the interface IHLogic which is included into class (pcsut) The method receives a string, an integer and an object and returns a string. The string this method receives in its general form can be made of two strings seperated from each other with a "|". The first string is the URL which is requested by the browser in order to obtain a copy of the file which the URL represents. The second string is available only if the call was to submit a form. The second string in this case contains form data which is made of "key=value" strings seperated from each others with "&". The integer it receives is the connection counter which the server updates in a thread safe manner exactly as the TCP server does. The object is of class "HRxTx" which is included into class (pcsut) internally. It handles the reception and transmission of data for the HTTP server. This object is necessary only for the advanced programmers who like to do additional operations which are not included into PC# software. The public variables which you can access are (assuming that rt is the object's reference name): rt.OY The byte array of class RxTx. rt.hc The HttpListenerContext object reference which the current thread handles. You can use the HttpListenerContext to do any operation which PC# does not do for you. Expected return data of method ServerLogic(): --------------------------------------------- The returned string is prepared by you. It should be in the form: sc|ct|message/file where: sc : is the 2-char status code which you assign to the operation (see above) ct : is the 2-char content-type code which you provide based on the file content which you know. message/file: If the status code was "ok", you should seperate the file from the URL you received from the server, modify it if necessary to make it the actual path of the file and add it to the string returned. If the status code was "mv" or "rd", you should add the new URL here. If the status code was anything else, you may if you want add a message to be displayed at the browser (If the browser allows) The three pieces of information are seperated from each other with vertical bars '|'. REMARK: Using the HTTP server requires the file "HLogic.dll" to be available into your working directory. You can use mode "lc" to create the minimum code version of this file. Recreating Personal C Sharp tools (by running pcs) generates the file also. The Client class: ----------------- There is no client class to be made this time. The browser is going to be the client. =============================================================================================== EXAMPLE 8: Write a server class to start the HTTP server and modify the Logic class to allow serving only the images available into the "images" subfolder of your working directory. Test retrieving the images using your browser. Whenever the image "icon.pnp" is requested, redirect client to "http://www.famsoft.org". =============================================================================================== THE SERVER CLASS: ----------------- //assembly pcsut.exe; public class hs:pcsut { public override void run() { cm("fe"); // Eliminate form. cls="r0";dm("ccf"); // Change Console color to red os="To Stop Server Press [Control][Break]";tm(); cls="S9";dm("ccf"); // Display message. Restore black color. // Start TCP server using one prefix which is "serving all files on localhost" when requests // come to port 55000. URS[0]="http://localhost:55000/";nm("hsm"); } } THE LOGIC CLASS: ---------------- //assembly pcsut.exe; public class HLogic:pcsut.IHLogic { // Logic class implements interface pcs.IHLogic //----------------- Method ServerLogic (Contains logic to be used by server) ---------------- public string ServerLogic (string os,int counter,pcsut.HRxTx rt) { // IN:os=URL received from browser + Post string (if available) // counter: Connection counter updated by server. // rt: Instance of class HRxTx which is the calling class. // OUT:os=String prepared here which contains "Status code", "Content-Type code" and a file // path, a URL or a message depending on status code. if (os.Length<1) return "br"; // If received empty string return "BadRequest" //-------------------------------- "Message received" calls ------------------------------- int i;string ps=""; // Define general use int and a str. for post data i=os.IndexOf("|"); if (i>-1) { ps=os.Substring(i+1); os=os.Substring(0,i); } if (os.Substring(os.Length-1,1)=="/") os=os.Substring(0,os.Length-1); // If URL ends with "/", eliminate it. // Getting wanted file from URL received. Scan URL backward. Stop when 1st "/" is met. for (i=os.Length-1;i>-1;i--) if (os.Substring(i,1)=="/") break; if (i<0) return "br"; // If no "/" found, return status="Bad Request" os=os.Substring(i+1); // Get string following last "/" (file wanted) // Check file: if (os.Length<7) return "nf"; // If shoter than expected, return "Not Found" os=os.ToLower(); // Convert to lower case if (os=="icon.pnp") return "rd|th|http://www.famsoft.org"; // If was file "icon.pnp", redirect to another page else if ("flower.jpg icon.jpg pix.jpg icon.bmp icon.ico ".IndexOf(os)<0) return "nf"; // If not a file we like to serve, return Not Found // Make Status codes: string cts=" "; // Start with " " meaning unknown content type if (os.IndexOf(".jpg")>-1) cts="ij"; // Set content type for jpeg file // If status="OK", Get full path: os="ok|"+cts+"|c:\\pcs\\images\\"+os; // Add the rest of the file path. Console.WriteLine("Connection number: "+counter+" Sending file: "+os.Substring(6)); return os; // Send the complete string after displying it. } } =============================================================================================== REMARKS: (1) We have assumed that the path for your working directory is "c:\pcs". If it was something else, modify the line commented with "Add the rest of the file path." accordingly in class Logic's code. (2) As you can see, the person who requests the file using his browser does not have to know the exact file path on your system which is good for security. He requests it as if it was in the root folder of "http://localhost". You are the one who tells the server what the full path is. Compiling and testing the server classes: ----------------------------------------- Save the server class into file "hs.cs". Compile the class with: pcp hs Save the Logic class into file "HLogic.cs". Compile it to a library file (.dll) with: pcl HLogic Start the server by: hs [ENTER] Start the Internet Explorer and type into its address bar: http://localhost:55000/flower.jpg and push [ENTER] The flower image should show up. Hit the "Refresh button" to make sure that the image did not come from the browser's cash. Repeat the procedure to get all other images in the folder. Now, Try requesting the file "icon.pnp", it should redirect you to our website. Also try requesting any file which is unavailable in the "images" folder, you should get the "HTTP 404 Not Found" message. --------------------------------------------------------------------------------------------- This is what you see on server's console: C:\pcs>hs To Stop Server Press [Control][Break] Connection number: 1 Sending file: c:\pcs\images\pix.jpg Connection number: 2 Sending file: c:\pcs\images\icon.jpg Connection number: 3 Sending file: c:\pcs\images\icon.ico Connection number: 4 Sending file: c:\pcs\images\icon.bmp Connection number: 5 Sending file: c:\pcs\images\flower.jpg And this is what you see on the Browser:


=============================================================================================== THE WEB SCREEN: =============== Using the browser as the HTTP client is reducing our ability to demonstrate more features of the HTTp server. We must have an HTTP client which operates at a lower level than the one which we access with nm("hg") The web client requires a display device which executes and displays HTML in order to see what it's doing. The TextScreen can only display plain text. It can show the HTML without executing it which is not enough for this job. This is why we developed the WebScreen. The WebScreen is of same size as the TextScreen. The major part of it displays HTML. However, it contains one line text area to display instructions to the user in addition to the usual text field at the bottom which is available in the TextScreen too. The last two controls respond to the same commands as their TextScreen counterparts. This means: os="Enter your name:";tm(); Displays instructions to user. os="Enter your name:";bli=2;tm("i"); Displays message then receives user's text at block 2. The WebScreen will accompany our HTTP client through most of the coming examples. THE HTTP WEB CLIENT: ==================== The HTTP web client is a very capable and easy to use software. The next example will introduce you to the HTTP web client software and the WebScreen. =============================================================================================== Example 9: Use the HTTP Client software to retrieve and display Microsoft home page. =============================================================================================== public class hc:pcs { public override void init() { tia=toa="w"; // Text output device=WebScreen base.init(); // Initialize parent class. } public override void run() { urs="http://www.microsoft.com";nm("ho"); // Open HTTP connection nm("hw"); // Write request to get the page nm("hr"); // Read returned data tm("wd"); // Display received HTML data nm("hc"); // Close connection } } ===============================================================================================


=============================================================================================== The WebScreen can be set into two different modes. The first mode is the "web browser" mode in which it can get a web page and display it by itself. The second mode is "display only" mode in which it's capable only of displaying HTML. Method tm("wd") automatically sets it into the "display only" mode before it loads the HTML document into it. Method tm("wu") automatically sets it into the "web browser" mode before it supplies it with the URL of the page to be retrieved and displayed. HOW TO SUBMIT A FORM PROGRAMMATICALLY: ====================================== Form data is submitted in a similar manner to query data. The only difference is that form data is sent seperately while query data is attached to the URL. Both are made of "key=value" strings which are seperated from each others with a "&". To send the query string, add a '?' to the end of the URL then add the query string to it before you call nm("ho") When you do so you are using method "GET". To send POST data, assign it to (os) before you call nm("hwp") When you do so you are using method POST. The simplest page example which we have made was example 2 of section "WPDI". So, get into the home page and run it to familiarize yourself with it before we get into next example. IN that example we installed a text field and a button. When the user types something into the text field and clicks the button, we get his text and display it at the bottom preceded with "You entered: ". In the next example we want to do this process programmatically. We like to feel the server that someone has typed "John" into the text field and clicked the button. So we expect to see the phrase "You entered: John" at the bottom. Our form is made of two controls, a text field named "tf0" with the value "John" and a button named "bt0" with the value "Submit" (which is its label) so we expect the post data to be "tf0=John&bt0=Submit". Is this all it takes? NO. The ASP.NET uses hidden controls internally. Our form contains a third control named "__VIEWSTATE" which is a hidden field. That field must be submitted to the server with the rest of your data or your post data will be neglected. For this reason, we are going to do the job in two steps. The first step will be getting the page without sending post data and searching it to get the value of the hidden field. The second step will be to reopen the page, send post data which include the hidden field, then read and display the returned page. =============================================================================================== Example 10: Use the HTTP Client software to interact with Personal C Sharp's web page2 of WPDI. Send post data to the page which tells the page that the name "John" has been typed into the page's text field and the page's button has been clicked. =============================================================================================== public class hc:pcs { string o1s; // Define a backup string for (os) public override void init() { tia=toa="w"; // Text output device=WebScreen base.init(); // Initialize parent class. } public override void run() { //----------------------------------- First Trip ----------------------------------------- urs="http://www.famsoft.org/WPDI/pg2.aspx";ks="nc";nm("ho"); // Open connection with the page, no cache nm("hw"); // Write request to get the page nm("hr"); // Read returned data txs=os; // Assign data to searchable string js="__VIEWSTATE";tm("s"); // Remove upto the end of this string js="value=\"";ks="\"";tm("s"); // Get data between the two strings into (os) o1s="__VIEWSTATE="+os+"&tf0=John&bt0=Submit"; // Complete the POST string, assign to (o1s) nm("hc"); // Close connection //----------------------------------- Second Trip ---------------------------------------- urs="http://www.famsoft.org/WPDI/pg2.aspx";ks="nc";nm("ho"); // Open connection for the second time os=o1s;nm("hwp"); // Write post data stored into (o1s) nm("hr"); // Read returned data tm("wd"); // Display received HTML data nm("hc"); // Close connection } } ===============================================================================================


=============================================================================================== POSTING DATA OF A COMPLEX FORM: =============================== Let us now look at example 4 of WPDI. The new form contains more controls. It's made of two sections, one at the left and one at the right, Each section has its own "Submit" button, Since it's all one form, clicking either one will cause fields of both sections to be filled up with submitted data. However page4's event handler will only analyze the section whose "Submit" button is clicked and display read data at the bottom of that section. We are going to be clicking button "bt1" of the right section. Let us list the keyname of each control, its label and the data which we like to assign to it. LEFT SECTION: ------------- tf0 First Name John tf1 Last Name Doe tf2 Street Address 123 Any Street tf3 City Los Angeles tf4 State CA tf5 Zip Code 12345 ch0 Years at this residency Less than Two years bt0 Submit Not clicked RIGHT SECTION: -------------- cb00 Over 65 Unchecked cb01 Retired Checked rb00 MasterCard Checked rb01 Visa Unchecked tf6 Credit Card number 1111 2222 3333 4444 tf7 Exp Date 01/09 tf8 V Code 123 ta0 Special Instructions Any Instructions bt1 Submit Clicked The only controls which we have neglected are the "Label Controls" since they are not submitted. Here is how each control type is submitted: TEXT FIELD & TEXT AREA: keyname=The text typed into it. DROPDOWN LIST: keyname=Text of the selected row. CHECK BOX : If checked: keyname=on If unchecked: Don't list it. RADIO BUTTON : If checked: 0=keyname If unchecked: Don't list it. BUTTON : If clicked: keyname=Button's label If unclicked: Don't list it. Applying these rules, our post string should be: "tf0=John&tf1=Doe&tf2=123 Any Street&tf3=Los Angeles&tf4=CA&tf5=12345&ch0=Less than Two years& cb01=on&0=rb00&tf6=1111 2222 3333 4444&tf7=01/09&tf8=123&ta0=Any Instructions&bt1=Submit" So, all you need to do is to add the hidden field "__VIEWSTATE" and submit the form. =============================================================================================== Example 11: Use the HTTP Client software to interact with Personal C Sharp's web page4 and send post data programmatically. =============================================================================================== public class hc:pcs { string o1s; // Define a backup string for (o1s) public override void init() { tia=toa="w"; // Text output device=WebScreen base.init(); // Initialize parent class. } public override void run() { //----------------------------------- First Trip ----------------------------------------- urs="http://www.famsoft.org/WPDI/pg4.aspx";ks="nc";nm("ho"); // Open connection with the page, no cache. nm("hw"); // Write request to get the page nm("hr"); // Read returned data txs=os; // Assign data to searchable string js="__VIEWSTATE";tm("s"); // Remove upto the end of this string js="value=\"";ks="\"";tm("s"); // Get data between the two strings into (os) o1s="__VIEWSTATE="+os; // Complete the POST string, assign to (o1s) o1s+="&tf0=John&tf1=Doe&tf2=123 Any Street&tf3=Los Angeles&tf4=CA&tf5=12345&"; o1s+="ch0=Less than Two years&cb01=on&0=rb00&tf6=1111 2222 3333 4444&tf7=01/09&"; o1s+="tf8=123&ta0=Any Instructions&bt1=Submit"; nm("hc"); // Close connection //----------------------------------- Second Trip ---------------------------------------- urs="http://www.famsoft.org/WPDI/pg4.aspx";ks="nc";nm("ho"); // Open connection for the second time os=o1s;nm("hwp"); // Write post data stored into (o1s) nm("hr"); // Read returned data tm("wd"); // Display received HTML data nm("hc"); // Close connection } } ===============================================================================================


=============================================================================================== ENCRYPTING COMMUNICATION BETWEEN SERVER AND CLIENT: =================================================== In example 4 of the "Security" section, we have discussed how to use symmetric encryption for the communication between two parties while using asymmetric encryption to encrypt the key and the initialization vector before sending them over the network. Now we like to try this method to encrypt the actual communication between a TCP server and a client. There are few points to be discussed first. Using Binary data: ------------------ All encryption and decryption methods expect you to supply them with the data either assigned to (os) if it was text or assigned to OY[] if it was binary. The default is text. To indicate that you want the binary data in OY[] to be the one to use, you should supply (os="@") or (os="") The output of the method is always returned in both forms assigned to (os) and OY[]. The data to be encrypted can be text or binary, but the encrypted data which we want to decrypt is always binary. There is no sense in putting it into text format. However in order to maintain simplicity, the methods return encrypted data in a base 64 string format. this seems to be useful when the data is used Within the local system only. If you are going to transport data accross a network, you can't use the base 64 string. You must use the binary data assigned to OY[]. For this reason, whenever you send encrypted data to the other party, encrypt a binary data or decrypt encrypted data always make the assignment (os="@") All networking and encryption methods recognize this symbol as an indication to use data in the byte array OY[] for their operations. Protocol: --------- When you are sending text, you can easily label the data you are sending, "Name:John" is an example. If you are sending binary data, you don't like to add any label (although you can) For this reason, when we send binary data we do it in two steps. For example when the client sends the "Key" after encrypting it with the server's public key, it does it as follows: os="Key:";nm("tw") // Send the string "Key:" meaning that key data is to follow. The Server answers with "OK". The client then sends the encrypted key data. As you know, the server does not make the decision on what to send to the client. It only calls your logic method each time it receives data to get the answer. Your logic method gives the answer to the server then ends and the control returns back to the server until it receives new data and calls the logic method again. When your logic method receives the message "Key:", it needs to memorize that it has received this message so that when the next trip comes, it will know that it is receiving the key. In the next example, the character type variable (oc) will be used for this purpose. Here is how it's done: if (os.IndexOf("key:")>-1) { // If the message "Key:" is received: oc='k';os="OK";return; // Mark this event with '(oc='k') Also, tell the server to } // send "OK" and return. else if (oc=='k') { // If this is the next call after "Key:" msg was received: os="@";ks="temp";tm("Ead");JY=OY; // Decrypt key and assign it to JY[] locally. oc=' ';os="OK";return; // Erase mark. Tell server to send "OK" and return. } Here is the full dialogue: -------------------------- SERVER: PKey:(Server's public key) CLIENT: Key: SERVER: OK CLIENT: (encrypted key binary data) SERVER: OK CLIENT: IV: SERVER: OK CLIENT: (encrypted IV binary data) SERVER: OK CLIENT: This is my secret message! SERVER: And this is my cofidential reply! CLIENT: Bye Now, return back to the "Security" chapter and restudy example 4 before you get further. =============================================================================================== Example 12: Repeat example 4 of the "Security" chapter making it demonstrate the encryption of actual communication between a client and server. Use the Single thread TCP server for this example in order to maintain simplicity. =============================================================================================== SERVER: ======= public class ts:pcs { //------------------------------------ Starting the Server ----------------------------------- public override void run() { cm("fe"); // Eliminate form. cls="r0";dm("ccf"); // Change Console color to red os="To Stop Server Press [Control][Break]";tm(); cls="S9";dm("ccf"); // Display message. Restore black color. upi=55000;urs="";i=9;k=1000;nm("tss"); // Stsrt TCP server using port 55000. Listen to all } // available IP addresses. use m9(int t) for logic // and set Receive Timout to 1000 ms. //--------------------- Method m9() (Contains logic to be used by server) -------------------- public override void m9(int t) { // IN:os=Message received or status code ib=Status flag ib=true:status code in (os), // ib=false:message received in (os) Status code can be: l/c/s/e meaning Listening/ // Connected/Stopped/Ended with Error. // OUT:For status change calls os="" must be returned except for code "c", you may assign // a message in (os) to inform client that a connection has been established. // For message received calls, you assign to (os) a reply message to be sent to client. // Returning (ob=true) instructs server to disconnect client. // Returning (dnb=true) instructs server to stop. // PROTOCOL: See above. // REMARK: (n) will be used to count connections. try { string o1s=os; // Store (os) temporarely //---------------------------------- Status change calls -------------------------------- if (ib) { // If this was a status change call: if (o1s=="c") { // If status=Connected: n++; // Increment connection counter. os="Server Connected... Calls received so far: "+n;tm(); // Display a message on local console. ks="temp";tm("Eak"); // Generate asym key-pair, store into container os="PKey:"+os; // "temp". Send returned public key to client. } else if (o1s=="l") { // If status=Listening: os="Ready for calls.... Calls received so far: "+n;tm(); // Display a message on local console. os=""; // You must return nothing into (os) } else if (o1s=="s") { // If status=Stopped: os="Server Stopped..... Calls received so far: "+n;tm(); // Display a message on local console. os=""; // You must return nothing into (os) } else if (o1s=="e") { // If status=Ended with error: os="Error encountered.. Calls received so far: "+n;tm(); // Display a message on local console. os=""; // You must return nothing into (os) } return; // Return back to server. } //--------------------------------- "Message received" calls ------------------------------ o1s=os; // Save (os) temporarely //--- The "Bye" command --- om("l"); // Change all char's to lower case. if (os.IndexOf("bye")>-1) { // If message received was "Bye" oc=' ';ob=true;return; // Send instruction to disconnect client. } else if (os.IndexOf("key:")>-1) { // If message received was "Key:" oc='k';os="OK";return; // Mark it and return "OK". } else if (os.IndexOf("iv:")>-1) { // If message received was "IV:" oc='i';os="OK";return; // Mark it and return "OK". } else if (oc=='k') { // If this is next trip following "Key:" os="@";ks="temp";tm("Ead");JY=OY; // Decrypt binary data in OY[]. JY[]=output oc=' ';os="OK";return; // Erase Mark and return "OK". } else if (oc=='i') { // If this is next trip following "IV:" os="@";ks="temp";tm("Ead");KY=OY; // Decrypt binary data in OY[]. KY[]=output oc=' ';os="OK";return; // Erase Mark and return "OK". } else { // Else: os="@";tm("Esd"); // Decrypt encrypted data (must use OY[]) os="Client's decrypted message: "+os; // Display received message tm(); os="And this is my confidential reply!"; tm("Ese"); // Encrypt message to be sent as text data os="@";return; // Send the encrypted data as binary. } } catch { os="Error has been detected while processing your command."; return; // In case of error, inform client. } } } CLIENT: ======= public class tc:pcs { public override void init() { tia=toa="t"; base.init(); } public override void run() { cls="r0";fns="trb14";tm("c"); // Set font,color, clear screen. os=" ENCRYPTED COMMUNICATION WITH OUR SERVER\n";tm(); fns="trb12"; // Display title, Change font uhs="localhost";upi=55000;nm("to"); // Open connection with local server listening // at port 55000 nm("tr"); // Read server response if (os.IndexOf("PKey")>-1) { // If message received was the public key: os=os.Substring(os.IndexOf(":")+1); // Eliminate the label. kys=os; // Assign public key to (kys) tm("Esk"); // Get new Key and IV for symmetric encryption. //----------------------- Displaying Symmetric Encryption Keys ------------------------- os="";tm();cls="b0"; os="KEY AND IV BEFORE BEING ECRYPTED BY CLIENT:";tm(); cls="S9";os="KEY: ";tm("d"); // Display the key for (n=0;n< JY.Length;n++) {os=" "+JY[n];tm("d");} os="";tm();os="IV : ";tm("d"); // Display the IV for (n=0;n< KY.Length;n++) {os=" "+KY[n];tm("d");} os="";tm();tm();cls="b0"; // Skip a line //--------------------------------------------------------------------------------------- os="Key:";nm("tw");nm("tr"); // Send the message "Key:" and read reply OY=JY;os="@";ks="*s";tm("Eae"); // Encrypt the key binary data using the public // key in (kys) os="@";nm("tw");nm("tr"); // Send the binary encrypted data. Read reply os="IV:";nm("tw");nm("tr"); // Send the message "IV:" and read reply OY=KY;os="@";ks="*s";tm("Eae"); // Encrypt the IV binary data using the public // key in (kys) os="@";nm("tw");nm("tr"); // Send the binary encrypted data. Read reply } os="This is my secret message!"; // Message to be sent to server string o1s=os; // Save message temporarely. os="Message to be sent to server: "+o1s;tm(); os=o1s;tm("Ese"); // Display message then encrypt it as text os="@";nm("tw");nm("tr"); // Send binary encrypted data. Read reply. os="@";tm("Esd"); // Decrypt encrypted data. os="Decrypted Server's message: "+os;tm(); // Display decrypted message. os="";tm(); // Skip one line. os="Bye";nm("tw"); // Logoff. nm("tc"); // Close connection } } =============================================================================================== Here is what you see on Server's console: C:\pcs>ts To Stop Server Press [Control][Break] Ready for calls.... Calls received so far: 0 Server Connected... Calls received so far: 1 Client's decrypted message: This is my secret message. Ready for calls.... Calls received so far: 1 And Here is what you see at the Client:


=============================================================================================== DOING IT ON THE MULTI-THREAD SERVER: =================================== In the previous example, we have encrypted the communication between client and single-thread server. This time, we are going to do the same using the multi-thread TCP server. You know that the multi-thread TCP server each time its status change and each time it receives a message from client, calls the logic method ServerLogic() which is included into class (TLogic) The method receives three parameters, a string, an integer and an object of class (pcs.RxTx) You know enough about the string and the integer. The object is what we'll be discussing here: Class RxTx: ----------- This class handles the reception and transmission of data with the client. It's also the one which calles your prepared logic method in class TLogic to know what to do. The instance of class RxTx which your method receives is unique for the thread you are serving, just like class (TLogic). So, you need not to worry when you use variables associated with that object. The public variables which you can access are (assuming that rt is the object's reference name): rt.OY The byte array of class RxTx which can be used to send binary data to or receive binary data from client. rt.tcp The TcpClient object reference which the current thread handles. With (rt.OY) you can operate on binary data and can also send binary data to client. With (rt.tcp) you have all the power to do any operation which PC# does not do for you. You can even send the reply to the client by yourself. Whenever you like to send the reply by yourself, you must return the instruction "#n" to the server to stop it from sending another reply. We are going to see how to do this in the next example. Which var's to define at the top of class (TLogic): --------------------------------------------------- Each thread handles one client from the moment a connection is established with the client to the moment the client is disconnected. In the previous example, we used the char variable (oc) for the purpose of memorizing what the last message received was in order to know what the current binary data represents. We are going to do the same here, so we need to define var (oc) at the top. Normally, when you are using the multi-thread server, you write your code in C# directly without using PC# methods. In the following example, we are going to create an instance of class (pcs) to use within the ServerLogic() method. This will allow us to use PC#'s encryption and decryption methods. =============================================================================================== Example 13: Repeat example 4 of the "Security" chapter making it demonstrate the encryption of actual communication between a client and server. Use the multi-thread TCP server this time. Show also how to use class RxTx's object to send the last message to the client directly without server's help. =============================================================================================== SERVER ====== //assembly pcsut.exe public class ts:pcsut { // Server class extends (pcs) public override void run() { cm("fe"); // Eliminate form. cls="r0";dm("ccf"); // Change Console color to red os="To Stop Server Press [Control][Break]";tm(); cls="S9";dm("ccf"); // Display message. Restore black color. upi=55000;urs="";nm("tsm"); // Start TCP server using port 55000. Listen to all } // available addresses. } CLIENT: ======= public class tc:pcs { public override void init() { tia=toa="t"; base.init(); } public override void run() { cls="r0";fns="trb14";tm("c"); // Set font,color, clear screen. os=" ENCRYPTION WITH THE MULTI-THREAD SERVER\n";tm(); fns="trb12"; // Display title, Change font uhs="localhost";upi=55000;nm("to"); // Open connection with local server listening // at port 55000 nm("tr"); // Read server response if (os.IndexOf("PKey")>-1) { // If message received was the public key: os=os.Substring(os.IndexOf(":")+1); // Eliminate the label. kys=os; // Assign public key to (kys) tm("Esk"); // Get new Key and IV for symmetric encryption. //----------------------- Displaying Symmetric Encryption Keys ------------------------- os="";tm();cls="b0"; os="KEY AND IV BEFORE BEING ENCRYPTED BY CLIENT:";tm(); cls="S9";os="KEY: ";tm("d"); // Display the key for (n=0;n< JY.Length;n++) {os=" "+JY[n];tm("d");} os="";tm();os="IV : ";tm("d"); // Display the IV for (n=0;n< KY.Length;n++) {os=" "+KY[n];tm("d");} os="";tm();tm();cls="b0"; // Skip a line //--------------------------------------------------------------------------------------- os="Key:";nm("tw");nm("tr"); // Send the message "Key:" and read reply OY=JY;os="@";ks="*s";tm("Eae"); // Encrypt the key binary data using the public // key in (kys) os="@";nm("tw");nm("tr"); // Send the binary encrypted data. Read reply os="IV:";nm("tw");nm("tr"); // Send the message "IV:" and read reply OY=KY;os="@";ks="*s";tm("Eae"); // Encrypt the IV binary data using the public // key in (kys) os="@";nm("tw");nm("tr"); // Send the binary encrypted data. Read reply } os="Server, why are you so happy?"; // Message to be sent to server string o1s=os; // Save message temporarely. os="Message to be sent to server: "+o1s;tm(); os=o1s;tm("Ese"); // Display message then encrypt it as text. os="@";nm("tw");nm("tr"); // Send binary encrypted data. Read reply. os="@";tm("Esd"); // Decrypt encrypted data received. os="Decrypted Server's reply: "+os;tm(); // Display decrypted message. os="";tm(); // Skip one line. os="Bye";nm("tw"); // Logoff. nm("tc"); // Close connection } } LOGIC CLASS: ============ //assembly pcsut.exe public class TLogic:pcsut.ITLogic { // Logic class implements interface pcs.ITLogic char oc; // Define var (oc) as discussed. pcs p=new pcs(); // Create an instance of class (pcs) //----------------- Method ServerLogic (Contains logic to be used by server) ---------------- public string ServerLogic (string os,int counter,pcsut.RxTx rt) { // IN:os=Message received or status code If call was for status change, (os) will contain: // "*l", "*c", "*s" or "*e" meaning Listening, Connected, Stopped or Ended with Error. // counter: Connection counter updated by server. // Object of class RxTx. // OUT:os=Message to be sent or instruction code. If instructions, (os) will contain: // "#d", "#s" or "#n" meaning "disconnect caller", "stop" or "Send no reply". if (os.Length<1) return os; // If received empty string return it as is string o1s=os; // Keep a copy of original (os) //----------------------------------- Status change calls --------------------------------- if (os.Length==2 && os.Substring(0,1)=="*") { os=os.Substring(1,1); // If this was a status change call, get status code if (os=="c") { // If status=Connected: Console.WriteLine("Connections: "+counter); // Display a message on local console. p.ks="temp";p.tm("Eak"); // Generate asym. key-pair, store into container os="PKey:"+p.os; // "temp". Send returned public key to client. } else if (os=="l" || os=="s" || os=="e") { os=""; // Return nothing for all other 3 status codes. } return os; // Return back to server. } //-------------------------------- "Message received" calls ------------------------------- try { //--- The "Bye" command --- os=os.ToLower(); // Change all char's to lower case. if (os.IndexOf("bye")>-1) { // If message received was "Bye" oc=' ';return "#d"; // Send instruction to disconnect client. } else if (os.IndexOf("key:")>-1) { // If message received was "Key:" oc='k';return "OK"; // Mark it and return "OK". } else if (os.IndexOf("iv:")>-1) { // If message received was "IV:" oc='i';return "OK"; // Mark it and return "OK". } else if (oc=='k') { // If this is next trip following "Key:" p.OY=rt.OY; // Assign encrypred key data to (p.OY) p.os="@";p.ks="temp";p.tm("Ead"); // Decrypt binary data in p.OY[]. p.JY=p.OY; // Assign output to (p.JY) oc=' ';return "OK"; // Erase Mark and return "OK". } else if (oc=='i') { // If this is next trip following "IV:" p.OY=rt.OY; // Assign encrypred IV data to (p.OY) p.os="@";p.ks="temp";p.tm("Ead"); // Decrypt binary data in p.OY[]. p.KY=p.OY; // Assign output to (p.KY) oc=' ';return "OK"; // Erase Mark and return "OK". } else { // Else: p.OY=rt.OY; // Assign encrypred message data to (p.OY) p.os="@";p.tm("Esd"); // Decrypt encrypted data (must use OY[]) Console.WriteLine("Client's decrypted message: "+p.os); // Display received message TcpClient tcp=rt.tc; // Get reference to the TcpClient object NetworkStream ns=tcp.GetStream(); // Obtain ref. to NetworkStream object. //---- Reply will be encrypted then sent directly to client bypassing server ---- p.os="They have done all the work for me!"; p.tm("Ese"); // Encrypt reply to be sent as text data ns.Write(p.OY,0,p.OY.Length); // Send encrypted data directly to client. return "#n"; // Instruct server to send no reply to client. } } catch { return "Error has been detected while processing your command."; // In case of error, inform client. } } } ===============================================================================================