Avri Blog

Blog à l'avricot - Lord Of Castle, java, nosql, Utomia, AvriChat, javascript, Json, Css, Mootools, ajax, php...

Aller au contenu | Aller au menu | Aller à la recherche

Stop Bubble | Start

lundi, novembre 9 2009

Jeux diablo like comet

Diablo-like

Voici une idée de ce que l'on peut réaliser en utilisant une connexion Comet avec un serveur java :
(c'est un ami qui fait ce projet, avec la base d'AvriComet)

Il s'agit d'un moteur de jeu 3D isométrique de type hack’n slash style diablo, en ligne.


Vous aller me dire : encore un, ce n'est pas original !

Ce qui fait l'originalité de ce jeu est qu'il est codé de manière à ce que l'utilisateur n'ai rien à installer sur son ordinateur excepté un navigateur web récent. (pas de flash ni de java).Et pourtant le jeu est vraiment interactif
Vidéo du jeux sur dailymotion
Démo en ligne

Alors ? Bluffé ? ;)

lundi, octobre 19 2009

Comet Javascript : the avriComet from moo-comet !

Lady and gentleman, this is the AvriComet !
It's very very inspired by moo-comet (Benjamin Hutchins) who have done a realy great work !
I have add a new method for comet for Firefox and make some changes.
If you have no idea of what is comet, you can see an exemple of the moo-comet on Benjamin Hutchins' web site
If you want more information about each solution used for each browser, check here : step by step comet tutorial

Download uncompressed 
  1. /*
  2. * @project AvriComet- Mootools 1.2.1> Request.Queue Class (in the function send), Can be found on www.clientcide.com
  3. * @author : Quentin Ambard
  4. * @version : 0.1
  5. * @url http://www.avricot.com/blog
  6. * @license MIT Style License
  7. *
  8.  
  9. it will create 4 differents types of tunnels, using the lasts specifications :
  10. it's -i think- the best solution, but the server will have to send all the informations in 4 differents ways...
  11. ->Trident : iframeForever (using ActiveXObject)
  12. Server :
  13. Specific Header :
  14. Content-Type: text/html; charset=utf-8
  15. Push :
  16. <script type="text/javascript">parent._cometObject.fireEvent('dataPushed', {JSON: "DATA TO PUSH"})</script>
  17. You must flush the buffer, sending some noise when you start the connexion, for exemple :"<span></span><span></span><span></span><span></span><span></span>"
  18. ->presto : source-event
  19. Server :
  20. Specific Header :
  21. Content-Type: application/x-dom-event-stream
  22. Push :
  23. Event: "+this.cometName+"\ndata: {JSON: "DATA TO PUSH"}\n\n (cometName is send on each Bayeux Publish request on the field ex )
  24. ->gecko : xhr object using multipart
  25. Server :
  26. Specific Header :
  27. Content-Type: multipart/x-mixed-replace;boundary=XXoXoX
  28. Push :
  29. Content-Type: text/json\r\n\r\n{JSON: "DATA TO PUSH"}\r\n\r\n--XXoXoX\r\n
  30.  
  31. ->Others : addCallback
  32. Server :
  33. Specific Header :
  34. none
  35. Push :
  36. {JSON: "DATA TO PUSH"}
  37.  
  38.  
  39.  
  40. AvriComet will fire 2 events :
  41. - error ('error text') ;
  42. - push (Object)
  43. When you send data to the server, you can do it throught the method send(whateverYouHaveToSend).
  44. It will catch the answer and fire the event :
  45. - answer (theAnswer)
  46.  
  47. */
  48. var AvriComet = new Class ({
  49. Implements: [Options, Events],
  50. options: {
  51. cometName: "MooComet", //name of the comet connexion, essential for presto engine.
  52. urlServer: '' //Url of the server.
  53. },
  54. length: 0,
  55. initialize: function(_options){
  56. this.setOptions(_options);
  57.  
  58. this.sender = new Request.Queue({
  59. concurrent : 2
  60. });
  61.  
  62.  
  63. this.cometType = (Browser.Engine.trident ? 3 : (Browser.Engine.presto ? 2 : (Browser.Engine.gecko ? 1 : 0)));
  64.  
  65. //ie
  66. if (this.cometType == 3) {
  67. this.tunnel = new ActiveXObject("htmlfile");
  68. }
  69. //presto (opera)
  70. else if (this.cometType == 2){
  71. this.tunnel = document.createElement("event-source");
  72. }
  73. //gecko (firefox)
  74. else if (this.cometType == 1){
  75. this.tunnel = new Request({
  76. url: this.options.urlServer,
  77. method: 'get',
  78. header: ''
  79. });
  80. this.tunnel.xhr.multipart = true ;
  81. this.tunnel.xhr.onload = function (event) {
  82. if (event.target.readyState == 4 ) {
  83. this.decodeJSON(event.target.responseText) ;
  84. //this.fireEvent('dataPushed', [event.target.responseText, event.target.responseXML]) ;
  85. }
  86. else {
  87. this.fireEvent('error', "Error xhr : readyState: "+event.target.readyState) ;
  88. }
  89. }.bind(this.tunnel);
  90. }
  91. //webkit (chrome/safari)
  92. else if (this.cometType == 0){
  93. this.tunnel = new Request.XHR(this.options);
  94. this.tunnel.addCallback(this.onChange.bind(this), {readyState: false, status: false});
  95. }
  96. return this;
  97. },
  98. /**
  99. * Have to be invoked when you want to close the connection
  100. */
  101. cancel: function() {
  102. if (this.cometType == 3) {
  103. this.tunnel.body.innerHTML="<iframe src='about:blank'></iframe>";
  104. }
  105. //default iframe forever for all broswer or just for browser != than presto && gecko
  106. else if (this.cometType == 2) {
  107. document.body.removeChild(this.tunnel);
  108. }
  109. else {
  110. this.tunnel.cancel();
  111. }
  112. return this;
  113. },
  114. /**
  115. * Create the tunnel with the server.
  116. * 3 differents type of tunnel, depends of the browser's engine:
  117. * trident => iframe with ActiveXObject object
  118. * presto => event-source (html5)
  119. * firefox => XMLHttpRequest with multipart
  120. * post the information with : src="'+this.options.urlServer+_post'"
  121. **/
  122. initTunnel: function(_post) {
  123. //Trident Engine (ie)
  124. if (this.cometType == 3) {
  125. this.tunnel.open();
  126. this.tunnel.write("<html><body></body></html>");
  127. this.tunnel.close();
  128. this.tunnel.parentWindow._cometObject = this;
  129. this.tunnel.parentWindow._cometObject.addEvent('dataPushed', this.firePush.bindWithEvent(this));
  130. this.tunnel.body.innerHTML = "<iframe id='test' src='"+this.options.urlServer+'?cometType='+this.cometType+'&cometName='+this.options.cometName+'&'+_post+"'></iframe>";
  131. }
  132. //Presto Engine (opera)
  133. else if (this.cometType == 2) {
  134. this.tunnel.addEventListener(this.options.cometName, this.decodeJSON.bindWithEvent(this), false);
  135. this.tunnel.setAttribute("src", this.options.urlServer+'?cometType='+this.cometType+'&cometName='+this.options.cometName+'&'+_post);
  136. }
  137. //gecko Engine (Firefox)
  138. else //if (this.cometType == 1)
  139. {
  140. //this.tunnel.addEvent('dataPushed', this.decodeJSON.bindWithEvent(this));
  141. this.tunnel.send('cometType='+this.cometType+'&cometName='+this.options.cometName+'&'+_post);
  142. }
  143. //safari+chrome
  144. /*else {
  145. this.tunnel.send({data: {'cometType': this.cometType, 'cometName': this.options.cometName}});
  146. }*/
  147. },
  148. send: function (_post) {
  149. var req = new Request.JSON({url: this.options.urlServer, link: 'cancel', method: 'get'});
  150. req.addEvent('complete', this.answerSender.bindWithEvent(this));
  151. this.sender.addRequest('sender', req);
  152. req.send(_post);
  153. },
  154. decodeJSON: function (_textResponse) {
  155. if(this.cometType == 2) {
  156. _textResponse = _textResponse.data ;
  157. }
  158. else if (_textResponse != "") {
  159. var newLength = text.length ;
  160. if (newLength>this.length) {//fix firefox behaviour when push 2 information in the same time.
  161. _textResponse = text.substring(this.length,newLength-1) ;
  162. /* possible implementation for FF answer (be carreful with the final "," : {JSON: DATA TO PUSH},
  163. *
  164. * text = "["+text.substring(this.length,newLength-1)+"]" ; //So you have [{objectData1},{objectData2},{objectData3}]
  165. var listAction = JSON.decode(text); //you get the array with all the json object from the server
  166. var size = listAction.length ;
  167. for(i=0;i<size;i++) {
  168. this.firePush(listAction[i]);
  169. }
  170. this.length = newLength ;
  171.  
  172. */
  173. }
  174. }
  175. this.firePush(_textResponse);
  176. },
  177. firePush: function (_object) {
  178. try {
  179. if (typeof _object != "object") {
  180. _object = JSON.decode(_object);
  181. }
  182. }
  183. catch (e) {
  184. this.fireEvent('error', 'transmission error or server error. Sometimes the last bracket has a little bug : '+_object);
  185. }
  186. this.fireEvent('push', JSON.decode(_object));
  187. },
  188. answerSender: function (_answer) {
  189. if (typeof(_answer) != "undefined") {
  190. this.fireEvent('answer', _answer);
  191. }
  192. }
  193.  
  194. }) ;
  195.  
  196. Request.XHR = new Class({
  197. Extends: Request,
  198. callbacks: [],
  199. check: function() {
  200. return true;
  201. /*if (!this.timer) return true;
  202. switch (this.options.link){
  203. case 'cancel': this.cancel(); return true;
  204. case 'chain': this.chain(this.start.bind(this, arguments)); return false;
  205. }
  206. return false;*/
  207. },
  208. addCallback: function(fn, options){
  209. options = $extend({
  210. fn: $empty,
  211. readyState: 4,
  212. status: 200
  213. }, options);
  214. this.callbacks.push($extend(options, {fn: fn}));
  215. return this;
  216. },
  217. onStateChange: function(){
  218. //if (!this.running) return;
  219. this.status = 0;
  220. $try(function(){
  221. this.status = this.xhr.status;
  222. }, this);
  223. this.response = {text: this.xhr.responseText, xml: this.xhr.responseXML};
  224. this.callbacks.forEach(function(callback) {
  225. if (callback.readyState != false && callback.readyState != this.xhr.readyState)
  226. return;
  227. if (callback.status != false && callback.status != this.status)
  228. return;
  229. callback.fn(this.processScripts(this.response.text), this.response.xml);
  230. }, this);
  231. }
  232. });


Coming soon :)
This isn't the final version....

dimanche, octobre 11 2009

Opéra (webkit) : the event-source object

Back to the main tutorial

Presto has a very nice feature : it implements the HTML5 event-source object.
It has been specialy designed for comet, so let's use it !

Client side

You just have to create an event-source object on your dom :

  1. //create the event-source object in the dom
  2. this.tunnel = document.createElement("event-source");
  3. //we add a specific listenenr on the event-source.
  4. this.tunnel.addEventListener("a_specific_id", this.decodeJSON.bindWithEvent(this), false);
  5. //we set the src attribute = the server URL
  6. this.tunnel.setAttribute("src", "your java comet server url");
  7.  
  8. //finaly the .decodeJSON function called every time a data will be pushed by the server
  9.  
  10. this.decodeJSON: function (_textResponse) {
  11. _textResponse = _textResponse.data ;
  12. }

Server Side

the header has to be a x-dom-event-stream :

  1. this.pw.print("Content-Type: application/x-dom-event-stream");

Every data has to be pushed as a new Event :

  1. this.pw.print("Event: a_specific_id\ndata: "+_the_data_to_push+"\n\n");

Conclusion :

Probably the best solution ! You can find all the event-source documentation here : opera labs event source

Back to the main tutorial

Firefox (gecko): the xhr multipart object

Back to the main tutorial

This is a powerfull solution for Firefox ! (better than listen the xhr object).
The idea is very simple. We are going to send the data as a multipart data. So for each part of the data sent by the server, we will specify the type of the data, and add a specific separator.
We will use the string separator "XXoXoX" (can be anything)

Server side :

The header send by the server has to be :

  1. this.pw.print("Content-Type: multipart/x-mixed-replace;boundary=XXoXoX");

So the server has to send for each new part the content type of the part, then the data you want to push, and at the end the separator.
When firefox will read the separator, he will know that he has receveid a new part.

  1. this.pw.print("Content-Type: text/json\r\n\r\n"+_the_data_to_push+"\r\n\r\n--XXoXoX\r\n");

(dont forget the -- befor the string separator)


Client side

On the client side, you just have to use a normal xhr object, but you have to active the multipart option.
Let's do it !

  1. //we create a norma xhr object (this is the mootools way but can be anything)
  2. this.tunnel = new Request({
  3. url: this.options.urlServer,
  4. method: 'get',
  5. header: ''
  6. });
  7. //we active the multipart option of the xhr object
  8. this.tunnel.xhr.multipart = true ;
  9. //we change the onload function, and everytime we got a readyState == 4, it means that we get a new part of data !
  10. this.tunnel.xhr.onload = function (event) {
  11. if (event.target.readyState == 4 ) {
  12. alert('data pushed :'+event.target.responseText) ;
  13. this.fireEvent('dataPushed', [event.target.responseText, event.target.responseXML])
  14. }
  15. else {
  16. this.fireEvent('error', "Error xhr : readyState: "+event.target.readyState)
  17. }
  18. }.bind(this.tunnel);



Conclusion :

easy to implement, powerfull, but just for Firefox !



Back to the main tutorial

chrome & safari : listen the xhr

Back to the main tutorial

The idea is to add a callback function to the xhr object., changing the onStateChange function as we want...
This is what is - i think- the most complicate solution.
Can't say if it's better than an iframe forever, because as we will se we have the same memory leak...

This is the mooClass I use, Based on Moo-Comet 0.1, Copyright (c) 2008, Benjamin Hutchins <http://www.xvolter.com/>.

  1. Request.Comet.XHR = new Class({
  2. Extends: Request,
  3. callbacks: [],
  4. addCallback: function(fn, options){
  5. options = $extend({
  6. fn: $empty,
  7. readyState: 4,
  8. status: 200
  9. }, options);
  10. this.callbacks.push($extend(options, {fn: fn}));
  11. return this;
  12. },
  13. onStateChange: function(){
  14. this.status = 0;
  15. $try(function(){
  16. this.status = this.xhr.status;
  17. }, this);
  18. this.response = {text: this.xhr.responseText, xml: this.xhr.responseXML};
  19. this.callbacks.forEach(function(callback) {
  20. if (callback.readyState != false && callback.readyState != this.xhr.readyState)
  21. return;
  22. if (callback.status != false && callback.status != this.status)
  23. return;
  24. callback.fn(this.processScripts(this.response.text), this.response.xml);
  25. }, this);
  26. }
  27. });
  28.  
  29. //initialization of the tunnel :
  30.  
  31. this.tunnel = new Request.Comet.XHR(this.options);
  32. this.tunnel.addCallback(this.onChange.bind(this), {readyState: false, status: false});
  33.  
  34. //The onChange function which will be called every time the readyState change = we get a new data :
  35. //You have to be aware that "text" contains ALL the data you have pushed from the initialization of the tunnel.
  36. //So you just have to select the last part of text if you want to get the new data (otherwise you will get all the data, quite useless...)
  37.  
  38. onChange: function(text, xml) {
  39. if (text != "") {
  40. var newLength = text.length ;
  41. if (newLength>this.length) { //fix stange behaviour (overall firefox) when push 2 information in the same time.
  42. text = text.substring(this.length,newLength-1) ;
  43. try {
  44. alert('The server push : '+text);
  45. var listAction = JSON.decode(text); //let's say that you get JSON from the server
  46. this.fireEvent('onPush', listAction);
  47. }
  48. this.length = newLength ;
  49. }
  50. catch (e) {
  51. console.log('error parsing the JSON data, remove the try() for debugging');
  52. //transmission error or server error. Sometimes the last bracket has a little bug and you dont get it, so you catch an error...
  53. }
  54. }
  55. }
  56. }


As with the iframe forever, dont forget to restart the tunnel when the text is too big...
on the server side you just have to write your JSON data without any problems.


Back to the main tutorial

samedi, juin 13 2009

Comet with IE (trident) : the activeX iframe forever....

Back to the main tutorial

Solution

With IE, the idea is to create an iframe forever.
The server has to send the data "chunked".
Every data pushed has to be something like : <script type="text/javascript">alert('I have push something !')</script>
If you want to avoid the "click" on each actualisation, you have to create the iframe with an activeXObject !

Client side

  1. this.openTunnel = function () {
  2. this.tunnel = new ActiveXObject("htmlfile");
  3. this.tunnel.open();
  4. this.tunnel.write("<html><body></body></html>");
  5. this.tunnel.close();
  6. this.tunnel.parentWindow._cometObject = this;
  7. this.tunnel.parentWindow._cometObject.addEvent('dataPushed', this.firePush.bindWithEvent(this));
  8. this.tunnel.body.innerHTML = "<iframe id='test' src='the url of the server. Have to use an Apache reverse proxy for exemple?type=ie'></iframe>";
  9. }
  10. this.firePush = function (_data) {
  11. alert('server push : '+_data);
  12. }
  13. //if we want to close the tunnel :
  14. this.closeTunnel = function () {
  15. this.tunnel.body.innerHTML="<iframe src='about:blank'></iframe>";
  16. }

Server Side

opening the tunnel : header :
Transfer-Encoding: chunked
Content-Type: text/html; charset=utf-8

the data : <script type="text/javascript">parent._cometObject.fireEvent('dataPushed', "THE DATA")</script>

An important point is to flush IE's buffer. You need at least -+10ko of data if you want a reaction from IE.
So the best solution is to send some noise during the initialization of the tunnel :

  1. this.write("<span></span><span></span><span></span><span></span><span></span><span></span><span></span>");


One of the problem with this solution is that all the data are kept on memory on the iframe
So if you will have a hudge memory leak.
The only solution is to close the iframe, and to re-open it every X data received by the server.



Back to the main tutorial

vendredi, juin 12 2009

Comet : Step by Step tutorial for every browser (iframe forever, xhr, event-source, multipart, socket HTML5...)

Comet : how to

It's quite hard to find a good comet tutorial which explains you all the hacks you need to use for each browser.
I've work around comet this last days, so here are all the solutions for every browser, step by step.
If you have no idea about comet, you can have a quick look here : Time for comet and here Low latency data - Alex Russel
Basicly, Comet is a client-side technologie which allows you to simulate a html socket, so the server can "push" the data to your browser.
You can't use the stream to communicate with the server. It's only Server-Client, not Client-Server.

I'm going to show you what is -I think- the best solution for each engine...

IE (trident) : the activeX iframe forever....
Firefox (gecko): the xhr multipart object
Opéra (webkit) : the event-source object
chrome & safari : listen the xhr


The all-browser-implementation : using an iframe... (in coming)

Other informations :

You can find information about headers, avast-shield... on the AvriChat topic !

Conclusion :

Comet can be hard to set up.
If you want to quickly implement a comet solution, you can have a look on the long-pooling solution (using in facebook chat for exemple).
Comet is -I think- more efficient than long pooling, but harder to implement.
AvriChat is based on this 4 solutions, one for each browser.

The iframe forever for all browser can be the best solution, because it's a good rapport between speed and development time.

lundi, mai 25 2009

Comet and cross-domain : how-to ?

We have a quite big problem with comet : if you try to ask for an ajax request on the same domain but on a different port, it will be forbidden by your navigator because it's an unsecure cross-domain request.
So how to fix it ?
Basicly i found 3 solutions !

1° : an Iframe loaded by the server

One of the solution could be to create an iframe with the server. On this iframe, you have to load all the javascript.
It result that the javascript comes from the same domain/port than the ajax request, and there wont be any cross-domain...
This solution is pretty ugly, because you won't be able to interfer with the iframe and the rest of the page.
It can be a good solution if you can't do the solution 3°, but I will assume that if you can load a java Server, you can use solution 3° :)

2° : using comet cross-domain

You can use comet cross-domain request, but I don't know how to manage it with IE, so good luck !
It shouldn't be impossible but seems quite hard !

3° : using a reverseProxy

This is the best solution :
Very easy to use, powerfull and fast !
Basicly you can do it with apache if you use it for the rest of your server.

First of all you have to load this two modules on apache :

  • LoadModule proxy_module modules/mod_proxy.so
  • LoadModule proxy_http_module modules/mod_proxy_http.so

then you just have to create a rule for the reverseProxy :

ProxyRequests Off
ProxyPass /directory_reached_by_the_comet_request http://localhost:9090
ProxyPassReverse /directory_reached_by_the_comet_request  http://localhost:9090

mercredi, mai 20 2009

Comet and ajax with Avast's shield web : The salvation - or not

Some testers told me that the Avast shieldWeb still blocks the comet request.
So it appears that the prefix "DWR-Reverse-Ajax" for the Server's name in the header is necessary but not sufficient...
(more info about this solution)
Right now Avast doesn't white-list the request just with the part on the header...
So what's the solution ?

Ok this hack seems very strange, but at least it works.
Basicly, Avast looks afraid when you just send him the header first, and nothing else.
So if you want to be nice with him, start by sending him a nice "\r\n" (= Server.EOL for End Of Line in the exemple) ! (I assume that he will interpret that as a first chunk, but i'm not sure...)
Ok it seems better : we have the connexion stucked just a 50% !
So what's wrong now ? Can't really tell you, but it seems that if you send the 2 EOL of the end of the header and in the same time the EOL for the first chunk, it's too much for avast. So you need to first send him the header with the 2 EOL. Wait a couple of milisec, and nex send him a first EOL chunk.
After that you can push as usualy :)

So Here is the very very strange but 100% solution :

  1. public void send () {
  2. Calendar c = Calendar.getInstance();
  3. String date = c.get(Calendar.DAY_OF_MONTH) + "/" + c.get(Calendar.MONTH) + "/" + c.get(Calendar.YEAR);
  4. try {
  5. this.pw.print("HTTP/1.1 200 OK");
  6. this.pw.write(Server.EOL);
  7. this.pw.print("Date: "+date);
  8. this.pw.write(Server.EOL);
  9. this.pw.print("Server: DWR-Reverse-Ajax Comet AvriChatServer");
  10. this.pw.write(Server.EOL);
  11. this.pw.print("Cache-Control: no-store, no-cache");
  12. this.pw.write(Server.EOL);
  13. this.pw.print("Connection: Connection");
  14. this.pw.write(Server.EOL);
  15. this.pw.write(Server.EOL);
  16. try {
  17. Thread.sleep(50); //in my case, I have wrong connexion under 50/100ms
  18. } catch (InterruptedException e) {
  19. e.printStackTrace();
  20. }
  21. this.pw.write(Server.EOL);
  22. } catch (IOException e) {
  23. e.printStackTrace();
  24. }
  25. this.pw.flush();
  26. }

Does that means that we have to had 50/100ms to every clients, with or without avast web shield ?
One solution could be to first start a normal connexion. If we dont have any answer after 500ms for exemple, we close the connexion, and start a new one in which one the server will have to wait a couple of milisec during the two EOL...

If you have any suggestion or better explanations...

jeudi, mai 14 2009

Comet : header en java : antivirus (avast internet shield) bloque la requete

!!!The full solution : Comment écrire correctement les headers d'une réponse http pour une appliquation comet ?
Le principal inconvenant de Comet est que l'on ne connait pas la taille de l'element à renvoyer. Il est donc impossible de placer dans le header un "Content-length: taille".
La solution toute trouvée serait d'utiliser le header "Transfer-Encoding: chunked" qui spécifie que le message sera envoyé par petit chunk (=morceau).
Seulement voila, les antivirus qui scannent les headers http trouvent qu'un header comme-suivant est insuffisant :

Content-Type text/javascript; charset=ISO-8859-1
Transfer-Encoding chunked

La requête se retrouve alors bloquée par l'antivirus, "en attente", et la réponse ne parvient jamais.

Pour remédier à cela, avast white-list depuis peu les headers ayant dans le nom du serveur le mot clé : DWR-Reverse-Ajax.
Il suffit donc de rajouter "Server: DWR-Reverse-Ajax" pour que avast arrête de bloquer la réponse.

Voici l'ensemble du code qui envoie les headers de avriChat :

  1. public static final byte[] EOL = {(byte)'\r', (byte)'\n' };
  2. /*************************************/
  3. public void send () {
  4. Calendar c = Calendar.getInstance();
  5. String date = c.get(Calendar.DAY_OF_MONTH) + "/" + c.get(Calendar.MONTH) + "/" + c.get(Calendar.YEAR);
  6. try {
  7. this.pw.print("HTTP/1.1 200 OK");
  8. this.pw.write(Server.EOL);
  9. this.pw.print("Date: "+date);
  10. this.pw.write(Server.EOL);
  11. this.pw.print("Server: DWR-Reverse-Ajax Comet AvriChatServer");
  12. this.pw.write(Server.EOL);
  13. this.pw.print("Vary: Accept-Encoding");
  14. this.pw.write(Server.EOL);
  15. this.pw.print("Connection: Keep-Alive");
  16. this.pw.write(Server.EOL);
  17. this.pw.print("Content-Type: text/html");
  18. this.pw.write(Server.EOL);
  19. this.pw.write(Server.EOL);
  20. } catch (IOException e) {
  21. e.printStackTrace();
  22. }
  23. System.out.println(this.content);
  24. this.pw.flush();
  25. }

(Attention, this.pw = new PrintWriter(this.socket.getOutputStream()) ne fonctionne pas.
Il faut envoyer les /r/n en byte avec un write() en utilisant un this.pw = new PrintStream(this.socket.getOutputStream());

Cela fonctionne (pour le moment) pour avast, il faudra voir si d'autres antivirus posent le même problème ou pas...

- page 1 de 2