0

HackThisSite 论坛上有一个主题详细介绍了 Omegle(omegle.com 上的匿名聊天客户端)的工作原理:https ://www.hackthissite.org/forums/viewtopic.php?f=76&t=3783

这有点过时了,但它仍然大部分是准确的。以此为起点,我一直在开发一个定制的 Omegle 客户端。起初我在 Game Maker 中对其进行了编程,但后来我切换到了 Javascript/AJAX(使用一点 PHP 后端来解决一些 XSS 问题),我认为它更适合用于 Web 应用程序服务。

基本上,Omegle 的工作方式是您不断轮询服务器,当您这样做时,它会发回自上次轮询以来发生的事件列表。这些事件是诸如“等待”(配对)、“已连接”(对陌生人)、“gotMessage”(包含接收到的消息的事件)、“strangerDisconnected”(当其他用户断开连接时)等.

就像我说的,那个链接有点过时了,Omegle 添加的一个新东西是一个名为 statusInfo 的事件,它包含一个带有一些服务器信息的 JSON 对象(包括当前工作服务器的列表)。这个想法是,每次你检查新事件时,你也会得到一个更新的工作服务器列表,这样你就可以在服务器出现故障时跳到服务器。

这一切都很好,花花公子,我的自定义客户端有时也可以正常工作……但是有一个错误。每隔一段时间,当它轮询服务器以获取事件时,服务器决定不响应。它甚至不发回任何类型的确认或状态代码,因此 AJAX 请求只是坐在那里等待永远不会到来的响应。奇怪的是,当这种情况发生时,如果我跳到服务器,它通常无济于事。不管服务器如何,它都会挂在下一个请求上,直到它随机决定重新开始工作。所有这一切似乎完全是武断的——它在某些时候完美运行,而在其余时间挂起,据我所知,这两个类别之间没有任何共同点。

更奇怪的是,如果我在发生这种挂起时尝试​​断开连接(这只是向 server.omegle.com/disconnect 发送请求并发布用户 ID),Omegle 会发回“内部服务器错误”消息;如果事件请求中没有挂起,当我断开连接时它不会这样做。

我很想将您链接到我的客户,但我目前处于防火墙后面,我几乎无法控制,所以我不得不在这里发布代码......它只有 2 个小文件和 1 个大文件,不过,希望有人可以测试它吗?

事不宜迟,这是我为我的客户端使用的整个 HTML/Javascript(没有使用任何库;全部从头开始编码)。我只是进去为你评论这一切;希望很清楚。

<style>
.error { color:#ff0000; }
.them { color: #ff0000; }
.you { color:#0000ff; }
.typer { color:#555555; font-weight:bold; font-size:11pt; }
</style>

<script>

/* Cross-platform XMLHttpRequest function */
function Request() {
  if (window.XMLHttpRequest ) { return new XMLHttpRequest(); }
  else if (window.ActiveXObject) {
    try {
      return new ActiveXObject("Msxml2.XMLHTTP");
    }
    catch (e) {
      try {
        return new ActiveXObject("Microsoft.XMLHTTP");
      }
      catch (e) {
        return false;
      }
    }
  }
}

/* Common array shuffling function used to shuffle the list of servers */
function shuffle(a) {
  var m = a.length, t, i;

  // While there remain elements to shuffle...
  while (m) {

    // Pick a remaining element...
    i = Math.floor(Math.random() * m--);

    // And swap it with the current element.
    t = a[m];
    a[m] = a[i];
    a[i] = t;
  }

  return a;
}

/* A general-purpose handler for the XMLHttpRequests, which simply checks for a "completed" state and then calls the associated handler. I plan on making this more
   robust once the overall system is working at all. */
function HandleXHR() {
  if (this.readyState==4) {
    this.handler(this.responseText);
  }
}

server=null;
servs=null;

/* Whenever a statusInfo message is received, it includes a list of working servers. This code, almost identical to Omegle's own, modifies our own list of servers
   to include all of those (and remove no-longer-working ones) without changing the relative order of the current servers on the list */
function updateServers(list) {
  list=shuffle(list);
  for (i=0; i<list.length; ++i) {
    if (servs.indexOf(list[i])<0) {
      servs.unshift(list[i]);
    }
  }

  for (i=0; i<servs.length; ++i) {
    if (list.indexOf(servs[i])<0) {
      servs.splice(i--, 1);
    }
  }

  server=servs[0];

}

typing=false;

/* Initiate looking for a connection; this requests a user ID from the server, which itself is randomly chosen from the list of servers. This is actually what Omegle's
   own code does--it shuffles the server list around for uniform usage. */
function Connect(what) {
  typing=false;
  what=JSON.parse(what);
  servs=shuffle(what.servers);
  server=servs[0];

  req=Request();
  where="http://"+server+"/start?rcs=1&spid=";
  req.open("GET", "getpage.php?url="+escape(where));
  req.handler=GotID;
  req.onreadystatechange=HandleXHR;
  req.send(null);
}

id=null;
eventwatch=null;

/* When a connection is made and we have a user ID, start polling for events immediately */
function GotID(what) {
  id=what.split('"')[1];
  eventwatch=setTimeout(GetEvents, 1);
}

/* Initiate the poll to the server for any recent events */
function GetEvents() {
  if (id==null) { return false; }

  where="http://"+server+"/events";  
  req=Request();
  req.open("POST", "getposted.php?url="+escape(where));
  req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

  req.handler=ParseEvents;
  req.onreadystatechange=HandleXHR;

  req.send("id="+escape(id));
}

/* The function that parses all the events when they've been received from a poll */
attempts=0;
function ParseEvents(what) {

  spot=document.getElementById("codespot");

  try {
    what=JSON.parse(what);
    attempts=0;
  }

  /* If the messages couldn't be parsed, i.e. the server didn't return anything or returned malformed JSON (or an error),
     hop servers and try again, up to 3 times before giving up */
  catch (e) {
    attempts++;

    /* Move the first server on the list to the end and pick the next server */
    temp=servs.shift();
    servs.push(temp);
    server=servs[0];

    if (attempts>=3) {
      spot.innerHTML+="<b class='error'>Error: "+e.message+"</b><br />";
      Disconnect();
      return false;
    }

    eventwatch=setTimeout(GetEvents, 2500);
    return false;
  }

  /* Process each message */
  for (p in what) {
    code=what[p][0];

    /* Connected event */
    if (code=="connected") {
      spot.innerHTML+="<b>Connected!</b><br />";
      document.getElementById("waiter").innerHTML="";
    }

    /* Message reception */
    else if (code=="gotMessage") { spot.innerHTML+="<b class='them'>Stranger:</b> "+what[p][1]+"<br />"; }

    /* The other user disconnected, so stop checking for events and set your user ID to null (meaning no connection) */
    else if (code=="strangerDisconnected") { typing=false; id=null; spot.innerHTML+="<b>Stranger disconnected</b><br />"; window.clearTimeout(eventwatch); }

    /* Process the statusInfo events by updating the server list */
    else if (code=="statusInfo") {
      updateServers(what[p][1].servers);
    }

    /* These are only used for Omegle logs, which I'm not implementing, so ignore them */
    else if (code=="identDigests") { /* Ignore these for now */ }

    /* Change stranger-typing status */
    else if (code=="typing") { typing=!typing; }
    else if (code=="stoppedTyping") { typing=false; }

    /* Before a connection is made, let the user know you're waiting for one */
    else if (code=="waiting") {
      document.getElementById("waiter").innerHTML="Looking for a stranger...";
    }

    /* If all else fails, we don't know what this message is, so output it for debugging purposes--hasn't happened in all my tests yet */
    else { spot.innerHTML+="Didn't understand: "+code+"<br />"; }
  }

  /* Update the "is typing" display and set a timeout for the next event poll */
  document.getElementById("typer").innerHTML=typing ? "Stranger is typing..." : "";
  eventwatch=setTimeout(GetEvents, 2500);
}

/* The function that attempts to disconnect from the conversation */
function Disconnect() {
  where="http://"+server+"/disconnect?id="+id;

  req=Request();
  req.open("POST", "getposted.php?url="+escape(where));
  req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

  req.handler=Disconnected;
  req.onreadystatechange=HandleXHR;

  req.send("id="+escape(id));
}

/* The function to send a message */
function Send(place) {
  if (id==null) { return false; }
  what=place.value;
  place.disabled=true;

  where="http://"+server+"/send";

  req=Request();
  req.open("POST", "getposted.php?url="+escape(where));
  req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

  req.handler=Sent;
  req.onreadystatechange=HandleXHR;

  req.send("id="+escape(id)+"&msg="+escape(what));
}

/* The function to handle the display when a message you've typed has been sent */
function Sent(what) {
  if (id==null) { return false; }
  document.getElementById("codespot").innerHTML+="<b class='you'>You:</b> "+document.main.mess.value+"<br />";
  document.main.mess.disabled=false;
  document.main.mess.value="";
}

/* The function to handle cleanup once you've disconnected */
function Disconnected(what) {
  typing=false;
  id=null;
  window.clearTimeout(eventwatch);
  document.getElementById("codespot").innerHTML+="<b>Disconnected";
  if (what.substr(0, 3)!="win") { document.getElementById("codespot").innerHTML+=" -- "+what; }
  document.getElementById("codespot").innerHTML+="</b><br />";
}

/* The function to initiate the requests to get the list of active Omegle servers */
function GetServer() {
  req=Request();
  where="http://omegle.com/status";
  req.open("GET", "getpage.php?url="+escape(where));
  req.handler=Connect;
  req.onreadystatechange=HandleXHR;
  req.send(null);
}

</script>

<!-- The chat area -->
<div id="waiter" class="typer"></div>
<div id="codespot"></div>
<div id="typer" class="typer"></div><br />

<!-- The form where you connect, disconnect, and send messages from -->
<form name="main" onSubmit="Send(document.main.mess); return false"><input type="button" value="Connect" onClick="GetServer()" /><input type='text' name='mess' /><input type='submit' value='Send' />
<input type="button" value="Disconnect" onClick="Disconnect()" />

而后端 PHP 脚本 getpage.php 和 getposted.php 只需使用 cURL 从 Omegle 获取响应(尽管位于不同的域中)并直接回显它们,从而允许 XHR 访问域外页面。

谁能帮我弄清楚为什么有时会发生停滞?

如果您想要 getposted.php 和 getpage.php 代码的简单代码,是这样的:

getposted.php:

<?php
  set_time_limit(60);
  $curl=curl_init();
  curl_setopt($curl, CURLOPT_URL, $_GET["url"]);
  curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); // Return value instead of outputting
  curl_setopt($curl,CURLOPT_POST, count($_POST));
  curl_setopt($curl,CURLOPT_POSTFIELDS, http_build_query($_POST, '', '&'));

  $all=curl_exec($curl);
  curl_close($curl);
  echo $all;
?>

获取页面.php

<?php
  set_time_limit(60);
  $curl=curl_init();
  curl_setopt($curl, CURLOPT_URL, $_GET["url"]);
  curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); // Return value instead of outputting

  $all=curl_exec($curl);
  curl_close($curl);
  echo $all;
?>
4

0 回答 0