LINUX.ORG.RU

Открыть сокет в эксклюзивном режиме

 ,


0

2

Добрый день.

Читаю из неблокирующего сокета с помощью select по таймауту. Всё бы хорошо, но одно «но»: другой процесс может подключиться к сокету и считать данные до моего процесса. Т.е. мы по селекту отработаем, но read ничего не считает (все считанно до нас).

открываю сокет так:

fd = open(port, O_RDWR | O_NOCTTY | O_NDELAY);

и после всех проверок (открылся и всё хорошо)

fcntl(fd, F_SETFL, O_NDELAY);

Какой еще есть флажек? Чтобы иному процессу выдалось желанное «device is busy bla-bla»

★★

Последнее исправление: GreenBag (всего исправлений: 2)

Пробовал добавить O_EXCL, но не помогло. Насколько понял — этот флаг только для ВНОВЬ создаваемых файлов. Для существующих не действителен.

GreenBag ★★
() автор топика

тебя не смущает, что open открывает файл?

anonymous
()

я бы сделал через liblockfile какой-нить. Все эти fcntl и lockf такая ебалайка...

true_admin ★★★★★
()
Последнее исправление: true_admin (всего исправлений: 1)

открываю сокет так:

open

Сынок, ты сокет так в жизни не откроешь.

red_eyed_peguin
()

А покажи-ка весь код :). У меня unix-сокет так не открылся.

Может ты с FIFO каким-нить путаешь?

true_admin ★★★★★
()

другой процесс может подключиться к сокету и считать данные до моего процесса

Где, где отсыпают такой чудной травы ?

MKuznetsov ★★★★★
()
Ответ на: комментарий от MKuznetsov

ok, сейчас покажу весь код. И да, тут всё без травы работает:

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <termios.h>

#define BAUD_RATE B9600

int main (void)
{
    fd_set read_flags,write_flags; // you know what these are
    struct timeval waitd;
    int thefd;             // The socket
    char outbuff[512];     // Buffer to hold outgoing data
    char inbuff[512];      // Buffer to read incoming data into
    int err;          // holds return values
    struct termios options;

    memset(&outbuff,0,sizeof(outbuff)); // memset used for portability
    //opening port
    thefd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY | O_ASYNC);
    if (thefd == -1) {
        // could not open port
        fprintf(stderr,"open_port: Unable to open %s\n", "/dev/ttyS0");
    } else {
        fcntl(thefd, F_SETFL, O_NDELAY);
        tcgetattr(thefd, &options);
        // BAUD rate
        cfsetispeed(&options, BAUD_RATE);
        cfsetospeed(&options, BAUD_RATE);
        // enable the receiver and set local mode...
        options.c_cflag |= (CLOCAL | CREAD);

        // no parity (8 in 1)
        options.c_cflag &= ~PARENB;
        options.c_cflag &= ~CSTOPB;
        options.c_cflag &= ~CSIZE;
        options.c_cflag |= CS8;

        // no flow controll
        //options.c_cflag &= ~CRTSCTS;    /* Also called CRTSCTS */

        // apply all changes immediately
        tcsetattr(thefd, TCSANOW, &options);
    }

    strcat(outbuff,"jarjam\n"); //Add the string jarjam to the output
    //buffer
    while(1) {
        waitd.tv_sec = 1;     // Make select wait up to 1 second for data
        waitd.tv_usec = 0;    // and 0 milliseconds.
        FD_ZERO(&read_flags); // Zero the flags ready for using
        FD_ZERO(&write_flags);
        FD_SET(thefd, &read_flags);
        if(strlen(outbuff)!=0) FD_SET(thefd, &write_flags);
        err=select(thefd+1, &read_flags,&write_flags,
                (fd_set*)0,&waitd);
        if(err < 0) continue;
        if(FD_ISSET(thefd, &read_flags)) { //Socket ready for reading
            FD_CLR(thefd, &read_flags);
            memset(&inbuff,0,sizeof(inbuff));
            if (read(thefd, inbuff, sizeof(inbuff)-1) <= 0) {
                close(thefd);
                break;
            }
            else printf("%s",inbuff);
        }
        if(FD_ISSET(thefd, &write_flags)) { //Socket ready for writing
            FD_CLR(thefd, &write_flags);
            write(thefd,outbuff,strlen(outbuff));
            memset(&outbuff,0,sizeof(outbuff));
        }
        // now the loop repeats over again
    }
}
GreenBag ★★
() автор топика
Ответ на: комментарий от GreenBag

эээ, чувак, это не сокеты, это последовательный порт. Сокет это то что man socket

Я хз будет ли fcntl так работать на файлы устройств, но, раз уж у тебя там в коде заморочки о портабельности, юзай лок-файлы.

true_admin ★★★★★
()

И параллельно еще вопрос: все это дело работает, если я, скажем сделаю

echo "test" > /dev/ttyUSB0
(другой аппарат подключен к /dev/ttyS0 через USB2Serial convertor). На выходе получаю: test и 0x0А (перевод строки). Но, если скажем, я буду слать через какой-нить скрипт, ну например:
#!/usr/bin/env python3

import serial

cmd1_state = b'\x3E\x01\x00\x01'

#Selecting serial port for commands to be sent --> /dev/ttyUSB0
serial_0 = serial.Serial('/dev/ttyUSB2');
print("Using serial port ", serial_0.portstr);
serial_0.write(cmd1_state)
         
# closing serial port
serial_0.close()

То данные будут «где-то» накапливаться и там «сидеть», пока я опять что-нибудь не пошлю через консоль и echo. И тогда все данные сразу «выплюнутся».

Как делать так, чтобы порт таки реагировал всегда на приход новых данных?

GreenBag ★★
() автор топика
Ответ на: комментарий от true_admin

Да вроде заморочек и нет никаких — будет все работать только под POSIX OS, а точнее под Linux.

GreenBag ★★
() автор топика
Ответ на: комментарий от zloy_starper

Открыть *Файл устройства* в эксклюзивном режиме

Согласен, возможно напутал с терминологией.

GreenBag ★★
() автор топика
Ответ на: комментарий от GreenBag

man buffered io
flush какой-нить попробуй или отключи буферизацию

aol ★★★★★
()

Открыть *Файл устройства* в эксклюзивном режиме
Согласен, возможно напутал с терминологией.

«к чёрту подробности», use-case приведи где «Читаю из неблокирующего сокета с помощью select по таймауту. Всё бы хорошо, но одно „но“: другой процесс может подключиться к сокету/dev/ttyS0 и считать данные до моего процесса»

пока видны только теоретические измышления и борьба с вымышленными мельницами.

MKuznetsov ★★★★★
()
Последнее исправление: MKuznetsov (всего исправлений: 1)
Ответ на: комментарий от MKuznetsov

Ну вот и пример: собирете вышеуказанный код. Запустите его. Будет, скажем, слушать /dev/ttyS0. Пишем вручную (через то же echo )периодически что-нибудь в устройство, подключенное, через /dev/ttyS1 к нему. Все ок. Теперь параллельно делаем watch -n cat /dev/ttyS0 и опять пишем в /dev/ttyS1 (если нету реальных портов всё можно через socat заэмулировать). В итоге в нашем коде срабатывает select, переходит на read, и считывает 0 символов.

GreenBag ★★
() автор топика
Ответ на: комментарий от GreenBag

Что-то мне подсказывает что если в драйвере такой функции нет то никак не залочишь.

Сам fcntl умеет только advisory locking (про шаманства с -o mand читай в мане и гугле). Advisory означает что если софтина не знает о блокировках то ничто её не остановит.

true_admin ★★★★★
()
Ответ на: комментарий от GreenBag

с этого примера и надо было начинать :)

watch _не_открывает_ ваш /dev/ttyS0. Он создает и использует /dev/snpXX связанный с вашим терминалом. А событие которое вы поймали прочитав 0 байт - возможно это недоработка/фича в реализации snp (видимо при открытии связного snp девайса ядро «выталкивает» (flush`ит) все данные из очередей связанного терминала).

Но в любом случае на уровне приложения вы никак не запретите ядру создавать snp устройства.

MKuznetsov ★★★★★
()
Ответ на: комментарий от MKuznetsov

Он создает и использует /dev/snpXX связанный с вашим терминалом

ты путаешь watch из линуха с фрибсдшным. В линухе watch просто периодически запускает заданную комманду

true_admin ★★★★★
()
Ответ на: комментарий от MKuznetsov

Ух, таких тонкостей я не знал. спасибо.

Кстати, такая проблема — для открытия устройства в неблокирующем режиме нужно использовать (как показал Google) fcntl(fd, F_NOCACHE). По крайней мере в этом http://www.manpagez.com/man/2/fcntl/ мане такая опция есть, но у меня (Ubuntu 12.04 LTS) нету! И соответственно

error: ‘F_NOCACHE’ undeclared (first use in this function)

хотя fcntl.h и unistd.h включены. У вас есть F_NOCACHE?

GreenBag ★★
() автор топика
Ответ на: комментарий от true_admin

согласен. смутило отсутствие аргумента у -n («watch -n cat /dev/ttyS0» в линукс просто не должен работать)

MKuznetsov ★★★★★
()
Ответ на: комментарий от GreenBag

error: ‘F_NOCACHE’ undeclared (first use in this function)

логично. Это опять-же не про Linux, да и что там кешировать в асинхронной линии-то ?

p.s. асинхронный порты программировал ну очень давно, но из памяти всплывает, что в select надо использовать все три набора дескрипторов и на всякую странную ситуацию (событий по 3-му набору, 0 байт на чтение, таймауты и прочая-прочая) надо проверять состояние цепей.

p.p.s. у вас реально получается запустить _ДВА_ экземпляра своей программы и они оба будут считывать данные, кто-вперёд ??

MKuznetsov ★★★★★
()
Ответ на: комментарий от MKuznetsov

Спасибо, было б интересно посмотреть как оно у Вас работает. У меня, как я сказал — кто-то один из двух процессов рандомно перехватывает.

А по поводу неблокированя я так и не понял. Что заставляет Select сработать тогда, когда в порту появится символ перевода строки? Почему оно где-то копит все символы, пока не получит этот признак? И как от этого избавиться?

GreenBag ★★
() автор топика
Ответ на: комментарий от GreenBag

Кому интересно, это помогло:

/* set input mode (non-canonical, no echo,...) */
        newtio.c_lflag = 0;
         
        newtio.c_cc[VTIME]    = 0;   /* inter-character timer unused */
        newtio.c_cc[VMIN]     = 1;   /* blocking read until 1 char received */
        
        tcflush(fd, TCIFLUSH);

Взято отсюда: http://tldp.org/HOWTO/Serial-Programming-HOWTO/x115.html

Вообщем, это вроде как «тонкая» подстройка fd для select

GreenBag ★★
() автор топика
Ответ на: комментарий от GreenBag

Что заставляет Select сработать тогда, когда в порту появится символ перевода строки

это не select так срабатывает, а терминал в каноническом режиме. Пока нечто (перевод строки, сигналы цепей, таймеры, наполнение) не заставит терминал выплюнуть данные, он их будет держать в буфере и не отдаст :) Переведите терминал в raw режим и будет щастье (или около того)

MKuznetsov ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.