Модераторы: Akina
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Многопользовательская БД, блокировка одновременного доступа к запи 
:(
    Опции темы
mihanik
  Дата 5.5.2006, 10:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


-=Белый Медведь=-
****


Профиль
Группа: Комодератор
Сообщений: 4054
Регистрация: 24.4.2006
Где: г. Тверь

Репутация: 11
Всего: 109



Привет!

Описание проблемы.
Нужно, чтобы к одной БД по локальной сети обращались пользователи на чтение/запись.
Как проследить за тем, чтобы они одновременно не получали доступ к одной и той же записи в БД?
Т.е. пока один запись читает/пишет, другой бы туда ничего не писал и не читал.

 smile 

Использую плоскую базу данных dBASE 5.0; (JET, Dynaset).

PS.
Локально всё работает, но нужно выходить в сеть... 


--------------------
Программистами не рождаются, - это родовая травма...
user posted imageuser posted image
PM MAIL WWW ICQ   Вверх
Akina
Дата 5.5.2006, 11:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Советчик
****


Профиль
Группа: Модератор
Сообщений: 20581
Регистрация: 8.4.2004
Где: Зеленоград

Репутация: 34
Всего: 454



Цитата(mihanik @  5.5.2006,  11:58 Найти цитируемый пост)
Использую плоскую базу данных dBASE 5.0; (JET, Dynaset).

Файловые БД не поддерживают механизма блокировок. Решение лежит в области ручного обеспечения блокировки.

В структуру файла вводится дополнительное поле LockerID. Если запись не блокирована, в ней содержится ноль, если там -1, она блокирована системно (административно), любое другое значение - это ID клиента, заблокировавшего запись. При сетевой работе наиболее удобно делать ID 64-битным и составлять его из МАС-адреса интерфейса (48 бит) и штампа времени запуска приложения или каким-либо образом формируемого на рабстанции номера сеанса (16 бит). При этом решается как проблема взаимных блокировок приложений с различных станций, так и нескольких экземпляров с одной станции (уникальность номера сеанса легко реализуется через мьютексы).

Технология блокировок проста. Необходимые для работы записи блокируются попыткой записи своего ID вместо нулевого. После чего конечное состояние проверяется чтением, и если запись своего ID была успешна, с этой записью можно работать. По завершении работы LockerID опять обнуляется - запись отпускается.

Зависшие блокировки снимаются как приложением с клиентской станции (выбираются все LockerID, относящиеся к этой станции, по МАС, после чего проверяется наличие мьютексов сеанса и обнуление всех ID для несуществующих на станции мьютексов), так и административно (обнуляются все LockerID, для которых не найдены МАС в списке текущих соединений сервера).

Однако это все в корне неправильно. Следует перейти на клиент-серверную БД, где механизм блокировок уже реализован.  


--------------------
 О(б)суждение моих действий - в соответствующей теме, пожалуйста. Или в РМ. И высшая инстанция - Администрация форума.

PM MAIL WWW ICQ Jabber   Вверх
mihanik
Дата 5.5.2006, 12:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


-=Белый Медведь=-
****


Профиль
Группа: Комодератор
Сообщений: 4054
Регистрация: 24.4.2006
Где: г. Тверь

Репутация: 11
Всего: 109



Едею более или менее понял.
Спасибо!

Немного непонятно, что такое "мьютекс"...

 

Это сообщение отредактировал(а) mihanik - 5.5.2006, 12:43


--------------------
Программистами не рождаются, - это родовая травма...
user posted imageuser posted image
PM MAIL WWW ICQ   Вверх
vbCrLf
Дата 5.5.2006, 12:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 54
Регистрация: 3.5.2006
Где: Севастополь

Репутация: 1
Всего: 1



Если бы я реализовал это на VB, то у меня вышла бы следующая картина:

На машине с базой висит простенький сервер, который принимает заказы на чтение/запись в базе,
и поочередно, по мере поступления таких заказов, выполняет их. ,) (т.е. только он работает с базой)
Соотвественно приложение-клиент прилагается.
  

Это сообщение отредактировал(а) vbCrLf - 5.5.2006, 12:48
PM MAIL ICQ   Вверх
mihanik
Дата 5.5.2006, 13:46 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


-=Белый Медведь=-
****


Профиль
Группа: Комодератор
Сообщений: 4054
Регистрация: 24.4.2006
Где: г. Тверь

Репутация: 11
Всего: 109



Цитата

простенький сервер, который принимает заказы на чтение/запись в базе


Какой, например?
Посоветуй!

 


--------------------
Программистами не рождаются, - это родовая травма...
user posted imageuser posted image
PM MAIL WWW ICQ   Вверх
vbCrLf
Дата 5.5.2006, 14:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 54
Регистрация: 3.5.2006
Где: Севастополь

Репутация: 1
Всего: 1



Ну в смысле написал бы на VB. Или у меня такое впечатление, что твой вопрос вообще не касается Visual Basic'a... 
PM MAIL ICQ   Вверх
mihanik
Дата 5.5.2006, 14:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


-=Белый Медведь=-
****


Профиль
Группа: Комодератор
Сообщений: 4054
Регистрация: 24.4.2006
Где: г. Тверь

Репутация: 11
Всего: 109



Цитата

Ну в смысле написал бы на VB.


Это понятно, что на VB. smile 

Я БД на VB программирую всего 3 недели...
Каким образом будут общаться клиент и сервер? smile 

I have no idea...
 


--------------------
Программистами не рождаются, - это родовая травма...
user posted imageuser posted image
PM MAIL WWW ICQ   Вверх
vbCrLf
Дата 5.5.2006, 17:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 54
Регистрация: 3.5.2006
Где: Севастополь

Репутация: 1
Всего: 1



Цитата

Каким образом будут общаться клиент и сервер?


Во-первых, активно юзаем Microsoft Winsock Control 6.0

Серверная часть:
Кидаем winsock на форму и называем его например просто W (Ненавижу длинные имена объектов и переменных, но это сугубо мое предпочтение).
В свойствах ставим этому контролу индекс 0.
Всё. дальше пришем код.

Код

Private Sub Form_Load()

    Start
     MsgBox "Сервер запущен. IP адрес: " & W(0).LocalIP & ";    Порт: " & W(0).LocalPort   ' для наглядности ,)

End Sub

Private Sub Start()   

    Dim MyPort as integer    
    
    MyPort=1777                    'Порт, через который будут цепляться клиенты
    W(0).LocalPort = MyPort   
    W(0).Listen                      'Открытие порта для ожидания подключений

End Sub

Private Sub W_ConnectionRequest(Index As Integer, ByVal requestID As Long)  'запрос соединения
    
    On Error Resume Next
    Dim CnctInd As Long, Cnt As Boolean
    Cnt = False
    For CnctInd = 1 To W.count - 1 'Проверяем все существующие контролы и ищем свободный
        If W(CnctInd).State = sckClosed Then
            Cnt = True
            Exit For
        End If
    Next CnctInd
    If Cnt = False Then               ' если свободного не нашли
        ConnectionIndex = W.count
        Load W(ConnectionIndex)    ' создаем новый
    End If
    Call W(ConnectionIndex).Accept(requestID&)

End Sub

Private Sub W_DataArrival(Index As Integer, ByVal bytesTotal As Long) 'пришло сообщение от клиента!

    Dim MSG As String

    W(Index).GetData MSG  'читаем входящее сообщение в переменную MSG 
    Action MSG, Index          'и передаем его вместе с индексом контрола в нашу функцию, которая будет его обрабатывать

End Sub

Private Sub Action(MSG As String, indx As Integer)  'Выполняем действия по инструкции в сообщении!
                    'Здесь я думаю ограничителем может слудить только фантазия разработчика
    On Error Resume Next
    Dim CMD As String
    Dim ARG As String
    
    CMD=Left$(MSG,4)                         'Разделяем сообщение на две части: На команду,
    ARG=Right$(MSG,Len(MSG)-4)       'и на аргумент
    Select Case UCase(CMD)              

        Case "QT_#":                            'Оп... получена команда отключения клиента...
            W(indx).Close                        'Ну и отключаем его нафик

        Case "GTR#":                            'Команда: получить значение ячейки
            Dim Value As String
            Value = GetRecord(Arg)         'Вызываем соответствующую функцию
            W(Indx).SendData (Value)    'Отправляем значение ячейки клиенту

        Case "CHR#":                            'Команда: Изменить содержимое ячейки
            ChRecord(Arg)                      'Вызываем соответствующую функцию

    End Select

End Sub

Private Function GetRecord(Arg As String) As String

             'Допустим в нашей базе данных Несколько таблиц, строки и столбики которых просто пронумерованы
    Dim Table As String
    Dim Xrec As String
    Dim Yrec As String

              'Буквально разделываем строку, получая необходимые значения
    Table = LTrim$(Left$(Arg,20))      'Получаем имя таблицы в базе
    XRec = LTrim$(Mid$(Arg,21,4))     'Получаем номер строки
    XRec = LTrim$(Right$(Arg,4))       'Получаем номер колонки
                     
              'Имея столь важные данные как имя таблицы, имя строки и имя столбца, мы можем
              'теперь извлеч значение ячейки, использую Вашу функцию, которая будет называться
              'например GetRecFromBase(Table As String, Xrec As String, Yrec As String) As String
    GetRecord = GetRecFromBase(Table, Xrec, Yrec)      'Вызываем Вашу функцию и получаем содержимое ячейки
 
End Sub

Private Sub ChRecord(Arg As String)

             'Допустим в нашей базе данных Несколько таблиц, строки и столбики которых просто пронумерованы
    Dim Table As String
    Dim Xrec As String
    Dim Yrec As String
    Dim Value As String

              'Буквально разделываем строку, получая необходимые значения
    Table = LTrim$(Left$(Arg,20))      'Получаем имя таблицы в базе
    XRec = LTrim$(Mid$(Arg,21,4))     'Получаем номер строки
    XRec = LTrim$(Mid$(Arg,25,4))     'Получаем номер колонки
    XRec = LTrim$(Right$(Arg,Len(Arg-28)))     'Получаем данные, которые будем записывать в ячейку
                     
              'Имея столь важные данные как имя таблицы, имя строки и имя столбца, мы можем
              'теперь извлеч значение ячейки, использую Вашу функцию, которая будет называться
              'например ChangeRecFromBase(Table As String, Xrec As String, Yrec As String, NewValue As String)
    ChangeRecFromBase(Table, Xrec, Yrec, Value)      'Вызываем Вашу функцию и записываем новое значение в ячейку

End Sub

Так... с этим вроде всё понятно. Включив фантазию, здесь можно наворотить довольно много.

А теперь...
Клиентская часть:
Опять кидаем контрол Winsock на форму, называем его W и пишем код.
Да, самое главное! В глобале обьявляем две переменные:
Dim Wait As Boolean
Dim inMSG As String

Онb нам пригодятся
Код

Private Sub Form_Load()

    Wait = False
    inMSG = ""
    Start

End Sub

Private Sub Start() 

    Dim ServerIP as String           'Айпи адрес или ДНС-имя компьютера, на котором работает сервер
    Dim ServerPort as String       '...и, соответственно, порт
    
    ServerIP = "10.0.0.111"
    ServerPort = "1777"

    W.Connect ServerIP, ServerPort   'пытаемся подцепиться к серверу

End Sub

Private Sub W_DataArrival(Index As Integer, ByVal bytesTotal As Long) 'пришло сообщение от сервера!

    Dim MSG As String

    W(Index).GetData inMSG  'читаем входящее сообщение в нашу глобальную переменную inMSG 
    Wait = False                      ' а это нужно, чтобы нижеследующая функция поняла, что пришёл ответ

End Sub

Private Sub W_Connect() 'Событие, вызываемое удачным подключением к серверу
        ' Ура! Мы подключились к серверу! теперь недоллго думая, мы можем что-нибудь сделать.
        ' К примеру наша программа подключается к серверу, и её интересует таблица Boobles.
        ' В этой таблице, в 4-ой строке в 7-ом столбике есть некое значение, которое хотелось бы
        ' увеличить в 16 раз!!!  Что мы сейчас и сделаем.

    Dim Table As String
    Dim Xrec As Integer
    Dim Yrec As Integer
    Dim Value As String
    Dim Str4Send As String

    Table = "Boobles"
    Xrec = "4"
    Yrec = "7"

        ' Сначала мы узнаем интересуемое нас значение!
        ' Для начала подготовим строку запроса серверу:
    Str4Send="GTR#" & Space(20-len(Table)) & Table & Space(4-len(Xrec)) & Xrec & Space(4-len(Yrec)) & Yrec
        ' На имя таблицы даем 20 символов, и заполняем оставшееся пространство пробелами
        ' На имя строки/столбца даем по 4 символа
    Wait = True   ' Включаем переменную ождидания ответа, при входящем сообщении переменная выключается
                         ' и программа понимает, что пришёл ответ
    W.SendData (Str4Send)    ' Отправляем нашу строку серверу
    Do While Wait =True
        Doevents
    Loop
    Value = inMSG    'Ответ от сервера пришёл в переменную inMSG, и мы его забираем в Value
        ' Value - это есть ни что иное, как содержимое нашей любимой ячейки!
        ' Остается умножить это значение на 16...
   Value = (Val(Value))*16
        ' ...сформировать строку для сервера...
    Str4Send="CHR#" & Space(20-len(Table)) & Table & Space(4-len(Xrec)) & Xrec & Space(4-len(Yrec)) & Yrec & Value
        ' ...и отправить серверу:
    W.SendData (Str4Send) 

End Sub



Вот вроде и всё... Код писал не в VB, возможны ошибки
Так же есть спецефические особенности контрола, их придёться обходить руками.
Но суть я думаю понятна.  

Это сообщение отредактировал(а) vbCrLf - 5.5.2006, 17:50
PM MAIL ICQ   Вверх
mihanik
Дата 6.5.2006, 07:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


-=Белый Медведь=-
****


Профиль
Группа: Комодератор
Сообщений: 4054
Регистрация: 24.4.2006
Где: г. Тверь

Репутация: 11
Всего: 109



Изящно... Изящно...

На следующей неделе буду копать в этом направлении...

Спасибо!
 


--------------------
Программистами не рождаются, - это родовая травма...
user posted imageuser posted image
PM MAIL WWW ICQ   Вверх
boevik
Дата 6.5.2006, 10:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник Клуба
Сообщений: 1452
Регистрация: 31.5.2004
Где: Израиль

Репутация: 12
Всего: 35



А может не будем огород городить и прислушаемся к совету Akina 
Цитата(Akina @  5.5.2006,  11:52 Найти цитируемый пост)
Однако это все в корне неправильно. Следует перейти на клиент-серверную БД, где механизм блокировок уже реализован. 

Поставь нормальную СУБД, типа SQL Server, и он уж позаботится об блокировках, чтении записей и т.д.

 


--------------------
Никогда не говори никогда
PM MAIL WWW   Вверх
vbCrLf
Дата 6.5.2006, 10:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 54
Регистрация: 3.5.2006
Где: Севастополь

Репутация: 1
Всего: 1



Как всегда.... smile
Погрузившись в написание, забыл о самом главном.
Недостаток кода в том, что во время его выполнения всё-равно не соблюдается ограничение одновременного доступа к ячейке...
Вот предлагаю к рассмотрению модифицированный код серверной части программы.
В глобале объявляем такой массив и парочку переменных:
Dim Ops(99) As String
Dim inOperation As Boolean
Dim NewCMD As Boolean

И далее....
Код

Private Sub Form_Load()
    Dim A As Integer

    For A = 0 to 99
        Ops(A) = ""        'Обнуляем очередь команд
    Next A
    inOperation = False 'Говорим программе, что она не выполняет никаких команд от клиента
    Start
    MsgBox "Сервер запущен. IP адрес: " & W(0).LocalIP & ";    Порт: " & W(0).LocalPort   ' для наглядности ,)
End Sub
Private Sub Start()   
    Dim MyPort as integer    
    
    MyPort=1777                    'Порт, через который будут цепляться клиенты
    W(0).LocalPort = MyPort   
    W(0).Listen                      'Открытие порта для ожидания подключений
End Sub
Private Sub W_ConnectionRequest(Index As Integer, ByVal requestID As Long)  'запрос соединения
    
    On Error Resume Next
    Dim CnctInd As Long, Cnt As Boolean
    Cnt = False
    For CnctInd = 1 To W.count - 1 'Проверяем все существующие контролы и ищем свободный
        If W(CnctInd).State = sckClosed Then
            Cnt = True
            Exit For
        End If
    Next CnctInd
    If Cnt = False Then               ' если свободного не нашли
        ConnectionIndex = W.count
        Load W(ConnectionIndex)    ' создаем новый
    End If
    Call W(ConnectionIndex).Accept(requestID&)
End Sub
Private Sub W_DataArrival(Index As Integer, ByVal bytesTotal As Long) 'пришло сообщение от клиента!
    Dim MSG As String
    Dim C As Integer
    W(Index).GetData MSG  'читаем входящее сообщение в переменную MSG 
    For C=0 to 99
        If Ops(C)="" then Ops(C) = MSG & Space(3-len(indx)) & Index: Exit For   'и передаем его вместе с индексом контрола в очередь
    Next C
    NewCmd = True
    if inOperation = False then Process ' Если операций никаких не выполняется, то выполнить их ,)
End Sub
Private Sub Process()   'Поочередно выполняем все команды клиента
    Dim C As Integer
    Dim Msg As String
    Dim Indx As Integer

    inOperation=True

    Do While NewCmd = True
        NewCmd = False
        For C=0 to 99
            Ops(C)<>"" Then
                Msg=Left$(Ops(c),Len(Ops(C))-3)
                Indx=Val(LTrim$(Right$(Ops(C),3)))
                Action Msg, Indx            'Вызываем функцию, которая выполняет инструкцию клиента
                Ops(C)=""
            End If
        Next C
    Loop
    inOperation=False
End Sub
Private Sub Action(MSG As String, Indx As Integer)  'Выполняем действия по инструкции в сообщении!
                    'Здесь я думаю ограничителем может слудить только фантазия разработчика
    On Error Resume Next
    Dim CMD As String
    Dim ARG As String
    
    CMD=Left$(MSG,4)                         'Разделяем сообщение на две части: На команду,
    ARG=Right$(MSG,Len(MSG)-4)       'и на аргумент
    Select Case UCase(CMD)              
        Case "QT_#":                            'Оп... получена команда отключения клиента...
            W(indx).Close                        'Ну и отключаем его нафик
        Case "GTR#":                            'Команда: получить значение ячейки
            Dim Value As String
            Value = GetRecord(Arg)         'Вызываем соответствующую функцию
            W(Indx).SendData (Value)    'Отправляем значение ячейки клиенту
        Case "CHR#":                            'Команда: Изменить содержимое ячейки
            ChRecord(Arg)                      'Вызываем соответствующую функцию
    End Select
End Sub
Private Function GetRecord(Arg As String) As String
             'Допустим в нашей базе данных Несколько таблиц, строки и столбики которых просто пронумерованы
    Dim Table As String
    Dim Xrec As String
    Dim Yrec As String
              'Буквально разделываем строку, получая необходимые значения
    Table = LTrim$(Left$(Arg,20))      'Получаем имя таблицы в базе
    XRec = LTrim$(Mid$(Arg,21,4))     'Получаем номер строки
    XRec = LTrim$(Right$(Arg,4))       'Получаем номер колонки
                     
              'Имея столь важные данные как имя таблицы, имя строки и имя столбца, мы можем
              'теперь извлеч значение ячейки, использую Вашу функцию, которая будет называться
              'например GetRecFromBase(Table As String, Xrec As String, Yrec As String) As String
    GetRecord = GetRecFromBase(Table, Xrec, Yrec)      'Вызываем Вашу функцию и получаем содержимое ячейки
 
End Sub
Private Sub ChRecord(Arg As String)
             'Допустим в нашей базе данных Несколько таблиц, строки и столбики которых просто пронумерованы
    Dim Table As String
    Dim Xrec As String
    Dim Yrec As String
    Dim Value As String
              'Буквально разделываем строку, получая необходимые значения
    Table = LTrim$(Left$(Arg,20))      'Получаем имя таблицы в базе
    XRec = LTrim$(Mid$(Arg,21,4))     'Получаем номер строки
    XRec = LTrim$(Mid$(Arg,25,4))     'Получаем номер колонки
    XRec = LTrim$(Right$(Arg,Len(Arg-28)))     'Получаем данные, которые будем записывать в ячейку
                     
              'Имея столь важные данные как имя таблицы, имя строки и имя столбца, мы можем
              'теперь извлеч значение ячейки, использую Вашу функцию, которая будет называться
              'например ChangeRecFromBase(Table As String, Xrec As String, Yrec As String, NewValue As String)
    ChangeRecFromBase(Table, Xrec, Yrec, Value)      'Вызываем Вашу функцию и записываем новое значение в ячейку
End Sub

 
PM MAIL ICQ   Вверх
vbCrLf
Дата 6.5.2006, 12:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 54
Регистрация: 3.5.2006
Где: Севастополь

Репутация: 1
Всего: 1



Простой пример создания сетевых клиент-сервер приложений на базе компонента MS WinSock Control 6.0
В данном примере реализуется передача текстовой строки серверу и получение строки от сервера. 

Присоединённый файл ( Кол-во скачиваний: 14 )
Присоединённый файл  eXamle.zip 4,76 Kb
PM MAIL ICQ   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "VB6"
Akina

Запрещается!

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делиться вскрытыми компонентами

  • Литературу по VB обсуждаем здесь
  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы по реализации алгоритмов рассматриваются здесь
  • Используйте теги [code=vb][/code] для подсветки кода. Используйтe чекбокс "транслит" (возле кнопок кодов) если у Вас нет русских шрифтов.


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Akina.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | VB6 | Следующая тема »


 




[ Время генерации скрипта: 0.0694 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.