Solved

Online card game

Posted on 2004-04-25
9
448 Views
Last Modified: 2013-11-13
Hello Experts,

I'm trying to create online bridge-like game (2 pair of players).
The game should be serverless(one of the players computer serves others)
Is trere any recomendation which technique to use, may be some online guide or sample code
Thanks in advance

Michael
0
Comment
Question by:Michael_D
  • 4
  • 4
9 Comments
 
LVL 13

Author Comment

by:Michael_D
Comment Utility
Anybody?
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
A complete "online bridge-like" game has many parts!

Did you want help on all of it or just a particular piece?

Some possible areas of discussion:

1) Graphics Manipulation (Loading / Saving Images)
2) Representing Cards using an Object Oriented Approach
3) Moving Cards around the Screen
4) Communication between the players

Idle_Mind
0
 
LVL 13

Author Comment

by:Michael_D
Comment Utility
Hi, Idle_Mind,

I don't really figure out the best way of communication between players and how to initiate the server mode.
Let say one person want to play a game.
He starts an application in multiplayer mode, chooses "Be a Host Computer" Option, fills out some parameters (game name etc.)
Another  player chooses "Connect to Host" option and there is 2 more options:
1. Enter the known IP address (simple)
2. Search for it over the net (here the problem).

Can you help with this?


0
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
Hi Michael_D,

I had forgotten all about this question sorry.  With regard to Option 2:

>> 2. Search for it over the net (here the problem).

Most systems that "search" for another host actually use a central server.  You have a server program on a dedicated machine with either a static IP or a registered domain name that your clients register with as a host.  Then when someone wants to search for a host, they simply query the server for a list of the currently available hosts and select one.

Idle_Mind
0
Better Security Awareness With Threat Intelligence

See how one of the leading financial services organizations uses Recorded Future as part of a holistic threat intelligence program to promote security awareness and proactively and efficiently identify threats.

 
LVL 13

Author Comment

by:Michael_D
Comment Utility
Hi Idle_Mind,

Thanks for your post, I did exactly that way. Actually I desided to use ASP.NET so now this question a bit out of topic.  Anyway I've created Web Service with 3 public methods: RegisterHost, UnregisterHost and GetHostList. It's make possible to search for host. Now I am working on communication between players and may be here you can help. What protocol is better to use TCP or UDP? If it's TCP, Do I have to put 4 winsock controls on the server side maintaining permanent connection with others   or use only one and connect/disconnect every time. I know technically both ways are possible. question is which way is better?

Michael_D
0
 
LVL 85

Accepted Solution

by:
Mike Tomlinson earned 500 total points
Comment Utility
TCP is a managed protocol, meaning the transmissions are guaranteed to arrive in the order they were sent and the next packet won't be sent until confirmation of the previous packets arrival is received.

UDP is a unmanaged, or broadcasting protocol, which means the packets are simply released and they are not guaranteed to arrive in order, if they arrive at all.

So TCP would be the obvious choice here since your transmissions will be involving important game decisions which affect the gameplay.  The game would be very unstable if sometimes your decision is known to the other players and sometimes it isn't.  =)

As far as communication goes you need to decide on two basic configurations:

A) All machines are clients with one connection to a hosted game on your ASP.Net server.
B) One of the clients acts as a host machine with 3 connections to the other clients and the other clients have only one connection to host.

Option A is appealing since all the clients can be behind firewalls and they can all still play together because of their common connection to an outside source (your ASP server).

Option B is appealing since the gameplay logic and overhead is taken away from the server but then you may not be able to have direct connection from client to client if the host is behind a firewall.

If you go with Option B, then the best way to proceed is to have one Winsock control in a control array, this way all your code is central to one subroutine and you won't have duplicated code for each winsock control serving the different connections.

It is common practice to have the winsock control at index 0 always listening, and the new incoming connections accepted on higher control indices.  Below is a complete server that allows for any number of connections in this exact manner.  The form has a winsock control in a control array (add a winsock control and set its index property to zero), and a richtextbox.  The code demonstrates how to maintain seperate input buffers for each control, and how to use a marker to determine the end of a transmission (they can arrive in several transmissions sometimes).  I have used Chr(1) as an end of transmission marker and Chr(0) as a parameter delimeter is this example.  The code is a lot to take in but it demonstrates many key concepts.

Let me know if you want to see the corresponding client code for this server.

Hope it helps,

Idle_Mind

Option Explicit

Private inputBuffer() As String
Private users As Integer

Private Sub Form_Load()
    Me.Caption = "Server"
    RichTextBox1.Locked = True
    users = 0
    ReDim inputBuffer(0)
   
    ' channel 0 is the only listening port
    Winsock1(0).LocalPort = 10001
    Winsock1(0).Listen
End Sub

Private Sub Form_Unload(Cancel As Integer)
    Dim a As Integer
   
    ' shut everything down
    For a = Winsock1.UBound To 0 Step -1
        If Winsock1(a).State <> sckClosed Then
            Winsock1(a).Close
        End If
    Next a
End Sub

Private Sub Winsock1_ConnectionRequest(index As Integer, ByVal requestID As Long)
    Dim acceptIndex As Integer
    Dim curindex As Integer
   
    ' See if there is an available Channel already loaded
    acceptIndex = -1
    If index = 0 Then
        For curindex = 1 To Winsock1.UBound
            If Winsock1(curindex).State = sckClosed Then
                acceptIndex = curindex
                Exit For
            End If
        Next curindex
    End If
   
    ' Load a channel if none available
    If acceptIndex = -1 Then
        acceptIndex = Winsock1.Count
        Load Winsock1(acceptIndex)
        ReDim Preserve inputBuffer(acceptIndex)
    Else
        inputBuffer(acceptIndex) = ""
    End If
   
    ' Accept connection on Channel decided upon
    Winsock1(acceptIndex).LocalPort = 0
    Winsock1(acceptIndex).Accept requestID
    Winsock1(acceptIndex).SendData "c" & Chr(0) & acceptIndex & Chr(1)
    addMsg Winsock1(0).RemoteHostIP & " Connected on Channel " & acceptIndex & vbCrLf
   
    ' Increase number of Users Connected and notify new Channel
    users = users + 1
    Me.Caption = "Server : " & users & " User(s)"
    Winsock1(acceptIndex).SendData "s" & Chr(0) & users & " User(s) connected" & vbCrLf & Chr(1)
    For curindex = 1 To Winsock1.UBound
        If curindex <> acceptIndex And Winsock1(curindex).State = sckConnected Then
            Winsock1(acceptIndex).SendData "a" & Chr(0) & "Channel " & curindex & Chr(1)
        End If
    Next curindex
   
    ' Notify existing users of new Channel Connection and Total Users
    For curindex = 1 To Winsock1.UBound
        If curindex <> acceptIndex And Winsock1(curindex).State = sckConnected Then
            Winsock1(curindex).SendData "s" & Chr(0) & "Channel " & acceptIndex & " Connected - " & users & " Total User(s)" & vbCrLf & Chr(1)
            Winsock1(curindex).SendData "a" & Chr(0) & "Channel " & acceptIndex & Chr(1)
        End If
    Next curindex
End Sub

Private Sub Winsock1_Close(index As Integer)
    Dim curindex As Integer
   
    ' Channel has disconnected
    Winsock1(index).Close
    addMsg Winsock1(index).RemoteHostIP & " on Channel " & index & " Disconnected" & vbCrLf
   
    ' Decrease Total number of users and notify all other channels of disconnection
    users = users - 1
    Me.Caption = "Server : " & users & " User(s)"
    For curindex = 1 To Winsock1.UBound
        If curindex <> index And Winsock1(curindex).State = sckConnected Then
            Winsock1(curindex).SendData "s" & Chr(0) & "Channel " & index & " Disconnected - " & users & " Total User(s)" & vbCrLf & Chr(1)
            Winsock1(curindex).SendData "d" & Chr(0) & "Channel " & index & Chr(1)
        End If
    Next curindex
   
    ' attempt to release unneeded Winsock Controls from end of array
    Do While Winsock1(Winsock1.UBound).State = sckClosed
        Unload Winsock1(Winsock1.UBound)
    Loop
    ReDim Preserve inputBuffer(Winsock1.UBound)
End Sub

Private Sub Winsock1_DataArrival(index As Integer, ByVal bytesTotal As Long)
    Dim inputString As String
   
    ' add current command to buffer and process the buffer
    Winsock1(index).GetData inputString, vbString
    inputBuffer(index) = inputBuffer(index) & inputString
    processInputs index
End Sub

Private Sub processInputs(index As Integer)
    On Error GoTo nosuchControl
   
    Dim markerPos As Integer
    Dim curInput As String
    Dim curindex As Integer
    Dim values As Variant
    Dim origIndex As Integer
    Dim recipient As Integer
    Dim newMsg As String
   
    ' see if we have a msg in our buffer
    markerPos = InStr(inputBuffer(index), Chr(1))
    Do While markerPos > 0 ' if we have a msg then process it
        curInput = Left$(inputBuffer(index), markerPos - 1) ' get msg from buffer
        inputBuffer(index) = Mid(inputBuffer(index), markerPos + 1) ' remove msg from buffer
       
        values = Split(curInput, Chr(0)) ' split msg into two parts
       
        Select Case values(0)
            Case "p"
                origIndex = CInt(values(1))
                recipient = CInt(values(2))
                If recipient <> origIndex And _
                        recipient > 0 And recipient <= Winsock1.UBound And _
                        Winsock1(recipient).State = sckConnected Then
                    Winsock1(recipient).SendData curInput & Chr(1)
noSuchRecipient:
                End If
           
            Case "send"
                origIndex = CInt(values(1))
                recipient = CInt(values(2))
                If recipient <> origIndex And _
                        recipient > 0 And recipient <= Winsock1.UBound And _
                        Winsock1(recipient).State = sckConnected Then
                                       
                    ' newMsg : recv | from | to | fileName | size
                    newMsg = "recv" & Chr(0) & _
                        values(1) & Chr(0) & values(2) & Chr(0) & _
                        values(3) & Chr(0) & values(4) & Chr(1)
                    Winsock1(recipient).SendData newMsg
                End If
               
            Case Else
                ' send msg to everyone except the originator
                origIndex = CInt(values(0))
                For curindex = 1 To Winsock1.UBound
                    If curindex <> origIndex And Winsock1(curindex).State = sckConnected Then
                        Winsock1(curindex).SendData curInput & Chr(1)
                    End If
                Next curindex
               
        End Select
       
        markerPos = InStr(inputBuffer(index), Chr$(1)) ' any more msgs in buffer?
    Loop
    Exit Sub
   
nosuchControl:
    If Err.Number = 340 Then
        Winsock1(origIndex).SendData "p" & Chr(0) & _
            recipient & Chr(0) & origIndex & Chr(0) & _
            "[No Longer Connected]" & vbCrLf & Chr(1)
        GoTo noSuchRecipient
    Else
        Err.Raise Err.Number
    End If
End Sub

Private Sub addMsg(message As String)
    ' add message to end of our console
    With RichTextBox1
        .SelStart = Len(.Text)
        .SelLength = 0
        .SelText = message
    End With
End Sub
0
 
LVL 13

Author Comment

by:Michael_D
Comment Utility
Thank you for your help. Although now everything is clear to me I would like to see the client code as well.

0
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
The whole program was a demonstration of how to implement a multiuser chat system with a central server like IRC, but also allows private communication between clients.  The client side consists of two forms shown below.  There is also imcomplete code to transfer files between clients.  To make the forms, paste the code sections below into notepad files and save them as .frm files.  Then they can be opened up in VB6 with the controls and layout like I had them.  Sometimes they get errors the first time they are loaded up in the IDE but you can just ignore those and recompile the app once they are added.

Regards,

Idle_Mind

******************************************************************
  First Form
******************************************************************
VERSION 5.00
Object = "{248DD890-BB45-11CF-9ABC-0080C7E7B78D}#1.0#0"; "MSWINSCK.OCX"
Object = "{3B7C8863-D78F-101B-B9B5-04021C009402}#1.2#0"; "RICHTX32.OCX"
Begin VB.Form Form1
   Caption         =   "Form1"
   ClientHeight    =   3765
   ClientLeft      =   60
   ClientTop       =   345
   ClientWidth     =   7080
   LinkTopic       =   "Form1"
   ScaleHeight     =   3765
   ScaleWidth      =   7080
   StartUpPosition =   3  'Windows Default
   Begin VB.ListBox List1
      Height          =   3570
      Left            =   4680
      TabIndex        =   2
      Top             =   120
      Width           =   2295
   End
   Begin VB.TextBox Text1
      Height          =   375
      Left            =   120
      TabIndex        =   1
      Text            =   "Text1"
      Top             =   3240
      Width           =   4455
   End
   Begin RichTextLib.RichTextBox RichTextBox1
      Height          =   3015
      Left            =   120
      TabIndex        =   0
      Top             =   120
      Width           =   4455
      _ExtentX        =   7858
      _ExtentY        =   5318
      _Version        =   393217
      Enabled         =   -1  'True
      ScrollBars      =   3
      TextRTF         =   $"Client.frx":0000
   End
   Begin MSWinsockLib.Winsock Winsock1
      Index           =   0
      Left            =   0
      Top             =   0
      _ExtentX        =   741
      _ExtentY        =   741
      _Version        =   393216
   End
End
Attribute VB_Name = "Form1"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Option Explicit

Private IP As String
Private port As Long
Private inputBuffer As String
Public channel As Integer
Public privateChats As Collection

Private Sub Form_Load()
    Me.Caption = "Client - Connecting..."
    RichTextBox1.Locked = True
    Text1.Text = ""
    Text1.Enabled = False
    IP = "127.0.0.1"
    port = 10001
    Set privateChats = New Collection
   
    ' attemp to connect to the server
    Winsock1(0).Connect IP, port
End Sub

Private Sub Form_Unload(Cancel As Integer)
    Dim f As Form
    Dim w As Integer
   
    For Each f In Forms
        Unload f
    Next
    For w = Winsock1.UBound To 0 Step -1
        Winsock1(w).Close
    Next w
End Sub

Private Sub List1_DblClick()
    On Error GoTo newChat
   
    Dim privateChat As Form2
   
    If List1.ListIndex <> -1 Then
        Set privateChat = privateChats(List1.List(List1.ListIndex))
        If privateChat.WindowState = vbMinimized Then
            privateChat.WindowState = vbNormal
        End If
        privateChat.SetFocus
    End If
    Exit Sub
   
newChat:
    Set privateChat = New Form2
    privateChat.recipient = List1.List(List1.ListIndex)
    privateChats.Add privateChat, privateChat.recipient
    privateChat.Caption = "[Private] Channel " & channel & " to " & privateChat.recipient
    privateChat.Show
End Sub

Private Sub Text1_KeyPress(KeyAscii As Integer)
    If KeyAscii = 13 And Text1.Text <> "" Then
        ' send msg if there is one and user pressed enter
        Winsock1(0).SendData channel & Chr(0) & Text1.Text & vbCrLf & Chr(1)
        addMsg Text1.Text & vbCrLf
        Text1.Text = ""
        KeyAscii = 0
    End If
End Sub


Private Sub Winsock1_Close(Index As Integer)
    If Index = 0 Then
        addMsg "Disconnected from Server" & vbCrLf
        Me.Caption = "Not Connected to Server"
        Text1.Enabled = False
    End If
   
    ' attempt to release unneeded Winsock Controls from end of array
    Do While Winsock1(Winsock1.UBound).State = sckClosed
        Unload Winsock1(Winsock1.UBound)
    Loop
End Sub

Private Sub Winsock1_Connect(Index As Integer)
    If Index = 0 Then
        addMsg "Connected to " & IP & " on Port " & Winsock1(0).RemotePort & vbCrLf
        Text1.Enabled = True
    End If
End Sub

Private Sub Winsock1_ConnectionRequest(Index As Integer, ByVal requestID As Long)
    If Index > 0 Then
        Winsock1(Index).Accept requestID
    End If
End Sub

Private Sub Winsock1_DataArrival(Index As Integer, ByVal bytesTotal As Long)
    Dim inputString As String

    If Index = 0 Then ' Chat communications
        ' add msg to buffer and process it
        Winsock1(0).GetData inputString, vbString
        inputBuffer = inputBuffer & inputString
        processInputs
    Else ' File Transfers
       
    End If
End Sub

Private Sub processInputs()
    Dim markerPos As Integer
    Dim curInput As String
    Dim values As Variant
    Dim curListItem As Integer
    Dim listenIndex As Integer
   
    ' see if we have a msg in our buffer
    markerPos = InStr(inputBuffer, Chr(1))
    Do While markerPos > 0 ' if we have a msg then process it
        curInput = Left(inputBuffer, markerPos - 1) ' get msg from buffer
        inputBuffer = Mid(inputBuffer, markerPos + 1) ' remove msg from buffer
       
        values = Split(curInput, Chr(0)) ' split msg into command and its values
        Select Case values(0) ' what command was it?
            Case "c" ' Channel Notification
                channel = CInt(values(1))
                Me.Caption = "Channel " & channel
                     
            Case "s" ' Server message
                addMsg values(1)
               
            Case "a" ' add channel
                List1.AddItem values(1)
                 
            Case "d" ' delete channel
                For curListItem = 0 To List1.ListCount - 1
                    If List1.List(curListItem) = values(1) Then
                        List1.RemoveItem curListItem
                        Exit For
                    End If
                Next curListItem
               
            Case "p" ' private message
                privateMessage curInput
               
            Case "recv" ' prepare to receive a file
                ' recv | from | to | fileName | size
                listenIndex = setupListen()
                   
               
            Case Else ' msg from another channel
                addMsg "Channel " & values(0) & ": " & values(1)
               
        End Select
        markerPos = InStr(inputBuffer, Chr$(1)) ' any more msgs in buffer?
    Loop
End Sub

Public Sub addMsg(ByVal message As String)
    ' add msg to end of our console
    With RichTextBox1
        .SelStart = Len(.Text)
        .SelLength = 0
        .SelText = message
    End With
End Sub

Private Sub privateMessage(msg As String)
    On Error GoTo newChat
   
    Dim privateChat As Form2
    Dim values As Variant
    values = Split(msg, Chr(0))
       
    Set privateChat = privateChats("Channel " & values(1))
    If privateChat.WindowState = vbMinimized Then
        privateChat.WindowState = vbNormal
    End If
    privateChat.SetFocus
    privateChat.addMsg "Channel " & values(1) & ": " & values(3)
    Exit Sub
   
newChat:
    Set privateChat = New Form2
    privateChat.recipient = "Channel " & values(1)
    privateChats.Add privateChat, privateChat.recipient
    privateChat.Caption = "[Private] Channel " & channel & " to " & privateChat.recipient
    privateChat.Show
    Resume Next
End Sub

Private Function setupListen() As Integer
    Dim curIndex As Integer
    Dim listenIndex As Integer
   
    listenIndex = -1
    For curIndex = 1 To Winsock1.UBound
        If Winsock1(curIndex).State = sckClosed Then
            listenIndex = curIndex
            Exit For
        End If
    Next curIndex
   
    If listenIndex = -1 Then
        listenIndex = Winsock1.Count
        Load Winsock1(listenIndex)
    End If
   
    Winsock1(listenIndex).LocalPort = 0
    Winsock1(listenIndex).Listen
   
    setupListen = listenIndex
End Function

******************************************************************
  Second Form
******************************************************************
VERSION 5.00
Object = "{3B7C8863-D78F-101B-B9B5-04021C009402}#1.2#0"; "RICHTX32.OCX"
Object = "{F9043C88-F6F2-101A-A3C9-08002B2F49FB}#1.2#0"; "comdlg32.ocx"
Object = "{831FDD16-0C5C-11D2-A9FC-0000F8754DA1}#2.0#0"; "MSCOMCTL.OCX"
Begin VB.Form Form2
   Caption         =   "Form2"
   ClientHeight    =   4020
   ClientLeft      =   60
   ClientTop       =   345
   ClientWidth     =   4470
   LinkTopic       =   "Form2"
   ScaleHeight     =   4020
   ScaleWidth      =   4470
   StartUpPosition =   3  'Windows Default
   Begin MSComDlg.CommonDialog CommonDialog1
      Left            =   3000
      Top             =   3480
      _ExtentX        =   847
      _ExtentY        =   847
      _Version        =   393216
   End
   Begin MSComctlLib.ProgressBar xFer
      Height          =   375
      Left            =   0
      TabIndex        =   3
      Top             =   3600
      Visible         =   0   'False
      Width           =   3255
      _ExtentX        =   5741
      _ExtentY        =   661
      _Version        =   393216
      Appearance      =   1
   End
   Begin VB.CommandButton sendFile
      Caption         =   "Send File"
      Enabled         =   0   'False
      Height          =   375
      Left            =   3360
      TabIndex        =   2
      Top             =   3600
      Width           =   1095
   End
   Begin VB.TextBox Text1
      Height          =   375
      Left            =   0
      TabIndex        =   0
      Top             =   3120
      Width           =   4455
   End
   Begin RichTextLib.RichTextBox RichTextBox1
      Height          =   3015
      Left            =   0
      TabIndex        =   1
      Top             =   0
      Width           =   4455
      _ExtentX        =   7858
      _ExtentY        =   5318
      _Version        =   393217
      ReadOnly        =   -1  'True
      ScrollBars      =   3
      TextRTF         =   $"PrivateChat.frx":0000
   End
End
Attribute VB_Name = "Form2"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Option Explicit

Public recipient As String
Public filePathToSend As String
Public fileNameToSend As String
Public fileSize As Long

Private Sub Form_Unload(Cancel As Integer)
    Form1.privateChats.Remove recipient
End Sub

Private Sub Text1_KeyPress(KeyAscii As Integer)
    If KeyAscii = 13 And Text1.Text <> "" Then
        If Form1.Winsock1(0).State = sckConnected Then
            ' send msg if there is one and user pressed enter
            Form1.Winsock1(0).SendData "p" & Chr(0) & Form1.channel & Chr(0) & _
                Mid(recipient, InStrRev(recipient, " ") + 1) & Chr(0) & _
                Text1.Text & vbCrLf & Chr(1)
            addMsg Text1.Text & vbCrLf
            Text1.Text = ""
        Else
            MsgBox "Not Connected to Server"
        End If
        KeyAscii = 0
    End If
End Sub

Private Sub sendFile_Click()
    On Error GoTo cancelled
   
    CommonDialog1.InitDir = App.Path
    CommonDialog1.CancelError = True
    CommonDialog1.ShowOpen
    filePathToSend = Left(CommonDialog1.FileName, InStrRev(CommonDialog1.FileName, "\"))
    fileNameToSend = Mid(CommonDialog1.FileName, Len(filePathToSend) + 1)
    fileSize = FileLen(filePathToSend & fileNameToSend)
    If Form1.Winsock1(0).State = sckConnected Then
        ' send | from | to | fileName | size
        Form1.Winsock1(0).SendData "send" & Chr(0) & Form1.channel & Chr(0) & _
            Mid(recipient, InStrRev(recipient, " ") + 1) & Chr(0) & _
            fileNameToSend & Chr(0) & fileSize & Chr(1)
    Else
        MsgBox "Not Connected to Server"
    End If
cancelled:
End Sub

Public Sub addMsg(ByVal message As String)
    ' add msg to end of our console
    With RichTextBox1
        .SelStart = Len(.Text)
        .SelLength = 0
        .SelText = message
    End With
End Sub


0

Featured Post

Free Trending Threat Insights Every Day

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

Join & Write a Comment

Suggested Solutions

Go is an acronym of golang, is a programming language developed Google in 2007. Go is a new language that is mostly in the C family, with significant input from Pascal/Modula/Oberon family. Hence Go arisen as low-level language with fast compilation…
Since upgrading to Office 2013 or higher installing the Smart Indenter addin will fail. This article will explain how to install it so it will work regardless of the Office version installed.
This tutorial explains how to use the VisualVM tool for the Java platform application. This video goes into detail on the Threads, Sampler, and Profiler tabs.
The viewer will learn how to user default arguments when defining functions. This method of defining functions will be contrasted with the non-default-argument of defining functions.

762 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

13 Experts available now in Live!

Get 1:1 Help Now