java - 列出套接字应用程序的游戏房间
问题描述
我有一个 javafx 桌面国际象棋应用程序,可以通过套接字在线播放。一个玩家创建 GameRoom(Serversocket),另一个玩家可以通过在对话框中输入 IP 地址直接加入。而不是这种方法,我想列出所有可用的房间并以这种方式连接。
实现这一点的最简单方法是什么?我曾考虑将服务器地址放在像 Firestore 这样的在线数据库中,但这对于桌面应用程序来说似乎不是最佳选择。
服务器套接字:
public class GameServer implements Runnable{
private PrintWriter pw;
private BufferedReader in;
private ServerSocket listener;
public GameServer(){
}
@Override
public void run() {
try {
listener=new ServerSocket(50000);
System.out.println("Server is listening on port 50000");
if(in==null || pw==null){
Socket socket=listener.accept();
System.out.println("A player has connected");
pw=new PrintWriter(socket.getOutputStream(),true);
in=new BufferedReader(new InputStreamReader(socket.getInputStream()));
}
Executors.newFixedThreadPool(1).execute(() -> {
try {
OnlineGameFunctions.receiveMoveAndResign(in);
} catch (IOException e) {
e.printStackTrace();
}
});
}catch (IOException e){
e.printStackTrace();
}
}
客户端:
public class ClientSocket {
private Socket socket;
private PrintWriter pw;
private BufferedReader in;
public ClientSocket() {
}
public void Connect(String ip, int port) throws IOException {
if(in==null || pw==null){
socket=new Socket(ip,port);
in=new BufferedReader(new InputStreamReader(socket.getInputStream()));
pw=new PrintWriter(socket.getOutputStream(),true);
}
Executors.newFixedThreadPool(1).execute(() -> {
try {
OnlineGameFunctions.receiveMoveAndResign(in);
} catch (IOException e) {
e.printStackTrace();
}
});
}
}
解决方案
您可以手动尝试连接到网络中可能的 IP 地址,这个答案是来自 SO 的答案的组合,我将在帖子末尾添加指向它们的链接。
您首先需要获取您的局域网 IP 地址及其前缀长度,然后使用它们列出所有可能的 IP 地址(使用commons-net),然后将这些 IP 地址划分到多个线程上,每个线程将循环通过一部分IP 并尝试连接到其中的每一个,如果连接成功 => 将 IP 添加到结果中。
我已经编写了这段代码(大部分代码)并通过创建 socketServer 在我的机器上对其进行了测试,它返回了我的 ip,所以我猜它可以工作:
public class SocketScan {
public static void main(String[] args) throws Exception {
System.out.println(scan(50000));
}
public static List<String> scan(int port) throws Exception {
System.out.println("scanning for servers...");
ArrayList<String> res = new ArrayList<String>();
// get your lan ip
InterfaceAddress addr = getLanIp();
String myIp = addr.getAddress().getHostAddress() + "/" + addr.getNetworkPrefixLength();
// list all possible ip addresses using your ip and mask
SubnetUtils utils = new SubnetUtils(myIp);
String[] allIps = utils.getInfo().getAllAddresses();
// split all ips on a number of threads
// and try to connect to each one of them
int threadCount = 24;
Thread[] threads = new Thread[threadCount];
int threadSize = allIps.length / threadCount;
for (int i = 0; i < threadCount; i++) {
final int index = i;
threads[i] = new Thread() {
public void run() {
int start = threadSize * index;
int end = threadSize * (index + 1);
for (int j = start; j < (index == threadCount - 1 ? allIps.length : end); j++) {
String ip = allIps[j];
if(checkIp(ip, port)) {
res.add(ip);
}
}
}
};
}
//start all threads
for (Thread thread : threads) {
thread.start();
}
//wait for all threads to finish
for (Thread thread : threads) {
thread.join();
}
return res;
}
//check if a connection to this ip is possible
public static boolean checkIp(String ip, int port) {
try {
Socket socket = new Socket();
socket.connect(new InetSocketAddress(ip, port), 150);
socket.close();
return true;
} catch (Exception ex) {
return false;
}
}
//This method loops through your network interfaces and returns
//the first address that is ipv4 and not loopback
public static InterfaceAddress getLanIp() throws Exception {
for (final Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); interfaces
.hasMoreElements();) {
final NetworkInterface cur = interfaces.nextElement();
if (cur.isLoopback()) {
continue;
}
for (final InterfaceAddress addr : cur.getInterfaceAddresses()) {
final InetAddress inet_addr = addr.getAddress();
if (!(inet_addr instanceof Inet4Address)) {
continue;
}
return addr;
}
}
return null;
}
}
请注意,这需要一些时间才能完成(在我的机器上使用我的 /24 子网大约需要 3 秒),如果您不想等待检查所有 ip,您可以在检查后通过消费者来处理 ip是成功的
用过的答案
IP 地址扫描器,这不考虑网络子网
获取 LAN ip只是将打印替换为返回
推荐阅读
- php - 尝试在沙盒模式下授权 Quickbooks Online 的致命错误(Consolibyte PHP 框架)
- javascript - 如何使用带有原始数据而不是图像的 createImageBitmap
- angular - 如何构建模块以单独部署
- wpf - 如何结合两个条件来覆盖数据网格的选定行样式?
- flutter - 使用 StreamProvider 和 StreamBuilder 时出错
- laravel - 在所有测试中禁用所有模型的质量分配保护
- powerbi - Power BI 无法识别公式中的数字,但会识别 0
- visual-studio-2019 - 在 VS2019 中调试 Roslyn 分析器时缺少扩展
- ansible - Ansible 是否有其他条件
- css - vs-liveserver 呈现与手动打开索引不同的结果