salut,
j'envisage de gerer un protocole de communication entre deux applications en sa basant sur UDP en unicast et je voulais savoir si la taille d'un message a envoyer est limitée par une taille donnée?
merci
salut,
j'envisage de gerer un protocole de communication entre deux applications en sa basant sur UDP en unicast et je voulais savoir si la taille d'un message a envoyer est limitée par une taille donnée?
merci
Oui, c'est limité par la couche du dessous, a savoir IP, puis par la taille max des datagrammes.
Au maximum un datagramme peut faire 65535 octets.
Au maximum un packet IP peut contenir 65535 - 20 (header ip) = 65515 octets
Au maximum un packet UDP peut contenir 65515 - 8 (header udp) = 65507 octets
Il faut faire attention que ce sont les tailles maximum theorique.
En pratique, windows limite la taille des datagrammes a 8192.
Donc sous windows un packet UDP peut contenir 8192 - 20 - 8 = 8164 octets
ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.
Ok je vois , mais pourquoi on distingue un packet d'un datagramme?
quelle est la difference? c'est trés important de moi de savoir sachant que tout ce u eje veux c'est envoyer de données selon un format qui m'a été exigé à une application distante sur le même LAN et en unicast( j'aimerai aussi savoir la difference entre unicast et multicast)![]()
merci infiniment
Bof. Datagramme/Paquet, c'est un peu equivalent. Generalement on dit datagramme lorsqu'on parle de la "trame" complete = tous les octets bout a bout.
Le packet c'est une suite d'octets, generalement composé d'un header et de données. Donc dans un datagramme, tu as un packet (header+données). Dans les données de ce packet, tu as un autre packet , et ainsi de suite. C'est ce qu'on appelle l'encapsulation.
Pour faire simple, l'unicast c'est l'envoie d'un datagramme par un emeteur a un destinataire. Le multicast c'est l'envoie d'un datagramme par un emeteur a plusieurs destinataires (et meme a tous les destinataires possibles)
ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.
Ok super je vois clair maintenant et un exemple m'a permis de mieux cerner les principes aussi :
http://sardes.inrialpes.fr/~jean/ens...reseau/PDF.pdf
cependant je suis face a une petite contrainte technique, étant donné que UDP est en mode non connecté et qu'il s'agit d'une connexion inicast ( exigée) comment mettre en ouvre un mecanisme d'acquittement de reception des datagrammes? on m'a demandé de le mettre en ouvre en respectant plusieurs timeout selon des cas bien défini(type de datagramme) ...donc comment est ce possible d'implementer cela?
merci
Si tu as suivi ce que j'ai dit avant, tu peux encapsuler a l'infini (ou presque) des paquets... a toi de creer ton propre "protocole" encapsulé dans UDP pour gerer tes acquitements et timeout:
Tu peux t'inspirer de protocoles existants comme RTP (Real-time Transport Protocol).
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 |----------------------------------------------| = Trame (8192 octets) |--20--|----------------8172-------------------| = IP (20+8172 octets) |-8-|--------------8164-----------------| = UDP (8+8164 octets) |--X--|------------Y----------------| = TonProtocole (X+Y=8164 octets)
ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.
merci,
pour ce qui timeout je voulais savoir si va concerne que l'envoie d'un message donné ou bien y en a d'autres?
Le seul timeout que tu peux gerer en UDP+Unicast, c'est le delai entre l'envoi d'un packet et la reception de l'acquittement du(des) receveur(s).Envoyé par jlassiramzy
Donc ca implique forcement que ton protocole gere les acquitements, et que ton "emetteur" maintienne une liste des acquitements attendus.
ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.
Salut,
tu veux dire que dans le cas de l'unicast il y a un seul timeout c'est ca ? de même que dans le cas du multicast?
Non. Je veux dire que dans UDP (unicast ou multicast) il n'y a aucune gestion. On ne sait meme pas si le message est correctement arrivéEnvoyé par jlassiramzy
.
C'est dans TON protocole que tu dois tout gerer. Donc en priorité, tu dois gerer l'acquittement de la bonne reception des paquets par le(s) receveur(s).
- Pour des raisons de perf, les receveur ne sont pas obligés de renvoyer une trame d'acquittement pour chaque paquet emis (1 trame emise ==> 1 trame acquittement x Nbre de client !!). Tu peux, par exemple, envoyer une trame d'acquittement tous les 10 ou 100 trames recues.
- Apres tu peux ajouter dans ton protocole des numeros de trames (1,2,3,...). Comme ca le receveur peut savoir s'il a "perdu" des trames. Il peut ainsi renvoyer dans la trame d'acquittement le % de trame recues/perdues.
- Apres tu peux ajouter dans ton protocole les date/heure d'envoie des trames. Comme ca le receveur peut savoir le temps de transit des trames (lag). Il peut ainsi renvoyer dans la trame d'acquittement le temps min/moyen/max de transit des trames.
etc.
Toutes ces infos peuvent permettre a l'emetteur d'ajuster des parametres (vitesse d'emission, durée du timeout, ...)
ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.
Ok je vois plus clair maintenant grâce a toi
j'ai cependant une question qui m'intrigue au niveau codage; au fait étant donné que les données que l'on envoie sont de type byte[] j'ai un soucis a implementer differents type de données que j'essaie d'envoyer dans ma trame j'explique: supposons que je veux envoyer les données suivantes:
Prenom ( type String)
Age ( type Int)
Moyenne(type float)
et ce concaténé de la façon suivante:
Comment je peux envoyer ce type de message sous forme de byte[] sachant qu'initialement ils sont de types differents? en plus comment connaitre préalablement la taille du tableau byte a envoyer dans ce cas?
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 Age=age#Prenom=prenom#Moyenne=moyenne
merci![]()
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 String s = "Age=age#Prenom=prenom#Moyenne=moyenne"; byte[] bytearray = s.getBytes(); int length = bytearray.length;
ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.
je m'en doutais....mais pourquoi doit on dans certains cas spécifier un charsetName? a quoi ca sert ?
merci
ca sert a specifier l'encodage des caracteres...Envoyé par jlassiramzy
Pour les carateres ascii y a pas trop de probleme. 1 caractere <-> 1 octet (de 0-127): A=65, B=66, C=67, ...
Quand tu rajoutes les caracteres accentués francais (éàè...) on arrive a tout faire faire tenir dans 1 octet (de 0-255)
Quand tu rajoutes les caracteres accentués suedois (A°), les carateres russes, les glyphes chinois ca devient impossible de faire correspondre 1 caractere <-> 1 octet. Les 255 valeurs ne suffisent pas !!
Donc il faut changer la methode d'encodage. Generalement on prend UTF-8. C'est un encodage a taille variable. L'ascii sur 1 octet, les caracteres accentués sur 2 octets, les glyphes sur 3 octets, ....
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6 // string -> byte utf8 byte[] bytearray = s.getBytes(Charset.forName("UTF-8")); // byte utf8 -> string String s = new String(bytearray,Charset.forName("UTF-8"));
ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.
j'ai essayé de suivre tes consignes et j'ai mis en place le client ets erveur suivants: mais les données reçues sont affichées bizarrementpourquoi a ton avis?
et voiçi le code client:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144 import java.net.*; import java.io.*; import javax.swing.*; import java.awt.*; import java.awt.event.*; public class UDPServeur extends JFrame{ private int port; private InetSocketAddress sa = new InetSocketAddress("localhost", 5000); private DatagramSocket server_socket = null; private JTextArea textAreaStatutServeur; private JTextArea textAreaMessages; private Container contentPane; private int no_client=0; public UDPServeur(){ remplirFenêtre(); demanderPort(); //création_socket(); while(true){ attente_connexion(); //new MultiServerThread(socket,no_client,textAreaStatutServeur, //textAreaMessages).start(); } } //Remplissage et affichage de la JFrame private void remplirFenêtre(){ textAreaMessages=new JTextArea(); textAreaMessages.setEditable(false); JScrollPane jsp1=new JScrollPane(textAreaMessages); textAreaStatutServeur=new JTextArea(); textAreaStatutServeur.setEditable(false); JScrollPane jsp2=new JScrollPane(textAreaStatutServeur); JButton boutonDéconnecter=new JButton("Déconnecter"); boutonDéconnecter.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent evt){ fermeture_connexion_serveur(); textAreaMessages.append("Emission perdue..."); //System.exit(0); } }); contentPane=this.getContentPane(); contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS)); contentPane.add(jsp1); contentPane.add(jsp2); contentPane.add(boutonDéconnecter); this.setTitle("IDU"); this.setSize(500,400); this.setVisible(true); } private void demanderPort(){ Dialogue d=new Dialogue(this,"Numéro du port du serveur"); String s=d.getString(); try { port = Integer.parseInt(s); } catch (Exception e) { textAreaStatutServeur.append(e.toString()); } } private void création_socket(){ try { //Ouverture d'une socket serveur sur le port donné en paramètre server_socket = new DatagramSocket(port); textAreaStatutServeur.append("Serveur en attente de clients à " + "<" + server_socket.getInetAddress() + " , " + server_socket.getLocalPort() + ">" + "\n"); } catch (IOException e) { textAreaStatutServeur.append(e.toString()); } } private void attente_connexion(){ //socket = server_socket.accept(); no_client++; try { server_socket = new DatagramSocket(sa); } catch (SocketException e) { textAreaStatutServeur.append(e.toString()); } DatagramPacket p = new DatagramPacket(new byte[146], 146); textAreaMessages.append("Prêt à recevoir"); try { server_socket.receive(p); } catch (IOException e) { textAreaStatutServeur.append(e.toString()); } String s = ""; try { s = new String(p.getData(), "UTF-8"); textAreaMessages.append("Recu : "+s); } catch (IOException e) //UnsupportedEncodingException { textAreaStatutServeur.append(e.toString()); } try { Thread.sleep(5000); } catch (InterruptedException e) { textAreaStatutServeur.append(e.toString()); } } private void fermeture_connexion_serveur(){ server_socket.close(); } public static void main(String args[]) { UDPServeur tcp=new UDPServeur(); } }
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176 import java.net.*; import java.io.*; import javax.swing.*; import com.sagem.module.task.CalculatorSimulator; import java.awt.*; import java.awt.event.*; public class UDPClient extends JFrame{ private String serveur; private int port; private Socket socket; private JTextArea textAreaStatutClient; private JTextArea textAreaMessages; private Container contentPane; public UDPClient(){ remplirFenêtre(); demanderDescriptionServeur(); connexion_serveur(); } //Remplissage et affichage de la JFrame private void remplirFenêtre(){ textAreaMessages = new JTextArea(); textAreaMessages.setEditable(true); JScrollPane jsp1 = new JScrollPane(textAreaMessages); textAreaStatutClient = new JTextArea(); textAreaStatutClient.setEditable(false); JScrollPane jsp2 = new JScrollPane(textAreaStatutClient); JButton boutonEnvoyer = new JButton("Envoyer UDP"); JButton boutonDéconnecter = new JButton("Déconnecter UDP"); boutonEnvoyer.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent evt){ envoi_données(); } }); boutonDéconnecter.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent evt){ fermeture_connexion(); System.exit(0); } }); contentPane=this.getContentPane(); contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS)); contentPane.add(jsp1); contentPane.add(jsp2); JPanel panel=new JPanel(); panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); panel.add(boutonEnvoyer); panel.add(boutonDéconnecter); contentPane.add(panel); this.setTitle("NAVCONS"); this.setSize(500,300); this.setVisible(true); } // genberer des donnees aleatoires private String sendData() { String value ="nothing"; CalculatorSimulator test = new CalculatorSimulator(); test.GenerateData(); value = "l_Cog:"+test.l_Cog+"#"+"l_Sog: " +test.l_Sog +"#"+ "l_Pitch: " + test.l_Pitch+"#"; return value; } private void demanderDescriptionServeur(){ Dialogue d=new Dialogue(this,"Nom du serveur"); serveur=d.getString(); d=new Dialogue(this," Specifier le numéro du port du serveur"); String s=d.getString(); try { port = Integer.parseInt(s); } catch (Exception e) { } } private void connexion_serveur(){ try { //Essai de connexion au serveur (nom du serveur, port du serveur) socket = new Socket(serveur, port); socket.setSoTimeout(1000); textAreaStatutClient.append("Connecté au serveur " + "<" + socket.getInetAddress() + " , " + socket.getPort() + ">" + "\n"); } catch (UnknownHostException e) { textAreaStatutClient.append(e.toString()); } catch (SocketTimeoutException e) { textAreaStatutClient.append("TimeOut Depassé..."+e.toString()); } catch ( IOException e) { textAreaStatutClient.append(e.toString()); } } private void envoi_données(){ //PrintWriter output = new PrintWriter(socket.getOutputStream(),true); //String message=sendData(); //textAreaMessages.getText(); //output.println(message); textAreaMessages.setText("Emission done"); // args 0 : l'adresse distante en notation pointée // args 1 : le port distant // args 2 : le message à envoyer InetSocketAddress sa = new InetSocketAddress("localhost", 5000); DatagramSocket ds = null; DatagramPacket p = null; byte[] buf = null; String data = "ok voiçi notre premiere tentative...\n"+sendData(); try { buf = data.getBytes("UTF-16"); System.out.println("taille buffer..."+buf.length); } catch (UnsupportedEncodingException e) { textAreaStatutClient.append(e.toString()); } try { p = new DatagramPacket(buf,buf.length,sa); ds = new DatagramSocket(); } catch (SocketException e) { textAreaStatutClient.append(e.toString()); } try { ds.setSoTimeout(2000); ds.send(p); } catch (IOException e) { textAreaStatutClient.append("Problem While sending data to remote server.."); } } private void fermeture_connexion(){ try { socket.close(); } catch (IOException e) { textAreaStatutClient.append(e.toString()); } } public static void main(String[] args) { UDPClient tcp = new UDPClient(); } }
Petite erreur entre les formats d'encodage et de decodage![]()
Dans le client:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6 String data = "ok voiçi notre premiere tentative...\n"+sendData(); try { buf = data.getBytes("UTF-16"); // <---- devrait etre UTF-8 !!!! System.out.println("taille buffer..."+buf.length); }
ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.
au fait j'ai essayé mais tous les caracteres qui sont au dela de la taille prédéfini du buffer que je récupere sont affichées bizarrement!!![]()
Oui c'est normal.Envoyé par jlassiramzy
Le byte[] que tu recoit coté serveur en faisant p.getData() contient la totalité du buffer de trame. Donc tes données plus du garbage.
La methode p.getLength() te permet de savoir combien il y a d'octets "a toi" dans le buffer de trame. Le reste etant le garbage.
Tu as donc 2 choix:
- recontruire un byte[] de la bonne taille, et construire ta String normalement
ou
- utiliser le byte[] avec le garbage et reconstuire ta String avec seulement les octets utiles.
La 2eme solution est plus simple en java, car le constructeur de String permet de specifier le nombre d'octet qu'on veut lire dans le byte[]:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 { s = new String(p.getData(), 0, p.getLength(), "UTF-8"); textAreaMessages.append("Recu : "+s); }
ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.
super je vais essayer cela
cependant une autre question ( je sais que j'abuse) : pourrais agir autrement au niveau des données que j'envoie? genre je construis le byet[] que j'envoie morceau par morceau ( une série de champs de differents types : int, String, Float...) au lieu de les concatenener dans un flux classqiue et appliquer la methode getBytes() ? est ce possible?
il y a meme mieux. Tu peux creer une classe en Java avec tous les attributs que tu veux emettre/recevoir
Et tu peux demander a Java de te transformer une instance de cette classe en binaire (byte[]) puis de retransformer le binaire en instance de classe
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 class MaTrame { int monint; double mondouble; String mastring; ... }![]()
Ca s'appelle la sérialisation binaire, et il y a un tuto ici meme.
ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.
Partager