VB.NET Chat Room Tutorial With PHP & SQL [Intermediate] - +Haze - Jan 3, 2015
Introduction
So this was requested by a few members here, and I thought it would be a good idea to do something different. I've seen tons of chats clients made in VB.NET that use sockets and TCP/IP, the tutorials only show how this would work for local networks. So I'm going to show how to do a chat room in vb.net that utilizes a webserver(PHP & some SQL to store our user information). So we could use this globally, and anyone with internet connection could connect and chat.
First of all I'll mention the disadvantages & advantages of using a webserver as a chat room whatever in the way I did it.
Advantages
+ It can be fast if you got a nice web-host
+ It's a good learning experience, from this tutorial you can learn a lot about PHP, SQL and coding in general(vb.net specifically)
Disadvantages
- Really and truly, this should be done with sockets but meh.
- This creates tons of requests to your web-host per minute but you can get a free one like x10 hosting that don't care.
Also, I'm not really focusing on security, I'm focusing on actually teaching people valuable skills and fundamentals. The only security, there is, is that the password is hashed + salted. And using PDO to prevent SQL Injections. Though the messages are not encoded or whatever.
Because I do not feel its necessary, if the user is really egar, they can learn how to do it for themselves. From the tutorial I'm writing out, I've done a lot of work on my side. And I'm actually going over every single variable declaration and function declaration and making sure I explain them fully. Both the PHP code, SQL Code and VB.NET code. I'm not just spitting out the source code. I'm actually being a TEACHER, which is something I think we don't see enough in this section.
And that's my justification for not encoding the messages or worried too much about security.
Setup MySQL Database
Okay so let's get started.
First we're going to start with our server side code. So this is the PHP and SQL stuff.
Firstly, I recommend that you go check out my first tutorial on how to do a login and registration system using vb.net php and sql. What this tutorial is is an extension of the previous one.
We login to our cPanel webhost and we setup our database.
Setup SQL Tables
So we're going to create 3 tables. tblLogin, tblMessage and tblMessageLog.
tblLogin will store all the login information of our users. And in that table we will have 5 columns
UserID, Username, Password, IsBanned, DateRegistered.
the UserID is unique ID that is used identify each user, just like you an I have a unique HF UserID.
Next is tblMessage. This will store all the information about each message that is sent by each of our users.
So again, MessageID to identify each message, UserID to identify which user sent it, the content of the message, and what time it was sent by.
now our final table. tblMessageLog.
This table is used when a message is read by any user. So when a user reads a message, it will appear in this table, so the MessageID of the message that was read, and the UserID of the user who has READ the message.
PHP Server Side Code
Note: I don't know PHP as well as I do C# and VB.NET so bare my sloppy code.
Step 1 we create a db.php which we will place code required to connect to our database and sql database where. So you need to create a database, with some users and stuff. Once you've learned how to do that else where, you can proceed.
We will call this db.php
PHP Code
<?php // Connection details //Change me $db_host = "localhost"; $db_name = ""; // database name $db_user = ""; // datebase user $db_pass = ""; // database password
$connstr = "mysql:host=$db_host;dbname=$db_name";
try { $con = new PDO($connstr, $db_user, $db_pass); } catch (exception $e) { echo "error"; //you might want to display a more meaningful error exit(); }
?>
We're now going to create the php script that handles our register requests.
We will call this register.php
PHP Code
<?php include 'db.php'; //We "Import/Include" this so we can access our DB credentials.
$user = $_GET['user']; $pass = $_GET['pass']; //We declare two GET variables that we can use to query against our database.
//We ensure the variables are not empty, if they're not we then proceed into the if statement if (!empty($user) && !empty($pass)) { if (registerSuccess($user, $pass)) { echo "success"; } else { echo "fail"; } } else { echo "jog on kid"; }
function registerSuccess($username, $password) { global $con; $salt = substr(str_shuffle("!£$%^&*abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"), 0, 25); $hash = crypt($password, $salt);
//We generate a random salt, and we then use the crypt function to generate us a random has.
$stmt = $con->prepare("INSERT INTO tblLogin(Username, Password, DateRegistered) VALUES(:user, :pass, :ts)"); //This insert query will insert the two values from our declared GET variables into our SQL tables.
$stmt->bindValue(':user', $username, PDO::PARAM_STR); $stmt->bindValue(':pass', $hash, PDO::PARAM_STR); $stmt->bindValue(':ts', date('Y-m-d G:i:s')); //We bind the values from our sql query to our prepared statement
//Executes the query, and if it is successful we print out "success" if ($stmt->execute()) { return true; } else { return false; }
$db = null; } ?>
We will now create our PHP script to login to our system from our vb.net application. We will call this login.php
PHP Code
<?php include 'db.php'; //We "Import/Include" this so we can access our credentials.
$user = $_GET['user']; $pass = $_GET['pass']; //We declare two GET variables that we can use to query against our database.
LoginUser($user, $pass);
function LoginUser($username, $password) { global $con;
$stmt = $con->prepare("SELECT * FROM tblLogin Where Username = :user Limit 1"); //This insert query will select all the data from our sql server, where the username of the GET variables //match the one in our SQL table. And the Limit 1 means, return only 1/the first result and ignore the rest.
//We has their current password, and use their hashed password as the salt, and compare // Hashing the password with its hash as the salt returns the same hash if ($hashedpassword == crypt($password, $hashedpassword)) { echo json_encode($arr); }
else { echo "doesn't exist"; }
$con = null; } ?>
This is an image of what the data it echos when you successfully login would look like.
This is the script we use to add a message to our database we will call this. addmessage.php
PHP Code
<?php include 'db.php'; //when we include, we import/include all our data from our db.php page so we can use them here. without //having to copy and paste the same data all the time
//we declare two GET variables, which will contain the userId of the user trying to add the message. //And msg is the content of the message they're trying to send. We could of used POST which would be better for larger //data, but for tutorial sake, let's just use GET.
//We check to see if the variables are empty or not, if they are not empty then we process. if (!empty($MessageContents) && isset($UserID)) { //If not empty, then we add the message, we create our own function called addMessage() //This function will take in two arguments as the parameters. UserID and MessageContents. //This function will then add the message to our sql table, and if it's successfull. //It will print "success" else it prints failed
$con = null; } else { //If the two GET variables are empty, this message will show. echo "jog on kid"; }
function addMessage($userId, $msg) { global $con; //by typing global, we reference the variable in our db.php. So we can use that variable to connect to our db
//we create use PDO(PHP Data Objects) to create a prepared SQL statement, which prevents SQL Injection attacks. //And we bind the data $userID and $msg from to user to our database. And we execute. If execution is succesful we return tru //else we return false
Next we create our script to get the unread messages of each user. --Warning, this bit gets a bit complex if you do not understand SQL very well, the queries are a bit complex, but don't worry.
We will name this script getunread.php
PHP Code
<?php include 'db.php'; //We "Import/Include" this so we can access our DB credentials.
//we create 3 GET variables. //We reat the timesignedIn variable,
//This validates to check if the variables are not empty. And validates to see if //The user requested is a valid user or not. If they're then we proceed. if (!empty($Action) && !empty($TimeSignedIn) && validUserID($UserID) == true) { //The GetUnreadCount will return an Integer, of the count of the unreadMessages of the user. //The GetUnreadMessages will return the conetents of all their unread messages.
if ($Action == "count") { echo getUnreadCount($UserID); }
function getUnreadCount($userId) { global $con; global $TimeSignedIn;
$stmt = $con->prepare("Select MessageID, Username, Message, TimeSent From tblLogin, tblMessage Where tblMessage.TimeSent >= :timesignedin And tblLogin.UserID = tblMessage.UserID And Not tblLogin.UserID = :ID And tblMessage.MessageID Not In(Select MessageID From tblMessageLog Where UserID = :ID)");
//This query is quiet complex, but it does is. It Selects all the infromation from the message table, //Where the time the message was sent is after the user has logged in, and where the message ID is not found in the MessageLog //table(because thats where all read messages go) and where the message is not a message sent by the user himself.
//We then bind the values off the userid and timesigned in to the query, and execute it and return the row count //The row count signifies the amount of results it found from then query.
return $stmt->rowCount(); }
function getUnreadMessages($userId) { global $con; global $TimeSignedIn;
$stmt = $con->prepare("Select MessageID, Username, Message, TimeSent From tblLogin, tblMessage Where tblMessage.TimeSent >= :timesignedin And tblLogin.UserID = tblMessage.UserID And Not tblLogin.UserID = :ID And tblMessage.MessageID Not In(Select MessageID From tblMessageLog Where UserID = :ID)");
//So while PDO its still reading the results, we add each result it reads into an array //We also add the MessageIDs of the data it reads to an array. So After that we can then add the //Message ID to the MessageLog table, to show it has been read.
echo json_encode($result);
//We then encode the result, and echo it out as JSON data.
//We then loop through each msgID we have, and add the message ID and userID to the messagelog table //this then lets the system know the message has been read, so it will never pull it up again }
function addMessageLog($userId, $messageId) { global $con;
$stmt = $con->prepare("Insert Into tblMessageLog(MessageID, UserID) Values(:msgID, :userID)");
//We could of used a mass Insert statement, but for tutorial purposes, let's keep it nice and simple
I thought to mention, the JSON data that is being outputted for each message would look like this. And our vb.net application would read that and convert it into an object.
But in our case, there will be a lot of those.
okay so now we have finished our server side scripting/coding. And we now go to our vb.net application to finish off.
This is a temporary place holder. - +Haze - Jan 3, 2015
VB.NET Form Designing & Code
So once again, head over to visual studio to complete our client side of the program.
First we need to create our two classes that will be very important in our program.
base.vb and chatclient.vb
the chatclient is a class we use send commands to our php server using a simple class with methods and functions. It's just like when you do. Console.WriteLine()
Console is a class, and WriteLine() is a method. And you put the string you want to show and it prints it to the console. What I'm doing is replicating a system like that. Where we could do similar things with out program. We call the client class, put in the information and the class will execute and return the response.
Okay so first create our base.vb file and here's the code that goes in it.
So heres our chatclient.vb code. I will post the images instead.
but before we code this bit, make sure you have installed the JSON.NET Nuget package from the Nuget Package Manager on visual studio.
Please may I add, when you login, if you're successful our webserver will show this. And we read that information and store it in our user variable. The server shows us our userId, If we're banned or not, and the time we signed in. Which will be all very import.
Explanation of GetUnReadMessages
Explanation of what this function does. So, if our webserver echos data that looks like
What the Dim msgObj() As Message = JsonConvert.DeserializeObject(Of Message())(currentResponse) code is doing is tranlating that JSON data into an array of our custom message object. And returning it so it could be used to add to our chat box.
Private Sub btnLogin_Click(sender As Object, e As EventArgs) Handles btnLogin.Click
'We first check if the two textboxes contain any empty characters, if they do we alert the user
'else we allow them to login
If String.IsNullOrWhiteSpace(txtUsername.Text) Or String.IsNullOrWhiteSpace(txtPassword.Text) Then
MessageBox.Show("You cannot leave the username or password fields blank")
Else
Try
Dim message As LoginMessage = Client.Login(txtUsername.Text, txtPassword.Text)
Select Case message
Case LoginMessage.Success
MessageBox.Show("Login approved, welcome back.")
Case LoginMessage.NotExist
MessageBox.Show("Either your username or password is incorrect")
Case LoginMessage.Banned
MessageBox.Show("Sorry your account has been banned")
Case LoginMessage.Error
MessageBox.Show("An error has occoured trying to log your account in")
End Select
Catch ex As Exception
MessageBox.Show("An error has occoured trying to log you in: " & ex.Message)
End Try
End If
End Sub
Private Sub txtPassword_KeyDown(sender As Object, e As KeyEventArgs) Handles txtPassword.KeyDown
If e.KeyCode = Keys.Enter Then
e.SuppressKeyPress = True
btnLogin.PerformClick()
End If
End Sub
Private Sub ShowChatForm()
frmChatRoom.Show()
Me.Hide()
End Sub
Private Sub ClearControls()
For Each txtbx As TextBox In Me.TabRegister.Controls.OfType(Of TextBox)()
txtbx.Clear()
Next
For Each txtbx As TextBox In Me.TabLogin.Controls.OfType(Of TextBox)()
txtbx.Clear()
Next
'So we loop through each tab page on our login Form, and filter through the textboxes only using the OfType
End Sub
Private Sub btnRegister_Click(sender As Object, e As EventArgs) Handles btnRegister.Click
If String.IsNullOrWhiteSpace(txtRegUsername.Text) Or String.IsNullOrWhiteSpace(txtRegPassword.Text) Then
MessageBox.Show("You cannot leave the username or password fields blank")
Else
If txtRegPassword.Text = txtRegConfirm.Text Then
Try
If Client.RegisterSuccess(ParseInfo(txtRegUsername.Text), ParseInfo(txtRegPassword.Text)) Then
MessageBox.Show("Registeration complete. Please login")
Else
MessageBox.Show("Registeration unsuccesfull")
End If
Catch ex As Exception
MessageBox.Show("An error has occured trying to register you : " & ex.Message)
End Try
Else
MessageBox.Show("Passwords do not match. Try again!")
End If
End If
End Sub
Private Function ParseInfo(txt As String) As String
Return Regex.Replace(txt, "[^a-zA-Z0-9]", String.Empty)
'what this does is to replace every non alphabetical item found in the string passed to it.
'so that means our username and password can only contain alphabetic letters and numbers
End Function
End Class
Private Event MessageRecieved As EventHandler
Private Event SpeedIntervalChanged As EventHandler
'We create our own custom event, and this event will be fired each time the system detects the user
'has a new message or when the speedInterval has changed and then we can create an event handler
'to do a certain action every time an event is raised.
Private SpeedInterval As Integer
Private UnreadMsgCount As Integer
Private Sub FormChatRoom_Load(sender As Object, e As EventArgs) Handles MyBase.Load
ShowGreatingMessage()
tmrNewMsg.Start()
End Sub
Private Sub ShowGreatingMessage()
lstChatBox.Items.Clear()
AddMsgToLst("Welcome to Tango chatroom!")
AddMsgToLst("Connected to chat server succesfully!")
End Sub
Private Sub ChatRoom_Shown(sender As Object, e As EventArgs) Handles Me.Shown
UnreadMsgCount = 0
End Sub
Private Sub Form1_Resize(sender As Object, e As EventArgs) Handles MyBase.Resize
If WindowState = FormWindowState.Minimized Then UnreadMsgCount = 0
End Sub
Private Sub FormChatRoom_FormClosed(sender As Object, e As FormClosedEventArgs) Handles Me.FormClosed
Client.AddSignOutMsg(MyUser.Username)
Application.Exit()
End Sub
Private Sub btnLogout_Click(sender As Object, e As EventArgs) Handles btnLogout.Click
Me.Dispose()
'By disposing of our Form, we release all its objects and the data involved.
Client.AddSignOutMsg(MyUser.Username)
frmLogin.Show()
End Sub
Private Sub tmrNewMsg_Tick(sender As Object, e As EventArgs) Handles tmrNewMsg.Tick
ThreadPool.QueueUserWorkItem(Sub() If Client.UnreadMessagesCount(MyUser.UserID) >= 1 Then RaiseEvent MessageRecieved(Me, EventArgs.Empty))
'So everytime this timer ticks, we use a threadpool to queue some work we want it to do.
'So, we Check if the use our client class we made. We check if the UnreadMessage count is greater than or equal
'To 1, if it is, we then raise our custom event. MessageRecieved
End Sub
Private Sub Me_MessageArrived(sender As Object, e As EventArgs) Handles Me.MessageRecieved
ThreadPool.QueueUserWorkItem(Sub() RetrieveUnreadMsg())
'When this event is raised we then do the work in the RetrieveUnreadMsg() Sub
End Sub
Private Sub RetrieveUnreadMsg()
Try
Dim msgList() As Message = Client.GetUnreadMessages(MyUser.UserID)
'So this gets an array of the message object we created in our base class.
If IsNothing(msgList) = False Then
'We check if the msgList has data in it, if it does we then add the data to our listbox
Invoke(Sub() lstChatBox.Items.AddRange(msgList))
Invoke(Sub() lstChatBox.TopIndex = lstChatBox.Items.Count - 1)
'This is used to scroll to the last added item in our listbox
If WindowState = FormWindowState.Minimized Then
UnreadMsgCount += msgList.Count
Invoke(Sub() ShowBallon("You have " & UnreadMsgCount & " unread message(s)", "Message", 100, ToolTipIcon.Info))
End If
End If
Catch ex As Exception
Invoke(Sub() ShowBallon(ex.Message, "Error occured", 100, ToolTipIcon.Error))
End Try
End Sub
Private Sub txtMessage_KeyDown(sender As Object, e As KeyEventArgs) Handles txtMessage.KeyDown
If e.KeyCode = Keys.Enter Then
e.SuppressKeyPress = True
btnSend.PerformClick()
End If
End Sub
Private Sub btnSend_Click(sender As Object, e As EventArgs) Handles btnSend.Click
If String.IsNullOrEmpty(txtMessage.Text) = False Then
If Client.SendMessage(txtMessage.Text) Then
AddMsgToLst(txtMessage.Text)
txtMessage.Clear()
Else
MessageBox.Show("Error sending your message boi")
End If
End If
End Sub
Public Sub AddMsgToLst(msg As String)
lstChatBox.Items.Add(String.Format("[{0}] {1}", Date.Now, msg))
lstChatBox.TopIndex = lstChatBox.Items.Count - 1
End Sub
Private Sub MaskedTextBox1_TextChanged(sender As Object, e As EventArgs) Handles MaskedTextBox1.TextChanged
Dim currentInterval As Integer
If Integer.TryParse(MaskedTextBox1.Text, currentInterval) Then
If currentInterval >= 500 Then
SpeedInterval = currentInterval
RaiseEvent SpeedIntervalChanged(Me, EventArgs.Empty)
Else
ShowBallon("Values can't be lower than 500", "Alert", 100, ToolTipIcon.Info)
End If
End If
End Sub
Private Sub Me_SpeedChanged(sender As Object, e As EventArgs) Handles Me.SpeedIntervalChanged
tmrNewMsg.Interval = SpeedInterval
End Sub
Private Sub lstChatBox_MouseDown(sender As Object, e As MouseEventArgs) Handles lstChatBox.MouseDown
If e.Button = Windows.Forms.MouseButtons.Right Then ChatContextMenuStrip.Show(MousePosition)
'This is used so when you right click on the listbox, it will Show a context menu, showing the items
'Copy and Remove
End Sub
Private Sub MenuItemCopy_Click(sender As Object, e As EventArgs) Handles MenuItemCopy.Click
'When we click copy, this is what happens.
If lstChatBox.SelectedIndex > -1 Then
Dim selectedMsg As Message = TryCast(lstChatBox.SelectedItem, Message)
If IsNothing(selectedMsg) = False Then
My.Computer.Clipboard.SetText(selectedMsg.Message)
Else
My.Computer.Clipboard.SetText(lstChatBox.SelectedItem.ToString())
End If
ShowBallon("Message has been copied to your clipboard", "Copied", 100, ToolTipIcon.Info)
End If
End Sub
Private Sub MenuItemRemove_Click(sender As Object, e As EventArgs) Handles MenuItemRemove.Click
'When we click remove, this is what happens
If lstChatBox.SelectedIndex > -1 Then lstChatBox.Items.RemoveAt(lstChatBox.SelectedIndex)
End Sub
Private Sub ShowBallon(text As String, title As String, timeout As Integer, icon As ToolTipIcon)
'This sub is handy for when we want to show our NotificationIcon
NotifyIconMsg.ShowBalloonTip(timeout, title, text, icon)
End Sub
End Class
Download Link to everything covered in this tutorial
The whole vb.net solution files, .sql data, php source codes. Everything you need to set this up in 5 minutes.
Credits Spoiler (Click to View)
Though I coded everything here myself, I got the inpiration to do this, and I also got help from other users in some minor PHP help and I got help from members that helped me test it in the video.
Vouch for my Nigga +Haze. Very good tutorial. Makes me comfortable to see my name in the credits spoiler.
Continue with your work!
* - Entire - Jan 3, 2015
This is actually an amazing tutorial. Very details etc.. Going to try it now ;D
* - sha110w - Jan 3, 2015
Why would you keep and read messages in/from a database for this instead of sending/reading them in real time between the server and client? If you want to keep a log, just log them in a file on the server.
* - Shoot - Jan 3, 2015
nice tutorial!
i wanna learn how to make a video chat (like skype) using VB.NET but just video & text nothing really extra
(Jan 3, 2015 01:40 PM)sha110w Wrote: Why would you keep and read messages in/from a database for this instead of sending/reading them in real time between the server and client? If you want to keep a log, just log them in a file on the server.
I have no idea what you're talking about. Either way, if you want to discuss why I didn't do so and so, get enough posts & PM me. I'd like to keep this thread as on topic as possible. The minute you go offtopic, I'll just ask admins to remove it, I've had enough of shitstorms being spawned on my threads.
(Jan 3, 2015 01:44 PM)Shoot Wrote: nice tutorial!
i wanna learn how to make a video chat (like skype) using VB.NET but just video & text nothing really extra
Thanks for the nice messages above. For this you'll need to learn ALOT, and I mean ALOT. Like Sockets, algorithms used for compressing data, Networking. Databases, OOP etc.
* - sha110w - Jan 3, 2015
(Jan 3, 2015 01:46 PM)+Haze Wrote: I have no idea what you're talking about.
What do you mean you have no idea what I'm talking about? YOU wrote this code didn't you? YOU understand what this code does don't you?
(Jan 3, 2015 01:40 PM)sha110w Wrote: Why would you keep and read messages in/from a database for this instead of sending/reading them in real time between the server and client? If you want to keep a log, just log them in a file on the server.
I don't think you know what you're talking about. He's using another way to store logs and such. Nothing to worry about if you ask me. The only thing that matters right now is that the chat system works which it obviously does.
* - sha110w - Jan 3, 2015
(Jan 3, 2015 01:58 PM)Keiko Wrote: I don't think you know what you're talking about. He's using another way to store logs and such. Nothing to worry about if you ask me. The only thing that matters right now is that the chat system works which it obviously does.
Code
function addMessageLog($userId, $messageId)
{
global $con;
$stmt = $con->prepare("Insert Into tblMessageLog(MessageID, UserID) Values(:msgID, :userID)");
//We could of used a mass Insert statement, but for tutorial purposes, let's keep it nice and simple
(Jan 3, 2015 01:58 PM)Keiko Wrote: I don't think you know what you're talking about. He's using another way to store logs and such. Nothing to worry about if you ask me. The only thing that matters right now is that the chat system works which it obviously does.
Did you even read the thread? If he wasn't using a database then SQL wouldn't be needed at all.
A simple JS -> VB sockets connection would be fine.
(Jan 3, 2015 02:09 PM)+Haze Wrote: I'm aware. I stated this in the first few lines of this thread. Spoiler (Click to View)
Anyway no more off topic posts please..
I didn't even aim my comment towards you or your thread.
I was replying to Keiko.
EDIT: Also while I'm at it, I'd like to mention that even if you are trying something new, you really shouldn't be teaching this sort of stuff. It's nothing more than a security risk and slow as hell.