Bonjour,

depuis quelques jours, j'essaye de réaliser un salon de discussion un peu compliqué, avec la possibilité de changer de pseudo/image, avec plusieurs salons disponibles.
Pour cela j'ai beaucoup cherché sur internet des exemples de code, et je me suis inspiré du très bon article de http://msdn.microsoft.com/en-us/magazine/cc300760.aspx (Pour la suite de mon post, je me suis inspiré en grande partie de la "Figure 7 Asynchronous Server" du lien précédent, mais j'ai également fait la "Figure 5 Simple Threaded Server" en désespoir de cause...).

Bref, le problème étant que je n'ai jamais utilisé de buffer pour l'envoie des données, et cela me pose problème. En effet il arrive que le serveur passe en "WouldBlock" j'ai donc ajouté ce bout de code que j'ai trouvé sur internet :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
 
            catch (SocketException exc)
            {
                if (exc.SocketErrorCode == SocketError.WouldBlock)
                {
                    Thread.Sleep(30);
                }
Mais j'ai vraiment pas l'impression que cela aide. De plus, j'ai compris que l'envoie des données se fait en respectant l'algorithme de Nagle. Ce qui fait que si le serveur se fait "spammer", le client peut recevoir des données coupées du type :

"Phrase1`Phrase2`Phrase3`Phra"

Alors je ne sais pas du tout si je fait quelque chose de mal. Mais j'ai cru comprendre qu'il fallait utiliser des délimiteurs de "Lignes". Le fait est que du coup les données coté client arrivant en bout de buffer sont intraitables.
La première solution serait de sauvegarder les données arrivant en fin, et de les juxtaposer avec celles arrivant en début de buffer suivant. Mais j'ai envie d'avoir votre avis avant de faire ça, car j'ai l'impression de faire fausse route.

Partie traitement des données
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
 
        private void ReceiveCallback(IAsyncResult result)
        {
            ClientConnectionInfo connection = (ClientConnectionInfo)result.AsyncState;
            try
            {
                int bytesRead = connection.Socket.EndReceive(result);
                if (bytesRead > 0)
                {
                    String data = System.Text.Encoding.UTF8.GetString(connection.Buffer).TrimEnd('\0').Trim();
                    String[] allDatas = data.Split('`');
                    foreach (String l in allDatas)
                    {
                        if (l.Split('@').ElementAt(0).Equals("ConnectionAuSalon"))
                        {
                            // Traitement
                        }
                        else if (l.Split('@').ElementAt(0).Equals("DeconnectionDuSalon"))
                        {
                            //Traitement
                        }
                        else if (l.Equals("QuitteApplication"))
                        {
                            // Traitement
                        }
                        else if (l.Equals("ArriveApplication"))
                        {
                     connection.pseudo = generationAleatoirePseudo();                          connection.Socket.Send(Encoding.ASCII.GetBytes("TonPseudo@" + connection.pseudo + '`'), ("TonPseudo@" + connection.pseudo + '`').Length, SocketFlags.None);
                        }
                        else if (l.Split('@').ElementAt(0).Equals("VerifieDisponnibilitePseudo"))
                        {
                            if (isNicknameInUse(l.Split('@').ElementAt(1)))
                            {
                                connection.Socket.Send(System.Text.ASCIIEncoding.ASCII.GetBytes("NonDisponnible" + '`'), ("NonDisponnible" + '`').Length, SocketFlags.None);
                            }
                            else
                            {
                                connection.Socket.Send(System.Text.ASCIIEncoding.ASCII.GetBytes("Disponnible" + '`'), ("Disponnible" + '`').Length, SocketFlags.None);
                            }
                        }
                        else if (l.Split('@').ElementAt(0).Equals("Message"))
                        {
                            SendNewMessageToSpecificChat(connection.IDSalon, connection.pseudo, l.Substring(8, l.Length - 8));
                        }
                        else if (l.Split('@').ElementAt(0).Equals("ModificationPseudo"))
                        {
                            modifyClientPseudo(connection, l.Split('@').ElementAt(1));
                            connection.pseudo = l.Split('@').ElementAt(1);
                        }
                        else if (l.Split('@').ElementAt(0).Equals("ModificationImage") || l.Split('@').ElementAt(0).Equals("MonImage"))
                        {
                            modifyClientImage(connection, l.Split('@').ElementAt(1));
                            connection.image = l.Split('@').ElementAt(1);
                        }
                    }
                    connection.Socket.BeginReceive(connection.Buffer, 0, connection.Buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), connection);
                }
                else CloseConnection(connection);
            }
            catch (SocketException exc)
            {
                if (exc.SocketErrorCode == SocketError.WouldBlock)
                {
                    Thread.Sleep(30);
                    Console.WriteLine("Socket exception: [" + exc.SocketErrorCode + "] has been handled");
                }
                else
                {
                    CloseConnection(connection);
                    Console.WriteLine("Socket exception: " + exc.SocketErrorCode);
                }
            }
            catch (Exception exc)
            {
                CloseConnection(connection);
                Console.WriteLine("Exception: " + exc);
            }
        }

L'essentiel du code du serveur (notamment pour la déclaration du buffer etc, correspond exactement au code de la Figure 7 du lien précédent). Je préfère cependant le rajouter ci-dessous si quelqu'un souhait y avoir accès :
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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
 
    public class AsynchronousIoServer
    {
        private Socket _serverSocket;
        private int _port;
        private String _ip;
 
        private class ClientConnectionInfo
        {
            public Socket Socket;
            public byte[] Buffer;
            public String image;
            public String pseudo;
            public String IDSalon = null;
        }
 
        private Thread _acceptThread;
        private List<ClientConnectionInfo> _allClients = new List<ClientConnectionInfo>();
 
        private UC.Chats _adminChat;
        private Dictionary<string, List<ClientConnectionInfo>> _inRoomsClients = new Dictionary<string, List<ClientConnectionInfo>>();
 
        public AsynchronousIoServer(int port, UC.Chats adminChat) { _port = port; _adminChat = adminChat; }
 
        private void SetupServerSocket()
        {
            // Resolving local machine information
            IPHostEntry localMachineInfo = Dns.GetHostEntry(Dns.GetHostName());
            IPEndPoint myEndpoint = new IPEndPoint(localMachineInfo.AddressList[1], _port);
 
            // Create the socket, bind it, and start listening
            _serverSocket = new Socket(myEndpoint.Address.AddressFamily,
                SocketType.Stream, ProtocolType.Tcp);
            _serverSocket.Bind(myEndpoint);
            _serverSocket.Listen((int)SocketOptionName.MaxConnections);
        }
 
        public void Start()
        {
            SetupServerSocket();
            for (int i = 0; i < 10; i++)
                _serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), _serverSocket);
        }
 
        private void AcceptCallback(IAsyncResult result)
        {
            ClientConnectionInfo connection = new ClientConnectionInfo();
            try
            {
                // Finish Accept
                Socket s = (Socket)result.AsyncState;
                connection.Socket = s.EndAccept(result);
                connection.Buffer = new byte[255];
 
                // Start Receive and a new Accept
                connection.Socket.BeginReceive(connection.Buffer, 0, connection.Buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), connection);
                _serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), result.AsyncState);
            }
            catch (SocketException exc)
            {
                CloseConnection(connection);
                Console.WriteLine("Socket exception: " + exc.SocketErrorCode);
            }
            catch (Exception exc)
            {
                CloseConnection(connection);
                Console.WriteLine("Exception: " + exc);
            }
        }
        private void ReceiveCallback(IAsyncResult result)
        {
            ClientConnectionInfo connection = (ClientConnectionInfo)result.AsyncState;
            try
            {
                int bytesRead = connection.Socket.EndReceive(result);
                if (bytesRead > 0)
                {
                    String data = System.Text.Encoding.UTF8.GetString(connection.Buffer).TrimEnd('\0').Trim();
                    String[] allDatas = data.Split('`');
                    _adminChat.addTextToTab("General", data + "[[[[[[" + bytesRead);
                    foreach (String l in allDatas)
                    {
                        if (l.Split('@').ElementAt(0).Equals("ConnectionAuSalon"))
                        {
                            connection.IDSalon = l.Split('@').ElementAt(1);
                            SendSysMsgToSpecificChat(connection.IDSalon, String.Format("{0:[HH:mm:ss]} ", DateTime.Now) + connection.pseudo + " nous à rejoins !");
                            _adminChat.addTextToTab(connection.IDSalon, String.Format("{0:[HH:mm:ss]} ", DateTime.Now) + connection.pseudo + " nous à rejoins !");
                            addToSpecificChat(connection.IDSalon, connection);
                        }
                        else if (l.Split('@').ElementAt(0).Equals("DeconnectionDuSalon"))
                        {
                            removeFromSpecificChat(connection.IDSalon, connection);
                            SendSysMsgToSpecificChat(connection.IDSalon, String.Format("{0:[HH:mm:ss]} ", DateTime.Now) + connection.pseudo + " est parti(e) !");
                            _adminChat.addTextToTab(connection.IDSalon, String.Format("{0:[HH:mm:ss]} ", DateTime.Now) + connection.pseudo + " est parti(e) !");
                            connection.IDSalon = null;
                        }
                        else if (l.Equals("QuitteApplication"))
                        {
                            _adminChat.addTextToTab("INFORMATION", String.Format("{0:[HH:mm:ss}", DateTime.Now) + "] {" + connection.Socket.RemoteEndPoint + "} (" + connection.pseudo + ") a quitté l'appli");
 
                            removeClientFromAllClients(connection);
                            CloseConnection(connection);
                        }
                        else if (l.Equals("ArriveApplication"))
                        {
                            connection.pseudo = generationAleatoirePseudo();
                            addClientToAllClients(connection);
 
                            connection.Socket.Send(Encoding.ASCII.GetBytes("Pseudo@" + connection.pseudo + '`'), ("Pseudo@" + connection.pseudo + '`').Length, SocketFlags.None);
                            //connection.Socket.Send(System.Text.ASCIIEncoding.ASCII.GetBytes("Pseudo@" + connection.pseudo), ("Pseudo@" + connection.pseudo).Length, SocketFlags.None);
                            sendChatRoomsToClient(connection);
 
                            _adminChat.addTextToTab("INFORMATION", String.Format("{0:[HH:mm:ss}", DateTime.Now) + "] {" + connection.Socket.RemoteEndPoint + "} (" + connection.pseudo + ") a rejoint l'appli");
                        }
                        else if (l.Equals("IFORCEDQUITTED"))
                        {
                            _adminChat.addTextToTab("INFORMATION", String.Format("{0:[HH:mm:ss}", DateTime.Now) + "] {" + connection.Socket.RemoteEndPoint + "} (" + connection.pseudo + ") SUCCESSFULLY FORCED QUIT");
                        }
                        else if (l.Split('@').ElementAt(0).Equals("VerifieDisponnibilitePseudo"))
                        {
                            if (isNicknameInUse(l.Split('@').ElementAt(1)))
                            {
                                connection.Socket.Send(System.Text.ASCIIEncoding.ASCII.GetBytes("NonDisponnible" + '`'), ("NonDisponnible" + '`').Length, SocketFlags.None);
                            }
                            else
                            {
                                connection.Socket.Send(System.Text.ASCIIEncoding.ASCII.GetBytes("Disponnible" + '`'), ("Disponnible" + '`').Length, SocketFlags.None);
                            }
                        }
                        else if (l.Split('@').ElementAt(0).Equals("Message"))
                        {
                            _adminChat.addTextToTab(connection.IDSalon, String.Format("{0:[HH:mm:ss}", DateTime.Now) + "] {" + connection.Socket.RemoteEndPoint + "} (" + connection.pseudo + ") a écrit " + l.Substring(8, l.Length - 8));
                            SendNewMessageToSpecificChat(connection.IDSalon, connection.pseudo, l.Substring(8, l.Length - 8));
                        }
                        else if (l.Split('@').ElementAt(0).Equals("ModificationPseudo"))
                        {
                            _adminChat.addTextToTab("INFORMATION", String.Format("{0:[HH:mm:ss}", DateTime.Now) + "] {" + connection.Socket.RemoteEndPoint + "} (" + connection.pseudo + ") a changé son pseudo en " + (l.Split('@').ElementAt(1)));
 
                            modifyClientPseudo(connection, l.Split('@').ElementAt(1));
                            connection.pseudo = l.Split('@').ElementAt(1);
                        }
                        else if (l.Split('@').ElementAt(0).Equals("ModificationImage") || l.Split('@').ElementAt(0).Equals("MonImage"))
                        {
                            modifyClientImage(connection, l.Split('@').ElementAt(1));
                            connection.image = l.Split('@').ElementAt(1);
                        }
                    }
                    connection.Socket.BeginReceive(connection.Buffer, 0, connection.Buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), connection);
                }
                else CloseConnection(connection);
            }
            catch (SocketException exc)
            {
                if (exc.SocketErrorCode == SocketError.WouldBlock)
                {
                    Thread.Sleep(30);
                    Console.WriteLine("Socket exception: [" + exc.SocketErrorCode + "] has been handled");
                }
                else
                {
                    CloseConnection(connection);
                    Console.WriteLine("Socket exception: " + exc.SocketErrorCode);
                }
            }
            catch (Exception exc)
            {
                CloseConnection(connection);
                Console.WriteLine("Exception: " + exc);
            }
        }
 
        private void CloseConnection(ClientConnectionInfo ci)
        {
            ci.Socket.Close();
            lock (_allClients) _allClients.Remove(ci);
        }
 
        private void addToSpecificChat(String ID, ClientConnectionInfo infos)
        {
            lock (_inRoomsClients) 
                _inRoomsClients[ID].Add(infos);
 
            lock (_inRoomsClients[ID])
            {
                foreach (ClientConnectionInfo c in _inRoomsClients[ID].ToArray())
                {
                    infos.Socket.Send(System.Text.ASCIIEncoding.ASCII.GetBytes("Connection@" + c.pseudo + ";" + c.image + '`'), ("Connection@" + c.pseudo + ";" + c.image + '`').Length, SocketFlags.None);
                }
            }
            lock (_inRoomsClients[ID])
            {
                foreach (ClientConnectionInfo c in _inRoomsClients[ID].ToArray())
                {
                    if (c != infos)
                    {
                        c.Socket.Send(System.Text.ASCIIEncoding.ASCII.GetBytes("Connection@" + infos.pseudo + ";" + infos.image + '`'), ("Connection@" + infos.pseudo + ";" + infos.image + '`').Length, SocketFlags.None);
                    }
                }
            }
        }
        private void removeFromSpecificChat(String ID, ClientConnectionInfo infos)
        {
            lock (_inRoomsClients)
                _inRoomsClients[ID].Remove(infos);
 
            lock (_inRoomsClients[ID])
            {
                foreach (ClientConnectionInfo c in _inRoomsClients[ID].ToArray())
                {
                    c.Socket.Send(System.Text.ASCIIEncoding.ASCII.GetBytes("Deconnection@" + infos.pseudo + '`'), ("Deconnection@" + infos.pseudo + '`').Length, SocketFlags.None);
                }
            }
        }
        private void removeClientFromAllClients(ClientConnectionInfo infos)
        {
            lock (_allClients)
                _allClients.Remove(infos);
 
            lock (_inRoomsClients)
            {
                foreach (String IDliste in _inRoomsClients.Keys)
                    if (_inRoomsClients[IDliste].Contains(infos))
                    {
                        _inRoomsClients[IDliste].Remove(infos);
                        removeFromSpecificChat(IDliste, infos);
                        //.SendSysMsgToSpecificChat(IDliste, String.Format("{0:[HH:mm:ss]} ", DateTime.Now) + infos.pseudo + " est parti(e) de manière INEDITE !");
                    }
            }
            infos.Socket.Close();
        }
        private void addClientToAllClients(ClientConnectionInfo infos)
        {
            lock (_allClients)
                _allClients.Add(infos);
        }
        private String generationAleatoirePseudo()
        {
            String pseud = "";
            Boolean isFound = true;
 
            if (_allClients != null)
            {
                while (isFound)
                {
                    isFound = false;
                    pseud = "Pseudonyme-" + new Random().Next(1, 1000);
                    lock (_allClients)
                    {
                        foreach (ClientConnectionInfo c in _allClients.ToArray())
                        {
                            if (c.pseudo.Equals(pseud))
                                isFound = true;
                        }
                    }
                }
                return pseud;
            }
            else
            {
                return "Pseudonyme-" + new Random().Next(1, 1000);
            }
 
        }
        private void sendChatRoomsToClient(ClientConnectionInfo infos)
        {
            lock (_inRoomsClients)
            {
                foreach (String nom in _inRoomsClients.Keys.ToArray())
                {
                    /*byte[] buffer = System.Text.Encoding.ASCII.GetBytes("NameOfChatRoom@" + nom);
                    infos.Socket.Send(buffer, buffer.Length, SocketFlags.None);*/
                    infos.Socket.Send(Encoding.ASCII.GetBytes("NameOfChatRoom@" + nom + "`"), ("NameOfChatRoom@" + nom + "`").Length, SocketFlags.None);
                }
            }
        }
        private Boolean isNicknameInUse(String nickname)
        {
            lock (_allClients)
            {
                foreach (ClientConnectionInfo c in _allClients.ToArray())
                {
                    if (c.pseudo.Equals(nickname))
                    {
                        return true;
                    }
                }
            }
            return false;
        }
        private void SendNewMessageToSpecificChat(String ID, string name, string msg)
        {
            lock (_inRoomsClients[ID])
                foreach (ClientConnectionInfo c in _inRoomsClients[ID].ToArray())
                {
                    c.Socket.Send(System.Text.ASCIIEncoding.ASCII.GetBytes(ID + "@Message@[" + String.Format("{0:HH:mm:ss}", DateTime.Now) + "] " + name + " : " + msg + '`'), (ID + "@Message@[" + String.Format("{0:HH:mm:ss}", DateTime.Now) + "] " + name + " : " + msg + '`').Length, SocketFlags.None);
                }
        }
        private void SendSysMsgToSpecificChat(String ID, String msg)
        {
            if (msg != "")
            {
                lock (_inRoomsClients[ID])
                {
                    foreach (ClientConnectionInfo c in _inRoomsClients[ID].ToArray())
                    {
                        c.Socket.Send(System.Text.ASCIIEncoding.ASCII.GetBytes(ID + "@Information@" + msg + '`'), (ID + "@Information@" + msg + '`').Length, SocketFlags.None);
                    }
                }
            }
        }
        private void modifyClientImage(ClientConnectionInfo infos, String image)
        {
            lock(_allClients)
                _allClients[_allClients.IndexOf(infos)].image = image;
        }
        private void modifyClientPseudo(ClientConnectionInfo infos, String pseudo)
        {
            lock(_allClients)
                _allClients[_allClients.IndexOf(infos)].pseudo = pseudo;
        }
}
Voila pour la plus grosse partie du code.
J'ai pas vraiment besoin de le signaler, mais c'est vraiment codé très salement.
Je peux ajouter les parties manquantes si nécessaire ainsi que la partie traitement coté client, mais elle ressemble beaucoup a cette partie.



Quoi qu'il en soit concrètement la j'ai deux soucis :
Soit le client reçoit des données incomplète en fin de buffer, donc traite n'importe comment.
Soit le serveur déclanche une socketException WouldBlock.

Voila j'espère ne rien avoir oublié, n'hésitez pas à me demander des compléments.
Toute critique sera appréciée à sa juste valeur, n'hésitez pas, même si cela ne concerne pas les Socket
Merci d'avance à ceux qui sauront me guider.

EDIT 1 :

J'ai décidé de réaliser un petit stockage du contenu de la fin du buffer si celui-ci ne contient pas un délimiteur de fin de ligne à la fin.
Cela règle le soucis coté Client/Serveur du buffer.

Voila le code (bien que très moche) :
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
                ...
                String endOfBuff = "";
                while (true)
                {
                    byte[] buffer = new byte[255];
                    int bytesRead = connection.Socket.Receive(buffer);
                    String data;
                    if (bytesRead > 0)
                    {
                        String[] allDatas;
                        [B]data = endOfBuff;
                        data += System.Text.Encoding.UTF8.GetString(buffer).TrimEnd('\0').Trim();
                        endOfBuff = "";
                        if (!data.EndsWith("`"))
                        {
                            endOfBuff = data.Split('`').ElementAt(data.Split('`').Count() - 1);
                            allDatas = data.Substring(0, data.LastIndexOf('`')).Split('`');
                        }
                        else
                        {
                            allDatas = data.Split('`');
                        }[B]

                        foreach (String l in allDatas)
                        {
                                   ....
Cependant, au bout d'un moment (environ après 2 minutes avec 25 clients envoyant des messages chaque seconde chacun), le serveur s'arrête. Et ce sans prévenir ni erreur etc... Rien n'est à signaler ni coté client, ni coté serveur. Bref, je suis vraiment perdu, quelqu'un à une idée, ou des améliorations à proposer ?

Merci beaucoup, encore une fois, toute aide est appréciée, même minime (S.V.P )