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;
?>