Skip to content

棋牌平台怎么用 IP 检测同 IP 伙牌/多开作弊?(Python 实现) ​

棋牌运营最头疼的作弊不是外挂,是「人」。两个小号账号在同一桌,互相喂牌、递暗号,稳赢不输——这就是伙牌。还有更偷懒的:同一个人开两台设备,两个账号挤进同一桌,左手打右手。这类作弊的共同特征就一个:多个账号来自同一个 IP。IP 归属地接口在这个场景里不是用来「查位置」的,是用来做同源判定的。这篇用 Python 把检测逻辑完整写一遍。

一、为什么看 IP 就能抓伙牌 ​

先想清楚对手的画像:

  • 伙牌:两个真人各拿一个号,同桌串通。网络层面不一定同 IP(他俩可能在不同网吧),但概率上同 IP/同归属地比例远高于正常玩家;
  • 多开:一个人两个号,很可能同设备同网络,IP 相同是硬特征;
  • 刷分/控号:工作室批量养号,同一批 IP 反复进出同一牌桌。

正常对局里,一桌 4 个人来自 4 个不同 IP 是常态;一旦出现「同桌 2 个及以上账号同 IP 或同城市同运营商」,命中可疑的概率非常高。所以检测逻辑的骨架是:每局结束,把同桌玩家的 IP 两两比对,相同的直接标红;不同的再查归属地,比对省市和运营商。

二、方案设计 ​

  1. 数据来源:牌局结算表,每局记录 room_id(牌桌)、player_id、ip、start_time;
  2. 同 IP 判定:一局内多个玩家 IP 相同 → 同 IP 多开,直接命中;
  3. 同源判定:IP 不同但归属地(省+市+运营商)相同 → 标记「疑似同源」,进人工复核队列;
  4. 频控:查归属地用免费接口循环调用,带缓存 + 限速(免费版 60 次/分钟/IP);
  5. 输出:可疑局列表 + 命中原因,交给风控同学复核或直接按规则处置。

为什么要把「同城市」也当信号:现在 IPv6 和动态宽带普及,同一个人两个号很可能是两个不同 IP 但同宽带同城市。只看 IP 相等会漏掉一批,加上归属地比对能多捞一网。

三、Python 实现 ​

python
#!/usr/bin/env python3
# mahjong_ip_check.py —— 棋牌桌同 IP/同源检测
# 用法:python3 mahjong_ip_check.py games.json
import json
import sys
import time
import requests

CACHE = {}          # ip -> 归属地 dict
LAST_CALL = 0.0

def query_geo(ip):
    """查 IP 归属地,带缓存 + 限速。免费版 60 次/分钟/IP。"""
    global LAST_CALL
    if ip in CACHE:
        return CACHE[ip]
    now = time.time()
    gap = 1.1 - (now - LAST_CALL)      # 每请求间隔至少 1.1s,留余量
    if gap > 0:
        time.sleep(gap)
    try:
        r = requests.get(f"https://ip9.com.cn/get?ip={ip}", timeout=5)
        d = r.json().get("data") or {}
        geo = {
            "country": d.get("country", ""),
            "prov": d.get("prov", ""),
            "city": d.get("city", ""),
            "isp": d.get("isp", ""),
        }
    except Exception:
        geo = {}                        # 查询失败不阻断,同 IP 判定仍有效
    LAST_CALL = time.time()
    CACHE[ip] = geo
    return geo

def check_game(game):
    """检查一局:返回命中信息列表。"""
    hits = []
    players = game["players"]           # [{"id": "u1", "ip": "1.2.3.4"}, ...]
    n = len(players)

    for i in range(n):
        for j in range(i + 1, n):
            a, b = players[i], players[j]

            # 1) 同 IP:直接命中(多开)
            if a["ip"] == b["ip"]:
                hits.append(f"同IP多开: {a['id']} 和 {b['id']} 共用 {a['ip']} (牌桌 {game['room_id']})")
                continue

            # 2) IP 不同 → 查归属地,比对省/市/运营商
            ga = query_geo(a["ip"])
            gb = query_geo(b["ip"])
            if not ga or not gb:
                continue
            same_src = (ga.get("prov") and ga.get("prov") == gb.get("prov")
                        and ga.get("city") == gb.get("city")
                        and ga.get("isp") == gb.get("isp"))
            if same_src:
                hits.append(
                    f"疑似同源: {a['id']}({a['ip']}) 和 {b['id']}({b['ip']}) "
                    f"同属 {ga.get('prov')}{ga.get('city')} {ga.get('isp')} (牌桌 {game['room_id']})"
                )
    return hits

def main(path):
    with open(path, encoding="utf-8") as f:
        games = json.load(f)

    total_hits = 0
    for game in games:
        hits = check_game(game)
        for h in hits:
            print(h)
            total_hits += 1
    print(f"\n共检查 {len(games)} 局,命中 {total_hits} 条可疑记录")
    print(f"归属地缓存 {len(CACHE)} 个 IP,节省重复查询")

if __name__ == "__main__":
    if len(sys.argv) != 2:
        print("用法: python3 mahjong_ip_check.py games.json")
        sys.exit(1)
    main(sys.argv[1])

输入数据 games.json 长这样:

json
[
  {
    "room_id": "R1001",
    "players": [
      {"id": "u1", "ip": "183.14.28.11"},
      {"id": "u2", "ip": "183.14.28.11"},
      {"id": "u3", "ip": "220.181.38.5"},
      {"id": "u4", "ip": "61.140.12.9"}
    ]
  },
  {
    "room_id": "R1002",
    "players": [
      {"id": "u5", "ip": "113.65.12.7"},
      {"id": "u6", "ip": "113.65.33.9"},
      {"id": "u7", "ip": "218.30.10.4"},
      {"id": "u8", "ip": "61.140.12.9"}
    ]
  }
]

跑一遍:R1001 里 u1、u2 同 IP 直接命中;R1002 里 u5、u6 如果都在同一城市同一运营商,会被标「疑似同源」。

四、落地注意事项 ​

  • 阈值和误伤:同 IP 命中基本可以实锤(家庭宽带、公司网络例外——同事摸鱼同桌打牌也是同 IP)。疑似同源只能进复核队列,别直接封号。同城同 ISP 的误报率不低,大城市尤其如此。
  • 缓存是刚需:一晚上几百桌牌局,IP 去重后可能只有几千个。不清缓存,同一 IP 只会查一次,免费额度完全够。
  • VIP 的 ip_type 能用在哪:家庭宽带、企业宽带、IDC 机房在 VIP 版里能区分。多开号常挂在「IDC 机房」段(工作室云主机),如果这个特征叠加同 IP,基本就是工作室没跑。
  • 时间维度别忽略:只查单局会漏掉「错峰伙牌」——两个账号错开时间进同一桌。把比对窗口拉长到 24 小时,同 IP 账号有过同桌历史,也值得标一下。
  • 合规:记录玩家 IP 要符合个人信息保护要求,检测结果只用于平台反作弊,别拿来做别的。

总结 ​

伙牌和多开是棋牌平台绕不开的猫腻,IP 维度是最便宜的第一道筛子。一套逻辑就三件事:同 IP 判多开、同归属地判同源、缓存限速保额度。接口 https://ip9.com.cn/get?ip=xxx 免费版就能跑通全流程,60 次/分钟不带注册;要上 ip_type 区分机房/家宽这类进阶字段,看官网 https://www.ip9.com.cn 的 VIP 说明。先把单局检测跑起来,再逐步加时间窗口,作弊的会少一大截。