Страницы

Поиск по вопросам

Показаны сообщения с ярлыком сокет. Показать все сообщения
Показаны сообщения с ярлыком сокет. Показать все сообщения

понедельник, 13 апреля 2020 г.

Java socket: зависает соединение

#java #сокет #cpp #linux

                    
Пишу код на Java, код прикреплю ниже, там же комментарии в местах, где зависает соединение.
По документации в c =in.read(buffer); должно лежать -1 при завершении передачи. Сервер
завершает передачу файла и запускает поток на чтение, но клиент упорно ждет данных.
Клиент на java.
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;

public class foto_url {
    public static void main(String args[]) throws IOException
    {int serverPort = 5000; // здесь обязательно нужно указать порт к которому привязывается
сервер.
            String address = "54.228.232.96";
            InetAddress ipAddress = InetAddress.getByName(address); // создаем объект
который отображает вышеописанный IP-адрес
            Socket socket = new Socket(ipAddress, serverPort); // создаем сокет используя
IP-адрес и порт сервера.
            // Берем входной и выходной потоки сокета,теперь можем получать и отсылать
данные клиентом. 
         OutputStream out = socket.getOutputStream(); 
        InputStream in = socket.getInputStream();
        System.out.println("download");
        downloadFiles(in,"new11", 1024);
        System.out.println("upload");
        uploadFiles(out,"new11", 1024);
        System.out.println("download");
        downloadFiles(in,"zzx", 1024); 
        socket.close();
    }
    public static void downloadFiles(InputStream in,String strPath, int buffSize) {
        try {

            // Конвертируем потоки в другой тип, чтоб легче обрабатывать текстовые
сообщения.
            OutputStream writer = new FileOutputStream(strPath);
            byte buffer[] = new byte[buffSize];
            int c = in.read(buffer);
            while (c > 0) {
                writer.write(buffer, 0, c);
              //c= in.available(); //При таком варианте тоже зависает
               //   in.read(buffer);
                c =in.read(buffer);//В этом месте зависает.
                System.out.println("c="+c);
            }

            writer.flush();
            writer.close();

        } catch (IOException e) {
            System.out.println(e);
        }
    }

    public static void uploadFiles(OutputStream out,String strPath, int buffSize) {
        try {

            // Конвертируем потоки в другой тип, чтоб легче обрабатывать текстовые
сообщения.
            InputStream read = new FileInputStream(strPath);
            byte buffer[] = new byte[buffSize];
            int c = read.read(buffer);
            while (c>0) {
                out.write(buffer);
                c =read.read(buffer);
            }
            out.flush();
            read.close();
        } catch (IOException e) {
            System.out.println(e);
        }
    }
}

сервер на с++ под линукс.
 #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    using namespace std;
    void send_pic(int sock, char *path);
    void get_pictures(int sock,char *name);
    int  new_server()//запускает основаной порт и экстренный порт
    {
        int listener;
        struct sockaddr_in addr;
        char buf[1024];
        int bytes_read; 
        int sock;
        addr.sin_family = AF_INET;
        addr.sin_port = htons(5000);
        addr.sin_addr.s_addr = INADDR_ANY;

        listener = socket(AF_INET, SOCK_STREAM, 0);
        if(listener < 0)
        {
            perror("socket");
            exit(1);
        }

        if(bind(listener, (struct sockaddr *)&addr, sizeof(addr)) < 0)
        {
            perror("bind");
            close( listener );
            exit(2);
        }

        listen(listener, 1);

        while(1)
        {   int sesID=-1;
            sock = accept(listener, NULL, NULL);
            if(sock < 0)
            {
                perror("accept");
                exit(3);
            }
            switch(fork())
            {
            case -1:
                perror("fork");
                break;

            case 0:
                close(listener);
                send_pic(sock,"/home/ec2-user/pic.gif");
                get_pictures(sock,"pic/ololo.gif");
                send_pic(sock,"pic/ololo.gif");
                close(sock);
                _exit(0);

            default:
                close(sock);
            }

        }
       close(listener);
    }

    void send_pic(int sock, char *path)
    {
        cout<<"\nsend_picture"<


Ответы

Ответ 1



Чтение из сокета и запись в сокет - всегда в разных потоках. Логика пакетов делается проще - отправляйте первыми 2 или 4 байтами размер (в байтах) контента. Дальше всё просто: читаем 2(или 4) байта в цикле (сохраняем в переменную, к примеру, _len), как только что-то прочитали - заполняем буфер кусками из сокета, пока не получили длину буфера равной _len. Всё, пакет передан. Начинаем с начала. Читать не обязательно кусками, но нужно проверять на доступность порции в буфере сокета (на случай лагов в сети).

суббота, 11 апреля 2020 г.

Возникновение сигнала SIGPIPE (ошибка EPIPE) при обращении из браузера Android устройства

#cpp #android #сокет #http #tcp_ip

                    
Написал демон на С++ (Линукс). Он слушает запросы от устройства на Android. 

Причем сделал универсально: в браузере на Android в адресной строке указывается IP-адрес/сайт
(на котором слушает демон), и сам демон посылает в ответ файл, который сохраняется
в браузере.

Использую неблокирующие сокеты, TCP и т.д.

Спустя некоторое время после начала отправки файла приходит ошибка EPIPE (errno =
32 - Broken pipe). После этой ошибки я закрываю сокет.

Не знаю как будет работать на других Android телефонах, но мой делает повторный запрос
и скачивает файл со второго раза без ошибки EPIPE. Боюсь, что на других телефонах файл
просто не скачается.

Если я проделываю то же самое со своего компьютера, то никаких ошибок EPIPE не возникает.

Хотелось бы разобраться почему так происходит.
    


Ответы

Ответ 1



Ошибка EPIPE возвращается обычно в том случае, если данный сокет никем не читается, тоесть нет ни одного процесса, имеющего открытый на чтение дескриптор, связанный с этим сокетом. Возможна ситуация, что Ваш клиент на Андроид по каким-то причинам закрыл дескриптор на чтение, связанный с этим сокетом, тогда, по идее, Вы должны были бы в демоне получить сигнал SIGPIPE и как-то обработать эту ситуацию. Если же вы игнорируете SIGPIPE, то вызов write() обязан вернуть вам EPIPE.

Ответ 2



Разобрался... Потратил кучу времени, чтобы выяснить - просто так на моём китайском телефоне (хотя вроде хуавей хвалят) работает браузер... в нете попадается информация, что стандартный загрузчик на Андроиде (особенно ниже 3.х) с ошибками скачивает файлы, и предлагается установить альтернативный браузер со встроенным менеджером загрузок, может Вам тоже этот подход попробовать? – margosh

Ответ 3



Эта ошибка возникает на всех линуксах, при работе с сокетами. Лечится вызовом signal(SIGPIPE, SIG_IGN);

четверг, 9 апреля 2020 г.

Помогите разобраться с работой библиотеки Socket.IO-client Java

#java #android #сокет

                    
Хочу воспользоваться библиотекой  compile 'io.socket:socket.io-client:0.6.1', вот ссылка

И я смог настроить соединение с сервером. Когда я соединяюсь, сервер видит клиента,
и даже получает шаблонное сообщение. Но дальше я не могу передавать сообщения на сервер.
Вот кусочек кода, который отвечает за подключение:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private TextView status;
    private Button button;
    private Socket socket;
    {
        try {
            socket = IO.socket(Constants.CHAT_SERVER_URL);
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        status = (TextView) findViewById(R.id.status);
        button = (Button) findViewById(R.id.button);
        button.setOnClickListener(this);

        socket.connect();
        if (socket.connected()) {
            status.setText("Connected");
        } else {
            status.setText("Disconnected");
        }
    }


Я запускаю приложение, создается сокет, и на сервере срабатывает событие о том, что
клиент подключился. Это после строчки socket.connect(); А так же на сервере видно такие
логи:

Welcome to Server side
Waiting for a client...Client connected
Wait for messages
GET /socket.io/?EIO=3&transport=polling HTTP/1.1
User-Agent: Dalvik/1.4.0 (Linux; U; Android 2.3.7; Android SDK built for x86 Build/GINGERBREAD)
Host: 10.0.2.2:4444
Connection: Keep-Alive
Accept-Encoding: gzip


То есть, клиент действительно присоединился, и отправил какие то данные. Но дальше
непонятно. Первое это то, что не срабатывает мое условие:

if (socket.connected()) {
                status.setText("Connected");
            } else {
                status.setText("Disconnected");
            }


потому-что socket.connected() возвращает false. НО на стороне сервера показано, что
соединение установлено (если я закрою клиента, то и сервер говорит, что соединение
разорвано). Выходит, сервер видит, что клиент присоединен, но на клиенте говорится,
что соединения нет. И при попытке отправить сообщение на сервер:

@Override
    public void onClick(View v) {
        if (v.getId() == R.id.button) {
            socket.send("::::::::::::::::::::::::::::::::::::::::::::");
        }
    }


НИЧЕГО НЕ ПРОИСХОДИТ!

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

Мне просто нужно String  посылать на сервер и ВСЕ.

Вот код СЕРВЕРА:

public class Server {
    public static void main(String[] args) throws IOException {
        System.out.println("Welcome to Server side");
        BufferedReader in = null;
        PrintWriter out = null;

        ServerSocket servers = null;
        Socket fromclient = null;

        // create server socket
        try {
            servers = new ServerSocket(4444);
        } catch (IOException e) {
            System.out.println("Couldn't listen to port 4444");
            System.exit(-1);
        }

        try {
            System.out.print("Waiting for a client...");
            fromclient = servers.accept();
            System.out.println("Client connected");
        } catch (IOException e) {
            System.out.println("Can't accept");
            System.exit(-1);
        }

        in = new BufferedReader(new InputStreamReader(fromclient.getInputStream()));
        out = new PrintWriter(fromclient.getOutputStream(), true);
        String input, output;

        System.out.println("Wait for messages");
        while ((input = in.readLine()) != null) {
            System.out.println(input);
        }
        out.close();
        in.close();
        fromclient.close();
        servers.close();
    }
}

    


Ответы

Ответ 1



Во первых: Библиотека ассинхронная. Что это значит? То, что следующий код socket.connect(); if (socket.connected()) { status.setText("Connected"); } else { status.setText("Disconnected"); } Всегда войдет в else. потому, что socket.connect() - ассинхронный метод, выполняется в отдельном потоке, и если сразу же спросить у сокета socket.connected(), то он вернет false, потому, что connect не успел еще выполниться. Вместо этого надо повесить Listener перед тем, как вызвать connect: socket.on(Socket.EVENT_CONNECT, new Emitter.Listener() { @Override public void call(Object... args) { //делаем все, что надо сделать после успешного коннекта } }) На счет остального - проверьте код сервера, мне показалось, что он должен обработать только первый коннект, и сразу завершиться.

воскресенье, 15 марта 2020 г.

Реализация клиент-серверного приложения

#c_sharp #aspnet #сокет #wcf #клиент_сервер


Имеется следующая архитектура: клиентская часть(dll-ка на C#) отсылает определенное
количество картинок на сервер(либо Windows Service, либо Web Service), где они обрабатываются,
а потом отсылается ответ в виде XML файла результатов обработки.

Клиент - это просто автоматизированное приложение, без интерфейса и ввода вывода. 

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

Пока что я не могу понять какая архитектура взаимодействия лучше всего подойдет для
моего случая. Есть несколько путей реализации, либо писать асинхронный сервер на сокетах,
либо использовать WCF, или просто написать ASP.NET приложение и залить его на IIS(к
этому варианту я склоняюсь больше всего).

Какой протокол передачи лучше всего использовать? Хватит ли HTTP для передачи большого
количества картинок(тогда можно двигаться в направлении Web Service), или стоит задуматься
о TCP/IP(здесь уже WCF)?

Кому интересно, несколько статей по созданию клиент-серверного приложения:

Example with ASYNC/AWAIT

Example with THREADS

Example with SOCKETS
    


Ответы

Ответ 1



HTTP, WCF и голый TCP справляются с заливкой картинок примерно одинаково. Особенно если учесть, что HTTP работает поверх TCP, а WCF - или поверх HTTP, или с собственным протоколом поверх TCP, в зависимости от настроек биндинга. Никакой ощутимой разницы между реализациями с точки зрения производительности не будет. То же самое с типом хостинга - между Self-Hosted и IIS нет практически никакой разницы (не при вашей нагрузке). Вам стоит использовать то, что вам удобнее в написании и поддержке.

воскресенье, 8 марта 2020 г.

Java Десериализация ArrayList в цикле

#java #сокет #arraylist


Ребята, добрый день! 

Я новичок и выполняю следующее задание. Нужно построить клиент-серверное приложение,
где клиенты отправляют на сервер объекты периодически. сервер эти значения добавляет
в ArrayList и периодически рассылает клиентам. Проблема в том, что на стороне клиента
читается не весь ArrayList. а читается только первые несколько элементов. Помогите
разобраться. Коды и вывод информации ниже.

СЕРВЕРНАЯ ЧАСТЬ

             package sockets;

import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.Iterator;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;


public class ServerSocketProg extends JFrame implements Runnable {

    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    ArrayList users;
    private ArrayList clientObjectOutputStreams;
    private ArrayList stringArrayList = new ArrayList();
    private ServerSocket serverSock;
    private JTextArea ta_chat;
    private Path path;
    private BufferedWriter fileWriter;

    public ServerSocketProg() {
        initComponents();


        path = Paths.get("ServerLogger.txt");
        try {
            fileWriter = Files.newBufferedWriter(path, 
                    StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING,
StandardOpenOption.WRITE);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    @Override
    public void run() 
    {
        clientObjectOutputStreams = new ArrayList();
        users = new ArrayList();  

        try 
        {
            serverSock = new ServerSocket(2222);
            while (true) 
            {
                Socket clientSock = serverSock.accept();                        
               
                ObjectOutputStream objectWriter = new ObjectOutputStream(clientSock.getOutputStream());
                clientObjectOutputStreams.add(objectWriter);

                Thread listenerObject = new Thread(new ClientObjectHandler(clientSock,
objectWriter));
                listenerObject.start();
                Thread intervalSender = new Thread(new SendArrayWithInterval());
                intervalSender.start();

                ta_chat.append("Got a connection. \n");
                fileWriter.write("Got a connection. \r\n");
            }
        }
        catch (Exception ex)
        {
            ex.printStackTrace();
            ta_chat.append("Error making a connection. \n");
            try {
                fileWriter.write("Error making a connection. \r\n");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }   



    class ClientObjectHandler implements Runnable   
    {
           BufferedReader reader;
           Socket sock;
           ObjectOutputStream client;
           ObjectInputStream objectReader;

           public ClientObjectHandler(Socket clientSocket, ObjectOutputStream user)
throws IOException 
           {
                client = user;
                try 
                {
                    sock = clientSocket;
                    objectReader = new ObjectInputStream(sock.getInputStream());
               
                }
                catch (Exception ex) 
                {
                    ta_chat.append("Unexpected error... \n");                   
                    fileWriter.write(ex.toString());
                }

           }

           @Override
           public void run() 
           {
                String message;    

                try 
                {
                    while ((message = (String) objectReader.readObject()) != null) 
                    {
                        ta_chat.append("Received Object: " + message + "\n");
                        fileWriter.write("Received Object: " + message + "\r\n");
                        stringArrayList.add(message);                           
          
                    } 
                 } 
                 catch (Exception ex) 
                 {
                    ta_chat.append("Lost a connection. \n");
                    try {
                        fileWriter.write("Lost a connection\r\n");
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    clientObjectOutputStreams.remove(client);
                 } 
           } 
        }

    class SendArrayWithInterval implements Runnable{
        @Override
        public void run() {
            while(true){
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if(stringArrayList.size()>0){
                    ta_chat.append("Telling everyone Array...\n");                  
                    sendEveryoneObject();
                }
            }
        }
    }

    public void sendEveryoneObject() {
        Iterator it = clientObjectOutputStreams.iterator();

        while (it.hasNext()) 
        {
            try 
            {
                ObjectOutputStream outputWriter = (ObjectOutputStream) it.next();


                outputWriter.writeObject(stringArrayList);

                outputWriter.flush();

                ta_chat.append("Sending: " + stringArrayList.toString() + "\n");        
                ta_chat.setCaretPosition(ta_chat.getDocument().getLength());
                fileWriter.write("Sending: " + stringArrayList.toString() + "\r\n");

            } 
            catch (Exception ex) 
            {
                ex.printStackTrace();
                ta_chat.append("Error telling everyone. \n");
                try {
                    fileWriter.write("Error telling everyone. \r\n");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }




    }


    private void initComponents() {
        JScrollPane jScrollPane1 = new javax.swing.JScrollPane();
        ta_chat = new javax.swing.JTextArea(40, 50);        
        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setTitle("Chat - Server's frame");
        setName("server");        

        jScrollPane1.add(ta_chat);
        jScrollPane1.setViewportView(ta_chat);
        add(jScrollPane1);

        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent arg0) {
                try {
                    fileWriter.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }); 

        setSize(400, 600);
        setVisible(true);
        pack();
    }



    public static void main(String[] args) {        
        Thread starter = new Thread(new ServerSocketProg());
        starter.start();            
    }
}


КЛИЕНТСКАЯ ЧАСТЬ: 

package sockets;

import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;


public class ClientsSocketsProgram extends JFrame implements Runnable {
    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    private String address = "localhost";
    private int port = 2222;
    private Boolean isConnected = false;  
    private static int instance = 0;
    private Socket sock;
    private ObjectOutputStream objectWriter;
    private ObjectInputStream objectReader; 
    private JTextArea ta_chat;
    private Path path;

    private int incrementerToSend = 0;

    public ClientsSocketsProgram() {

        initComponentsClientFrame();
        connectToServer();       

        Thread incomeReader = new Thread(new IncomingReader());
        incomeReader.start();
    }   

    class IncomingReader implements Runnable
    {
        ArrayList inputArrayList;// = new ArrayList();
        @Override
        public void run() 
        {
            ArrayList here = new ArrayList();


            try 
            {
                while( true  ){

                    here =(ArrayList) objectReader.readObject();        
           

                    ta_chat.append("Object received: " + here.toString() + "\n "); 
                    ta_chat.setCaretPosition(ta_chat.getDocument().getLength());
             
                }
           }catch(Exception ex) {

               ex.printStackTrace();
           }
        }



    }

    @Override
    public void run() { 
        while(isConnected){
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            ta_chat.append("Sending " + incrementerToSend + "\n");

            sendObject(" " + incrementerToSend);
            ++incrementerToSend;
        }

    }




    private void initComponentsClientFrame() {

        JScrollPane jScrollPane = new javax.swing.JScrollPane();
        ta_chat = new javax.swing.JTextArea(20, 30);

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setTitle(" Chat - Client's frame");
        setName("client"); 


        jScrollPane.add( ta_chat );
        jScrollPane.setViewportView(ta_chat);
        add(jScrollPane);

        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent arg0) {
                closeConnections();
            }
        }); 

        setSize(400, 600);
        setVisible(true);
        pack();
    }



    private void connectToServer() {
        if (!isConnected) 
        {
            try 
            {
                sock = new Socket(address, port);                
                objectReader = new ObjectInputStream(sock.getInputStream());
                objectWriter = new ObjectOutputStream(sock.getOutputStream()); 
                isConnected = true; 
            } 
            catch (Exception ex) 
            {
                ta_chat.append("Cannot Connect! Try Again. \n");

            }

        } else if (isConnected) 
        {
            ta_chat.append("You are already connected. \n");

        }
    }

    private void sendObject(String text) {

        try  {
            objectWriter.writeObject(text);
            objectWriter.flush();           
        } catch (Exception ex) {
            ta_chat.append("Message was not sent. \n");


            ex.printStackTrace();
        }

    }

    private void closeConnections(){
        if(isConnected){
            try{
                objectWriter.close();
                objectReader.close();
                sock.close();

                isConnected = false;
            }catch(Exception e){
                e.printStackTrace();
            }
        }       
    }





    public static void main(String args[]) 
    {
        Thread thread = new Thread(new ClientsSocketsProgram());
        thread.start();

    }


}


ВЫВОД СЕРВЕРНОЙ ЧАСТИ (ЗАПУЩЕНО 2 КЛИЕНТА): 

         Got a connection. 
Received Object:  0
Received Object:  1
Telling everyone Array...
Sending: [ 0,  1]
Received Object:  2
Got a connection. 
Received Object:  3
Received Object:  0
Received Object:  4
Received Object:  1
Telling everyone Array...
Sending: [ 0,  1,  2,  3,  0,  4,  1]
Sending: [ 0,  1,  2,  3,  0,  4,  1]
Telling everyone Array...
Sending: [ 0,  1,  2,  3,  0,  4,  1]
Sending: [ 0,  1,  2,  3,  0,  4,  1]
Received Object:  5
Received Object:  2
Received Object:  6
Received Object:  3
Telling everyone Array...
Sending: [ 0,  1,  2,  3,  0,  4,  1,  5,  2,  6,  3]
Sending: [ 0,  1,  2,  3,  0,  4,  1,  5,  2,  6,  3]


ВЫВОД КЛИЕНТСКАЯ ЧАСТЬ: 

Sending 0
Sending 1
Object received: [ 0,  1]
 Sending 2
Sending 3
Sending 4
Object received: [ 0,  1]
 Object received: [ 0,  1]
 Sending 5
Sending 6
Object received: [ 0,  1]
 Sending 7
Object received: [ 0,  1]
 Sending 8
Sending 9
Object received: [ 0,  1]
 Object received: [ 0,  1]
 Sending 10
Sending 11
Object received: [ 0,  1]
 Sending 12

    


Ответы

Ответ 1



Это происходит потому, что вы каждый раз передаете один и тот же объект ArrayList. ObjectInputStream/ObjectOutputStream позволяют передать объект вместе со всеми его полями, которые могут содержать ссылки на другие объекты, которые тоже могут на кого-то ссылаться. Поскольку может быть несколько ссылок на один объект, то при десериализации эти ссылки восстанавливаются, а не создается несколько копий. Т.е. writeObject видит, что этот объект он уже записывал, и кладет в поток ссылку, а readObject возвращает вам ссылку на ранее десериализованный объект. Чтобы этого избежать, вы можете использовать ObjectOutputStream.writeUnshared, чтобы основной объект считался уникальным. Еще можно после записи вызывать ObjectOutputStream.reset() чтобы последующие записи не учитывали ранее сериализованные объекты, и записывали их по-новой, а не в виде ссылок. Может быть стоит создавать новый ArrayList в sendEveryoneObject и рассылать его. Замечу, что вы очень вольно обращаетесь с многопоточностью. ArrayList не является потокобезопасной коллекцией, и не дает никаких гарантий при одновременной модификации (когда два клиента прислали вам сообщение, например). Вам нужно или самостоятельно обеспечить потокобезопасность через synchronized или Lock из java.util.concurrent, использовать Collections.synchronizedList (обратите внимание на способ использования итератора в документации метода), либо использовать какую-то коллекцию из java.util.concurrent (CopyOnWriteArrayList вам не очень подойдет, т.к. много модификаций, но для хранения сообщений может подойти одна из очередей). В последних двух случаях стоит скопировать данные перед отправкой клиентам в локально созданный список. При обращении к компонентам Swing следует использовать SwingUtilities.invokeLater, т.к. все обращения должны выполняться из Event Dispatch Thread. Создавать новый new Thread(new SendArrayWithInterval()) при каждом подключении клиента не нужно, у вас поэтому начинают дублироваться посылки сервера.

Сокеты в python: параметр backlog у socket.listen() метода

#python_3x #сокет


Здравствуйте. Написал простое клиент-серверное приложение используя сокеты. Клиент
отправляет строку серверу, сервер переводит эту строку в верхний регистр и отправляет
клиенту, а клиент просто печатает эту строку.

#Сервер
import socket
from threading import Thread

def handler(client_socket, cli_address):
    print(client_address, 'was connected')
    while 1:
        recieve_message = client_socket.recv(1024)
        client_socket.send(recieve_message.upper())

server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(('127.0.0.1', 5000))
print('server starts')
server_socket.listen(1)
while 1:
    client_socket, client_address = server_socket.accept()
    Thread(target=handler, args=(client_socket, client_address)).start()
#-----------------------------------------
#Клиент
import socket

client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect(('127.0.0.1', 5000))
while 1:
    message = input()
    client_socket.send(message.encode())
    recieve_message = client_socket.recv(1024)
    print(recieve_message.decode())


Вопрос вызывает метод listen. В качестве параметра он принимает максимальное число
соединений. В данном случает он равняется 1. Но запустив сервер и пять клиентов, все
они благополучно работают, почему?
    


Ответы

Ответ 1



Вы не совсем правильно понимаете этот параметр у listen. Как происходит работа серверного сокета: клиент подключается к серверу. его ставят в очередь (и этот параметр у listen определяет размер очереди) если в очереди уже нет места - клиенту отказывают в подключении. отдельно сам сервер вызывает accept для сокета. Этим он забирает одного клиента с очереди сокетов, ожидающих подключение. Теперь законный вопрос, а сколько же ставить размер очереди? Она должна быть такой, что бы код успевал принять всех клиентов. То есть, если Вы тестируете и подкючаетесь одним клиентом и следующее подключение будет только после обрабоки предыдущего, то даже размер в 1 будет достаточный. Если же у Вас тяжело нагруженный сервер и клиенты туда валят сотнями, а в коде могут быть затыки по accept'у клиентов, то нужно ставить побольше, я видел и 100, и 150. Но если клиенты валят с такой скоростью, что код не успевает их выгребать, то тут никакой размер очереди не поможет.

Ответ 2



Из документации для socket.listen([backlog]): Enable a server to accept connections. If backlog is specified, it must be at least 0 (if it is lower, it is set to 0); it specifies the number of unaccepted connections that the system will allow before refusing new connections. If not specified, a default reasonable value is chosen.(выделение моё) Выделенная часть говорит, что backlog параметр определяет число непринятых соединений, после которых новые соединения отвергаются. Что такое непринятое соединение и/или кол-во очередей связанных с новыми соединениями может зависеть от системы, например, см. man listen(2) и если хочется подробностей: How TCP backlog works in Linux. "Непринятое" соединение—это вероятно соединение, для которого socket.accept() метод не был вызван. Поэтому даже с server_socket.listen(1) ваш сервер может сколько угодно (пока кол-во запущенных потоков систему не подвесит) одновременных клиентов обслуживать, если между открытиями новых соединений достаточна пауза, чтобы новый поток запустился и следующий server_socket.accept() мог бы вернуться.

пятница, 28 февраля 2020 г.

C Socket: Долго сбрасывается соединение

#c #сокет


есть такой код:

int read_s, str_size;
char recive_msg[TEXTSIZE_BUFER];
struct sockaddr_in server, client;

str_size = sizeof(struct sockaddr_in);
server.sin_port = htons(100);
server.sin_addr.s_addr = INADDR_ANY;
server.sin_family = AF_INET;

for (;;) {
    int socket_r = socket(AF_INET, SOCK_STREAM, 0), clnt_r;

    setsockopt(socket_r, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout));
    setsockopt(socket_r, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout));
    printf("22\n");

    memset(server.sin_zero, '\0', sizeof(server.sin_zero)); 
    if (bind(socket_r, (struct sockaddr *)&server, sizeof(server)) < 0)
        goto close_socket;

    printf("aa\n");

    listen(socket_r, 10);
    if ((clnt_r = accept(socket_r, (struct sockaddr *)&client, &str_size)) <= 0)
        goto close_socket;

    read_s = recv(clnt_r, recive_msg, TEXTSIZE_BUFER, 0);
    printf("close\n>%s\n", recive_msg);

close_socket:
    setsockopt(socket_r, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int));
    setsockopt(clnt_r, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int));
    close(clnt_r);
    close(socket_r);
}


Проблема: Когда софт выполняет SO_REUSEADDR и close(), возвращается в начале и потом
секунд 20-30 циклится на printf("22\n");, только после этого идет дальше. Вопрос такой:
в чем может быть причина?

Изначально у меня задумка была поделить клиент и сервер часть в 2 отдельных потока...и
когда освобождается порт выполняется тот или иной поток.
    


Ответы

Ответ 1



SO_REUSEADDR говорит, что этот сокет можно привязать к адресу, даже если он уже привязан. Делать его перед закрытием сокета - бессмысленно. Изменять эту опцию имеет смысл только, если сокет создан, но ещё не привязан.

Ответ 2



В месте закрытия сокета поменять на следующий код: close_socket: shutdown(clnt_r, SHUT_RDWR); close(clnt_r); shutdown(socket_r, SHUT_RDWR); close(socket_r); } по вкусу, если таймаут закрытия велик на ваш взгляд, можно добавить: close_socket: struct linger lng = { 0, 0 }; if (setsockopt(clnt_r, SOL_SOCKET, SO_LINGER,(void*)(&lng), sizeof(struct linger)) < 0) { perror("set: linger 1"); } shutdown(clnt_r, SHUT_RDWR); close(clnt_r); if (setsockopt(socket_r, SOL_SOCKET, SO_LINGER,(void*)(&lng), sizeof(struct linger)) < 0) { perror("set: linger 2"); } shutdown(socket_r, SHUT_RDWR); close(socket_r); } но лучше искать причину медленного закрытия.. Она легко может быть и на стороне клиента. И в общем, с лингером не все так просто, вы изменяете запланированное поведение системы, в области очереди отправки пакетов. Это не всегда есть правильное решение. Если опция SO_LINGER разрешена, то вызовы close(2) или shutdown(2) приведут к задержке процесса до отправки всех сообщений в очереди сокета или до истечения времени задержки. В противном случае выход из вызовов будет произведен немедленно и закрытие будет произведено в фоновом режиме. Если сокет закрывается как часть вызова exit(2), то задержка всегда происходит в фоновом режиме. Собственно мануал В вашем случае, разумно пересмотреть код, делать bind и listen в цикле непонятно зачем.. система явно против.. :) В цикле надо читать из сокета, и возможно отвечать, уверен что клиент ожидает общения :) Функция accept и read должна быть в цикле, и её удобно обернуть во что то более приемлемое, для выхода из неё в случае ошибки. Попробуйте перейти к более приспособленным оберткам, избегая использование goto, например: do { accept() .... if (error) break; read() ... // другой код if (noerror) break; } while (0); close_socket_code()...

пятница, 14 февраля 2020 г.

Путь к UNIX сокету не может быть более 16 символов

#cpp #linux #c #сокет


Есть две моих программы на C/C++. Код для работы с Unix сокетами я брал с одного
давно устаревшего сайта, других более понятных примеров я не нашел. В итоге я не могу
использовать сокет, длина которого более 14-16 символов. Код клиента:  

int sock;
sock = socket(AF_UNIX, SOCK_STREAM, 0);
struct sockaddr srvr_name;
if (sock < 0) {
    perror("socket failed");
    return EXIT_FAILURE;
}
srvr_name.sa_family = AF_UNIX;
strcpy(srvr_name.sa_data, SOCK_NAME);
if (connect(sock, &srvr_name, sizeof (srvr_name)) < 0) {
    perror("connect failed");
    exit(2);
}
send(sock, buf, strlen(buf), 0);


Код сервера:  

sock_ = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock_ < 0) {
    throw socket_exception(socket_exception::SocketError);
}
srvr_name.sa_family = AF_UNIX;
filename_c = filepath.c_str();
strcpy(srvr_name.sa_data, filename_c);
if (bind(sock_, &srvr_name, strlen(srvr_name.sa_data) +
        sizeof (srvr_name.sa_family)) < 0) {
    throw socket_exception(socket_exception::BindError);
}
listen(sock_, max_connect-1);


В strace(При том что путь к сокету я указал как /run/defu123456789/34567)  

connect(3, {sa_family=AF_UNIX, sun_path="/run/defu12345"}, 16) = -1 ENOENT


Размер буфера sock_name ограничен 256 байтами. Использовать библиотеку boost нельзя.
Как изменить код так, что бы он работал корректно?
    


Ответы

Ответ 1



В итоге я не могу использовать сокет, длина которого более 14-16 символов. Потому, что структура sockaddr описана в файле /usr/include/bits/socket.h таким образом: /* Structure describing a generic socket address. */ struct sockaddr { __SOCKADDR_COMMON (sa_); /* Common data: address family and length. */ char sa_data[14]; /* Address data. */ }; Как видите, под поле адреса сокета отведено ровно 14 байт. Откуда взялось это ограничение - долгая история. Но оно есть.

четверг, 13 февраля 2020 г.

Получить имя сетевого интерфейса в linux

#cpp #linux #сокет


Начал выкладывать свою библиотеку: https://github.com/mikelsv/opensource , возникла
проблема в примере mping.

Для получения ответов от пингуемого хоста нужно включить прослушивание интерфейса.
В коде по умолчанию используется интерфейс eth0, которого вполне может и не быть. Собственно
на VPS где проверяется работоспособность этого интерфейса нет, вследствие чего возникает
ошибка.

Текущая реализация: https://github.com/mikelsv/opensource/blob/master/msvcore/net/sniffer.h#L56
и https://github.com/mikelsv/opensource/blob/master/msvcore/net/sniffer.h#L96

unsigned char device[1024];
strcpy((char*)device, "eth0");
strcpy((char*)interface.ifr_name,(char*)device);
if(ioctl(sock,SIOCGIFFLAGS,&interface)<0){ printf("failed reading data for %s!\n",device);
return 1; }


Так как интерфейса eth0 в системе нет, вылетает ошибка "failed reading data for eth0".

Как получить имя интерфейса существующего интерфейса? Так, чтобы у интерфейса был
реальный ip. Как вариант, можно передавать в функцию локальный ip с которого послан
пинг, тогда как получить название интерфейса зная его ip?

Код старый, написан коряво, переписывать смысла нет, а работоспособность восстановить
хочется.
    


Ответы

Ответ 1



думаю, вам подойдёт функция getifaddrs. описание и пример применения смотрите командой $ man getifaddrs либо в интернете

Ответ 2



Утилита ifconfig возвращает список интерфейсов, их состояние и адреса на них. Вот тут пишут как получить тот же результат не вызывая стороннюю программу.

Ответ 3



Можно посмотреть в файле конфигураций: sudo nano /etc/network/interfaces Или выполнить команду: ifconfig -a

понедельник, 10 февраля 2020 г.

Какие типы данных и в каком виде можно передавать через TCP bsd socket?

#cpp #c #сокет #tcp


Во всех примерах использования bsd сокетов на си для передачи и приема информации
используется массив символов фиксированной длинны char buff[255]. Например read(socketFD,
buff, sizeof(buff));. Возникает пара вопросов:


Обязательно ли использовать массив char, или же можно передавать произвольные объекты?
например можно ли передать double buff[255] или даже struct someStructType buff[255]?
Что будет, если отправить массив например в 200 элементов, а на принимающей стороне
считать только 100? При повторном чтении мы прочитаем те же элементы, или же следующие
100 элементов?

    


Ответы

Ответ 1



В сокет передаются исключительно байты, что бы ваши объекты превратить в байты или создать их из них нужно использовать протоколы сериализации, такие как например T-L-V или ProtoBuf. В примерах приводится массив-буфер для считывания в него информации, как правило функция чтения возвращает реальное количество считанных байт. Если вы отправите 200 байт, а считаете 100, то вам надо будет считать потом еще 100. Буфер может быть любого размера (но как правило его делают не более MTU/MRU). Как правило при обмене данными через сеть, они упаковываются в пакет вида: packetLength:packetData, что позволяет гарантированно считывать переданный пакет. Так, например, отправляя 200 байт нужной информации вы: Считываете в буфер приема данные из сокета. Читаете из этого буфера длину пакета packetLenght (сколько байт - зависит от типа переменной и архетиктруры); Создаете буфер размером packetLength. Это не тот же самый буфер в который вы читаете из сокета!! Копируете из буфера приема данные в созданный буфер, при этом если размер данных меньше, чем осталось в буфере, то копируете нужно кол-во, если больше, то пишите все, а затем дочитываете далее из сокета в буфер приема до тех пор, пока не вытащите все.

Ответ 2



Первый, по сути, вопрос переносимости. Вы просто передаете блок байтов. Нужны гарантии корректной интерпретации их с другой стороны. Вдруг там другой порядок байтов? Другой формат данных? Или еще что-то? Вот поэтому и используется максимально обобщенный вариант - просто сколько-то байтов. Что касается структур - то учтите еще и выравнивание. Надеюсь, чем чревата передача просто объектов в духе string s; write(fd, &s, sizeof(s)); вы понимаете? :) А то некоторые ухитряются... Второй - прочтете при следующем чтении остальные 100 байтов. Если, конечно, до того не закроете соединение :)

воскресенье, 9 февраля 2020 г.

можно ли переиспользовать сокет

#linux #c #сокет


здравствуйте, вот, допустим, есть у меня клиентский сокет... сделали connect, приняли
данные/отправили... а можно ли приконнектится к другому адресу, и так же, как ни в
чем ни бывало, начать обмен с другим совершенно хостом? я так понимаю, параметр SO_REUSEADDR
на это не влияет
    


Ответы

Ответ 1



Именно, опция SO_REUSEADDR используется только для бинда портов и к исходящим подключениям отношения не имеет.

Ответ 2



Опция SO_REUSEADDR используется в одном, крайне редком случае - когда одновременно две (или более программы) используют один сокет. Подробно это описано в гл. 9. "Повышение производительности" книги "Создание сетевых приложений в среде Linux" (Шон Уолтон).

среда, 5 февраля 2020 г.

Как закрыть Socket?

#java #многопоточность #сокет


есть отдельный поток класс который работает с Socket-ом.Когда я отключаю клиент он
все равно продолжает работать. Метод socket.isClosed() возвращает false а socket.isConnected()
true хотя клиент давно уже отрублен.Как можно закрыть этот Socket?  Есть мысли отправить
сигнальный текст из клиента чтобы закрыть соединение типа :

String line = bufferedReader.readLine();
   if(line.equals("exit")){
         socket.close();
     }


но я не могу использовать readLine() так как из клиента я отправляю только тогда
когда получаю данные от сервера.Как это можно реализовать?помогите пожалуйста.Вот код
который есть сейчас.На самом деле кода тут очень много ,я написал самую главную логику.

        @Override
        public void run() { 

                try(PrintWriter out = new PrintWriter(socket.getOutputStream(),true);
                    BufferedReader in = new BufferedReader(
                                           new InputStreamReader(
                                             socket.getInputStream(),
                                               Charset.forName("cp1251")))){

                 while(true) {

                        out.println("soobshenie s servera");
                        out.flush();

                      if((socket.isClosed())||(!socket.isConnected())){
                        socket.close();
                        System.out.println("поток завершен");
                        break;
                      }
                  } 
              }
        }

    


Ответы

Ответ 1



Методы isConnected() и isClosed() в Java работают немного не так, как вы представляете. Метод isConnected() возвращает true тогда, когда сокет хоть раз был connected. Даже если сокет уже давно закрыт, isConnected() будет true. Метод isClosed() возвращает true, когда сокет был хоть раз закрыт. Если сокет никогда не был подключен, isClosed() вернет false, даже не смотря на то, что по факту он не открыт. Следовательно, вот так вот можно проверить, подключен ли сокет к серверу на текущий момент. boolean connected = socket.isConnected() && !socket.isClosed();

пятница, 31 января 2020 г.

Как при помощи обычных сокетов установить SSL соединение?

#java #c #сокет


Здравствуйте. Подскажите пожалуйста. В учебных целях пишу SSL клиент. Читал RFC 5246,
6101 Вроде все понятно написано. Вот только есть проблема. Я пытался посмотреть как
проходит соединение на практике. Считывал поток байтов, отправленный браузером, с сервер
сокета. Дальше писал точно такой же поток байтов только с именем хоста yandex.ru. Подключался
к хосту на 443 порту и отправлял поток байтов. Так же отправлял на сервер 32 случайных
байта. В ответ сервер ничего не присылает а просто закрывает соединение. Использовать
сторонние библиотеки не хочу. Интересно все сделать самому для лучшего понимания. Маюсь
с этим уже 2 недели. Что я делаю не так? Как хотя бы начать handshake?
    


Ответы

Ответ 1



Легче всего будет практиковаться с конкретными инструментами. Запишите SSL-дамп c помощью Wireshark или Netmon. По дампу диалог между сервером и клиентом становится предельно прозрачным, если при этом внимательно изучать rfc, которым Вы руководствуетесь. Кроме того, Вам просто необходим еще один инструмент, называется OpenSSL. Он включает в себя набор утилит командной строки, которые помогут очень быстро нащупать верное направление при генерации ключей. Конечно, Вы можете генерировать их и сами, вручную, без дополнительных инструментов (хотя, не понимаю, зачем это Вам, но Вам обязательно скажут о плюсах использования подобных инструментов, я даже не буду касаться этой темы). Следующий шаг - потратьте немного времени на сертификат. Если Вам будет доступен сервер, для которого Вам будет известен сертификат с приватным ключом, то вы сможете расшифровать весь трафик (и сервера и клиента), это так же умеют делать Wireshark и Netmon. Такой сервер сейчас очень легко организовать прямо дома, все что Вам для этого нужно - Apache. Если Вы расшифруете трафик, то Вам будет понятно, правильно ли работает Ваша реализация, это автотестирование сэкономит Вам уйму времени. А вот теперь - самое интересное: Клиент посылает ClientHello, в котором передает на сервер случайные 32 байта, набор шифров и набор методов сжатия, с которыми он умеет обращаться. Сервер отвечает с ServerHello, в котором передает клиенту свои случайные 32 байта, шифр и метод сжатия, которые он выбрал из тех, что предложил клиент. Затем, сервер передает набор сертификатов (обычно - один) с публичным ключом и завершающее цепочку пустое сообщение DONE ("все хорошо, я готов к хендшейку"). Следующим сообщением, клиент посылает на сервер зашифрованный публичным ключом pre master secret. Расшифровать его можно только приватным ключом сервера, этим, по сути, и гарантируется безопасность диалога. Далее, клиент посылает пакет ChangeCipherSpec ("все хорошо, приступаем к общению с текущими параметрами безопасности"). Последнее сообщение клиента - пакет Finished, в котором он зашифровал 12-байтный проверочный хеш. Зашифровал уже ключом, который должен быть сгенерирован из pre master secret. В ответ, сервер посылает подряд 2 сообщения: аналогичный ChangeCipherSpec и свой Finished. На этом, хендшейк завершен. Теперь о ключах. Из pre master secret генерируется master secret. Из master secret генерируется последовательность key expansion, которая является донором для основных ключей, которыми кодируется и хэшируется трафик. Длина и наличие соответствующих ключей обуславливается выбранным шифром. Во всех SSL-RFC в приложении есть таблички, по которым эти параметры можно определить. Есть даже пример, на котором разбирается конкретный шифр. Это основной алгоритм. В деталях - уже конкретные реализации протоколов, например: у SSL3.0 и SSL3.1(TLS1.0) алгоритмы генерации различны, их описание есть в соответствующих RFC.

Ответ 2



Считывал поток байтов, отправленный браузером, с сервер сокета. Дальше писал точно такой же поток байтов, только с именем хоста yandex.ru. Сервер рвёт соединение, так как нарушена процедура согласования (handshake). Клиент и сервер договариваются об использовании симметричного сессионного ключа. Этот ключ заново создаётся для каждой новой сессии на основе ключевой пары сторон и дополнительных случайно выбираемых параметров. Потому сервер и отказался принимать ключ, использованный в совершенно неизвестном для него захваченном сеансе связи, и разорвал соединение. 32 случайных байта были отправлены до завершения рукопожатия, а потому сервер принял их за корректное начало ответа клиента и ничего не отвечал, ожидая продолжения передачи.

среда, 29 января 2020 г.

Отправка данных через сокет (упаковка) с++

#cpp #linux #сокет #tcp #udp


Необходимо отправить данные типа:float/int/char через сокет, как организовать "упаковку"
на стороне отправителя что бы отправить всё одним пакетом, и "распаковать" на принимающей
стороне.

В сети нашёл скрин приложения для игры, как упаковать так же? 


    


Ответы

Ответ 1



Примитивный способ для простых случаев - описать структуру данных: struct Data { int a; float b; char c[32]; } data; Записать её в сокет на передающей стороне (send(socket, &data, sizeof(data), 0) и на принимающей прочитать в точно такую же структуру (recv(socket, &data, sizeof(data), 0)). Очень важно чтобы стуктура на обоих сторонах (передающей и приемной) была идентичной по расположению в памяти (одинаковые размеры типов, одинаковый порядок байтов в системе, одинаковое выравнивание полей структуры, одинаковое представление чисел с плавающей точкой). Иначе получаем не те данные, что отправили. На практике, если принимающая сторона ещё и на другом языке написана, получим лишнюю возню и простор для появления ошибок. Следующий вариант - набивать буфер данных вручную: int foo = 42; long bar = 0; std::string str; str.append((char*)&foo, sizeof(int)); str.append((char*)&bar, sizeof(long)); Здесь уже нет проблемы с выравниванием полей структуры как в первом варианте, т.к. данные мы склеиваем сами, без промежутков. Но остальные проблемы пока ещё с нами (по прежнему порядок байтов, размеры типов, представление чисел с плавающей точкой должны быть идентичными на передатчике и приемнике). Ручная, побайтовая набивка потока. uint32_t foo = 42; std::vector buffer; buffer.push_back(static_cast(foo >> 0)); buffer.push_back(static_cast(foo >> 8)); buffer.push_back(static_cast(foo >> 16)); buffer.push_back(static_cast(foo >> 24)); Здесь просто берем каждый кусок данных и вручную переносим в выходной поток в независимом от системы порядке. Разбирать тоже придется вручную. Наиболее универсальный способ, т.к. все аспекты генерируемого потока контролируем сами. Для удобства можно написать класс сериализатора/десериализатора для требуемых типов (включая пользовательские). Со временем (а может быть и сразу) добавляются сложности, связанные с изменением передаваемых данных (например понадобилось передать дополнительные данные или какие-то старые уже стали неактуальными). Особенно если приемник должен принимать данные и в старом формате и в новом. Придется добавлять какие-то идентификаторы версии. Дополнительно нужно обработать случаи, когда нужно передать опциональные данные (которые могут отсутствовать) или данные динамического размера (массивы). Чтобы не решать все эти задачи самостоятельно, можно взять готовое решение, например protobuf от google. Поддерживает разные языки, имеет систему версий, поддержку комплексных данных. Или немного более простое решение (но и более быстрое), тоже от google flatbuffers. Если объем передаваемой информации не критичен, возможно будет удобным формировать данные в виде json (например с помощью https://github.com/nlohmann/json). Если на принимающей стороне JavaScript программист, он будет вам очень благодарен (да и не только JavaScript программист). Также, как программисту из типизированного языка, рекомендую использовать схемы для проверки json. Как альтернативу json можно взять messagepack, который "как json", но компактнее. Если нужно ещё компактнее, можно пожать передаваемую строку с помощью zlib например. Для всех вариантов также надо учитывать, что передавать указатели бессмысленно, т.к. на принимающей стороне они будут указывать неизвестно куда. Также понимать тонкости передачи данных по сети. К примеру данные, отправленные по UDP, могут не дойти до получателя, данные отправленные по TCP могут быть фрагментированы или склеены с соседними при получении и т.п. Возможно стоит подумать о готовых сетевых библиотеках, например RakNet, которая включает в себя практически все для построения мультиплеерной игры.

Ответ 2



В функциях отправки данных на другой сокет (например, send) и функциях приема данных (например, recv) одним из параметров всегда является указатель на буфер с этими данными (байтами). Необходимо предварительно сформировать этот буфер. Сделать это можно очень разными способами. Например, если структура передаваемых данных динамическая и/или таких структур очень много, то можно формировать буфер, так сказать, "на лету". Т.е. мы нужные данные постепенно, по мере их получения, запихиваем в буфер. std::string buffer; uint32_t i32 = 0x32fe56ad; float f = 1.0; std::string str = "1234"; uint8_t sz = str.size(); buffer.append((char*)&i32, sizeof(i32)); buffer.append((char*)&f, sizeof(f)); buffer.append((char*)&sz, sizeof(sz)); buffer.append(str); std::cout << "lenght message: " <

Ответ 3



Правильным подходом будет использование сериализации данных. Например: Protocol Buffers, JSON, XML, ASN.1, и т.п. Сравнительная таблица.

Как задать свой адрес Java сервера на сокетах?

#java #сервер #сокет #localhost


Есть простенький Java сервер на сокетах. Сервер запускается на локальной машине,
поэтому для доступа к нему нужно использовать адрес localhost. Как сделать доступ к
серверу не по имени localhost, а по какому-нибудь другому имени, например example.com
    


Ответы

Ответ 1



Вариант 1. Доступ к серверу нужен только с локальной машины. В этом случае достаточно внести изменения в файл %windir%\System32\drivers\etc\hosts, вписав в него строку 127.0.0.1 example.com Вариант 2. Доступ к серверу нужен с нескольких машин из локальной сети. В этом случае нужно чтобы сервер слушал не только адрес 127.0.0.1, но и адрес, указанный в настройках сетевой карты. Впрочем, можно не заморачиваться и слушать все адреса (0.0.0.0). На остальных машинах нужно также внести изменения в файл hosts. Вариант 3. Нужен доступ из сети Интернет. Это самый сложный случай. Для начала нужен "белый" статический IP-адрес. Этот адрес должен быть либо у сервера, либо у какого-либо устройства, способного "пробрасывать" нужные порты на ваш сервер. Далее у какого-либо регистратора покупаем доменное имя example.com и вносим в настройки этого доменного имени так называемую A-запись, в которой и будет указан "белый" статический IP-адрес.

Ответ 2



Если я правильно понял,в файле hosts у тебя должно быть следующее: 127.0.0.1 localhost добавь такую же строку и поменяй localhost на то, что тебе нужно

пятница, 24 января 2020 г.

Java socket timeout

#java #сокет


Проект

В общем вот ссылка на проект, реализована игра быки-коровы:
сервер генерирует "слово" из 4 цифр, а клиент пытается его отгадать. Налажено подключение
и отключение. Но не могу реализовать тайм аут, если пользователь или сервер долгое
время не получали сообщений.
Максимум что удавалось, это в контроллере Клиента вызывать в методе нажатия кнопки
после считывая данных функцию setOutTimeout, или которая бы сверяла System.currentMills
заданную перед проверкой и после, все в целом отрабатывает и закрывает сокет, но появляется
ошибка в Моделе: Stream closed.
    


Ответы

Ответ 1



В общем, реализовал как смог, с помощью костылей. class TimeoutThread extends Thread { private final int time; private boolean isStop = false; public TimeoutThread(int time) { super(); this.time = time; } public void stopCheckTimeout() { this.isStop = true; } @Override public void run() { try { System.out.println("start"); long endTime = System.currentTimeMillis() + time; count = time / 1000; while (!isStop) { count--; Thread.sleep(1000); System.out.println("ooo"); ClientPresenter.getInstance().handleResult(String.valueOf(count)); if (System.currentTimeMillis() > endTime) { System.out.println("disc"); disconnect(); break; } } System.out.println("finish"); } catch (Exception e) { e.printStackTrace(); } } И использовал: new Thread(() -> { TimeoutThread timeoutThread = new TimeoutThread(10000); while (status) { try { if (isReceive()) { timeoutThread.stopCheckTimeout(); //stop current timeout boolean gameFinished = check(); if(gameFinished) { return; } timeoutThread = new TimeoutThread(10000); // start new timeoutThread.start(); } } catch (Exception e) { status = false; } } }).start();

Передавать и принимать поток. Socket

#c_sharp #сокет #xaml #uwp #потоки_данных


Записываю картинку с камеры в поток:

var stream = new InMemoryRandomAccessStream();
await _mediaCapture.StartRecordToStreamAsync(encodingProfile,stream);


Задача: как-то разбить этот на байты и передать его по сокету, а на приеме снова
создать поток? Получается своего рода трансляция.

Поток необходимо шифровать и дешифровать с помощью aes. Метод шифрования реализован,
принимает массив байт. Как передать туда поток?
    


Ответы

Ответ 1



Вот такой код получается: private async Task SendStreamAsync(MediaEncodingProfile encodingProfile, MediaCapture mediaCapture) { var socket = new DatagramSocket(); var outputStream = await socket.GetOutputStreamAsync(new HostName("example.com"), "12345"); var writeOnlyStream = new WriteOnlyStreamStream(outputStream); await mediaCapture.StartRecordToStreamAsync(encodingProfile, writeOnlyStream); return new CompositeDisposable {writeOnlyStream, outputStream, socket}; } private sealed class WriteOnlyStreamStream : IRandomAccessStream { readonly IOutputStream _outputStream; public WriteOnlyStreamStream(IOutputStream outputStream) { _outputStream = outputStream; } public IInputStream GetInputStreamAt(ulong position) { throw new NotSupportedException(); //or we can return empty stream } public IOutputStream GetOutputStreamAt(ulong position) { return _outputStream; } public ulong Size { get { return 0; } set { } } public bool CanRead => false; public bool CanWrite => true; public IRandomAccessStream CloneStream() { throw new NotSupportedException(); } public ulong Position => 0; public void Seek(ulong position) { } public void Dispose() { this._outputStream.Dispose(); } public IAsyncOperationWithProgress ReadAsync(IBuffer buffer, uint count, InputStreamOptions options) { throw new NotSupportedException(); } public IAsyncOperationWithProgress WriteAsync(IBuffer buffer) { return _outputStream.WriteAsync(buffer); } public IAsyncOperation FlushAsync() { return _outputStream.FlushAsync(); } } Что здесь происходит: Создали Udp сокет (в нем пакеты будут теряться, биться, никакой целостности) Создали спец-класс обертку, чтобы пропихнуть IOutputStream внутрь MediaCapture. Здесь есть варианты, можно вместо исключений возвращать пустой IInputStream Создали IDisposable, который надо будет закрыть после всех операций (используем Reactive Extensions). Как правильнее посоветовали в вопросах, здесь лучше использовать TCP сокеты. Для клиентской части будет всё 1-в-1, только классы немного поменяются. Для серверной части надо использовать класс StreamSocketListener (он есть в uwp), с которого уже слушать какой-нибудь порт.

Не отправляются данные с клиента (андроид) на сервер (java pc)

#java #android #сокет #клиент_сервер


Доброе утро.

Есть клиент на андроиде, с него должна отправиться строка на сервер.
Запускаю клиент на реальном устройстве.

Вот код клиента:

public class Client extends Thread {

private boolean running = false;
private static Socket s = null;
private String ip = "тут мой ip";
private int port = 80;
private String data;
static DataInputStream din = null;
static DataOutputStream dout = null;

public Client(String name, int score){
    data = name + " " + score;
}

public void setRunning(boolean b){running = b;}

@Override
public void run() {
    while (running) {
        try {
            s = new Socket(ip, port);
            dout = new DataOutputStream(s.getOutputStream());
            din = new DataInputStream(s.getInputStream());
            dout.writeUTF(data);
        } catch (Exception ex) {}
    }
}


}

Все это действо должно отработать в другом классе, вот в этом методе:

private void sendData(String name, int score){
    client = new Client(name, score);
    client.setRunning(true);
    client.start();
}


Код сервера:

public class Server {


static ServerSocket ss;
static Socket s;
static DataInputStream din;
static DataOutputStream dout;

static String data = "";

public static void main(String[] args){
    try{
        System.out.println("Сервер запущен\n");
        ss = new ServerSocket(80);
        s = ss.accept();

        din = new DataInputStream(s.getInputStream());
        dout = new DataOutputStream(s.getOutputStream());

        while(true){
            data = din.readUTF();
            System.out.println("Score: " + data);
        }

    } catch(Exception ex){ex.printStackTrace();}
    System.out.println("Сервер остановлен");
}


}

Wi-fi на устройстве включил, ip точно мой, все по-идее должно работать.
Клиент в отдельном потоке, не понимаю тогда в чем дело.
Подскажите, пожалуйста.
    


Ответы

Ответ 1



На стороне клиента: Client.java: import java.util.*; import java.net.*; import java.io.*; public class Client extends Thread { //Все null,false,0 по умолчанию. Можно не писать. private boolean running; private Socket s; private String ip = "192.168.1.4"; private int port = 1234; static DataInputStream din; static DataOutputStream dout; public void setRunning(boolean b){running = b;} @Override public void run() { try { s = new Socket(ip, port); dout = new DataOutputStream(s.getOutputStream()); din = new DataInputStream(s.getInputStream()); while (running); } catch (Exception ex) { ex.printStackTrace(); } finally { close(); } } public boolean isConnected() { return s!=null&&s.isConnected(); } public void sendMessage(String message) { if(dout!=null&&running) { try { dout.writeUTF(message); dout.flush(); } catch(Exception ex) { running = false; } } } public void close() { if(din!=null) try{din.close();}catch(IOException io){} if(dout!=null) try{dout.close();}catch(IOException io){} if(s!=null) try{s.close();}catch(IOException io){} } } Вызов класса: Client clt = new Client(); clt.setRunning(true); clt.start(); while(!clt.isConnected()); clt.sendMessage("Hello"); clt.sendMessage(", World!"); clt.close(); Манифест: На стороне сервера: Server.java: static ServerSocket ss; static Socket s; static DataInputStream din; static DataOutputStream dout; static String data = ""; public static void main(String[] args){ try{ System.out.println("Сервер запущен\n"); ss = new ServerSocket(1234); s = ss.accept(); din = new DataInputStream(s.getInputStream()); dout = new DataOutputStream(s.getOutputStream()); while((data = din.readUTF())!=null){ System.out.println("Score: " + data); } } catch(Exception ex){ ex.printStackTrace(); } finally { if(dout!=null) try{dout.close();}catch(IOException io){} if(din!=null) try{din.close();}catch(IOException io){} if(s!=null) try{s.close();}catch(IOException io){} if(ss!=null) try{ss.close();} catch(IOException io){} } System.out.println("Сервер остановлен"); }

Прослушивание массива Stream'ов в Java

#java #сокет #потоки_данных #epoll #android_bluetooth


Существует ли в Java какой-либо специфический механизм мультиплексирования событий
ввода-вывода, подобный epoll?

P.S. В частности меня интересует прослушка массива BluetoothSocket'ов (Android).
    


Ответы

Ответ 1



Ответ - нет. Если программа в Java многосокетная, обычно создаються Thread'ы на каждое соединение. Java - многоплатформенная система, и не на всех платформах может быть аналог epoll. PS. Попробуй java.nio.channels.Selector, теоритичечки он должен работать и с Bluetooth соединениями.

воскресенье, 12 января 2020 г.

Работа с AF_UNIX SOCK_DGRAM SO_BROADCAST сокетом

#c #сокет #unix #select #unix_socket


Здравствуйте, пишу чатик, начал с параллельного эхо-сервера.
Теперь пытаюсь сделать так, чтобы сообщения от одного подключенного клиента рассылались
всем подключенным клиентам.
В процессе-родителе создаю сокет в файловом пространстве, и делаю его широковещательным:
  int commfd;
  struct sockaddr commaddr;

  if( ( commfd = socket( AF_UNIX, SOCK_DGRAM, 0 ) ) < 0 )
    perror( "error in common socket" );

  bzero( &commaddr, sizeof( commaddr ) );
  commaddr.sa_family = AF_UNIX;
  strcpy( commaddr.sa_data, COMM_SOCK_NAME );

  if( bind( commfd, &commaddr, sizeof( commaddr ) ) < 0 )
    perror( "error in bind for commfd" );

  int n=1;
  if( setsockopt( commfd, SOL_SOCKET, SO_BROADCAST, &n, sizeof(n) ) < 0 )
    perror( "error in setsockopt" );

И в дочернем процессе-обработчике клиента подключаюсь к этому сокету, и через select
жду, пока сокет этот будет доступен на чтение. 
В общем суть задумки: клиент пишет сообщение серверу (а на сервере для этого клиента
свой процесс-обработчик), это сообщение отсылается назад этому же клиенту, и это же
сообщение пишется в широковещательный сокет. В это же время процессы - обработчики
других клиентов ждут того самого сообщения, принимают, и выводят на экран.
Но не срабатывает select в обработчике, что я не так делаю?
Весь код сервера здесь: http://paste.ubuntu.com/7617694/    


Ответы

Ответ 1



UNIX-cокеты не поддерживают широковещательную передачу. Приём-передача широковещательных udp-пакетов осуществляется с использованием (кроме опции SO_BROADCAST) широковещательных адресов, которые не предусмотрены в AF_UNIX. Так же невозможно забиндить более одного сокета на один AF_UNIX-адрес, что потребовалось бы для приёма.