【问题标题】:Winsock2 & JNA, can't get WSAEnumProtocols() to work properlyWinsock2 & JNA,无法让 WSAEnumProtocols() 正常工作
【发布时间】:2013-03-01 05:56:52
【问题描述】:

(也许我用烦人的细节解释了这个问题,tl;dr 版本在底部)

我一直在尝试使用 JNA 在 Java 中创建一个简单的 Winsock 测试应用程序。我调用的第一个函数是 WSAStartup(),使用以下代码:

public interface Ws2_32 extends Library {
    Ws2_32 INSTANCE = (Ws2_32) Native.loadLibrary("ws2_32", Ws2_32.class);

    int WSAStartup(short version, LPWSADATA lpwsaData);

}

public static void main(String[] args){
    LPWSADATA   lpwsaData = new LPWSADATA();
    short       version = 2;
    int         result = Ws2_32.INSTANCE.WSAStartup(version,lpwsaData);

    System.out.println("WSAStartup() returned: " + result);

    if((resultado = Ws2_32.INSTANCE.WSAStartup(version,lpwsaData)) == 0){
        System.out.println("LPWSADATA struct:");
        System.out.println("wVersion: " + lpwsaData.wVersion);
        System.out.println("wHighVersion: " + lpwsaData.wHighVersion);

        System.out.print("szDescription: ");
        for(byte b : lpwsaData.szDescription){
            System.out.print((char) b);
        }
        System.out.print("\n");

        System.out.print("szSystemStatus: ");
        for(byte b : lpwsaData.szSystemStatus){
            System.out.print((char) b);
        }
        System.out.print("\n");

        System.out.println("iMaxSockets: " + lpwsaData.iMaxSockets);
        System.out.println("iMaxUdpDg: " + lpwsaData.iMaxUdpDg");
    }
}

这可行,我得到以下值:

w版本:2

wHighVersion:514

szDescription: WinSock 2.0

szSystemStatus: 运行中

iMaxSockets: 0

iMaxUdpDg: 0

在 WSAStartup() 成功返回后,我尝试调用 WSAEnumProtocols() 但出现以下错误:

线程“main”java.lang.UnsatisfiedLinkError 中的异常:查找函数“WSAEnumProtocols”时出错:找不到指定的过程。

然后我使用 Dependency Walker 打开 ws2_32.dll,我发现没有同名的函数。我只找到了 3 个名称相似的:WSAEnumProtocolsA()、WSAEnumProtocolsW() 和 WSCEnumProtocols()。 我将使用 WSAEnumProtocolsA() 作为示例,但是我对所有 3 个都使用了下一个过程并得到了相同的结果:

首先我调用了 WSAStartup() 并且没有返回错误。 根据 WSAEnumProtocols 的 MSDN 定义,第一次调用该函数应该是这样的: WSAEnumProtocols(null, wsaprotocol_info, lpdwBufferLength) 第一个参数是 null,第二个是指向 WSAPROTOCOL_INFO 结构的指针,第三个是缓冲区的长度。如果该长度为零,则函数应返回 -1 (SOCKET_ERROR),并且对 WSAGetLastError() 的调用应返回 WSAENOBUFS,这意味着缓冲区不够大,无法包含 WSAEnumProtocols() 返回的信息,应设置变量 lpdwBufferLength具有可以传递给 WSAEnumProtocols 以检索所有请求信息的最小缓冲区大小。 我不能让它工作。 WSAEnumProtocols() 返回 -1 但 lpdwBufferLength 的值没有改变, WSAGetLastError() 返回 0 而不是 10055 (WSAENOBUFS)

我也尝试使用 WSASetLastError() 并将其设置为其他错误代码,然后调用 WSAGetLastError(),但它总是返回 0。

tl;博士 我无法让 WSAEnumProtocols(null, wsaprotocol_info, lpdwBufferLength) 工作。 WSAEnumProtocols() 返回 -1 但 lpdwBufferLength 的值没有改变, WSAGetLastError() 返回 0 而不是 10055 (WSAENOBUFS)

更新:

这是我用来声明 Winsock 函数的接口

public interface Ws2_32 extends Library {
    Ws2_32 INSTANCE = (Ws2_32) Native.loadLibrary("ws2_32", Ws2_32.class);

    int WSAStartup(short version, LPWSADATA lpwsaData);
    int WSAEnumProtocolsW(int[] lpiProtocols, WSAPROTOCOL_INFO lpProtocolBuffer, int lpdwBufferLength);
    int WSACleanup();
    int WSAGetLastError();
    int WSASetLastError(int iError);
}

这是我调用函数的代码:

public class TestWSAEnumProtocolsA {

    public void start(){

        WSAPROTOCOL_INFO        wsaprotocol_info = new WSAPROTOCOL_INFO();
        LPWSADATA               lpwsaData = new LPWSADATA();
        int                     lpdwBufferLength = -2;
        int                     result = 0;
        short                   version = 514;

        if((result = Ws2_32.INSTANCE.WSAStartup(version, lpwsaData)) != 0){
            System.out.println("Error #" + result + " at WSAStartup()");
            return;
        } else {
            System.out.println("WSAStartup() finished correctly.");

            if((result = Ws2_32.INSTANCE.WSAEnumProtocols(null, wsaprotocol_info, lpdwBufferLength)) == -1){
                System.out.println("WSAEnumProtocolsW() returned: " + result);
                System.out.println("lpdwBufferLength is: " + lpdwBufferLength);

                System.out.println("WSAGetLastError() returned: " + Ws2_32.INSTANCE.WSAGetLastError());

                System.out.println("Now I'm setting it to 10004");
                Ws2_32.INSTANCE.WSASetLastError(10004);
                System.out.println("WSAGetLastError() returned: " + Ws2_32.INSTANCE.WSAGetLastError());
            }
        }
    }
}

该代码产生以下输出:

WSAStartup() 正确完成。

WSAEnumProtocolsW() 返回:-1

lpdwBufferLength 为:-2

WSAGetLastError() 返回:0

现在我将其设置为 10004

WSAGetLastError() 返回:0

这就是我定义调用这些函数时使用的结构的方式:

public class WinSock2_structs {

    public static class LPWSADATA extends Structure{

        public short            wVersion;
        public short            wHighVersion;
        public byte             szDescription[] = new byte[256+1];
        public byte             szSystemStatus[] = new byte[128+1];
        public short            iMaxSockets;
        public short            iMaxUdpDg;
        public char             lpVendorInfo;       
}

    public static class WSAPROTOCOLCHAIN extends Structure{

        public int   ChainLen;
        public int   ChainEntries[] = new int[7];
    }

    public static class GUID extends Structure{

        public int      Data1;
        public short    Data2;
        public short    Data3;
        public short    Data4;
        public byte     Data5[] = new byte[8];

    }

    public static class WSAPROTOCOL_INFO extends Structure{

        public int                  dwServiceFlags1;
        public int                  dwServiceFlags2;
        public int                  dwServiceFlags3;
        public int                  dwServiceFlags4;
        public int                  dwProviderFlags;
        public GUID                 ProviderId;
        public int                  dwCatalogEntryId;
        public WSAPROTOCOLCHAIN     ProtocolChain;
        public int                  iVersion;
        public int                  iAddressFamily;
        public int                  iMaxSockAddr;
        public int                  iMinSockAddr;
        public int                  iSocketType;
        public int                  iProtocol;
        public int                  iProtocolMaxOffset;
        public int                  iNetworkByteOrder;
        public int                  iSecurityScheme;
        public int                  dwMessageSize;
        public int                  dwProviderReserved;
        public char                 szProtocol[] = new char[256];

    }
}

【问题讨论】:

  • 请显示您尝试使用WSAEnumProtocols()WSAGetLastError() 的代码。

标签: java jna winsock2


【解决方案1】:

您调用WSAStartup() 两次,这将要求您调用WSACleanup() 两次才能正确卸载WinSock。您应该只调用一次WSAStartup()

WSAPROTOCOL_INFO 结构的szProtocol 成员是TCHAR 元素的数组。 TCHAR 映射到 charwchar_t,具体取决于调用应用程序是针对 ANSI/MBCS 还是 UNICODE 编译的。这就是为什么 ws2_32.dll 中没有 WSAEnumProtocols() 函数的原因。有单独的WSAEnumProtocolsA()(用于 Ansi)和WSAEnumProtocolsW()(用于 Unicode)函数。由于 Java 使用 Unicode 字符串,您应该让您的 JNA 代码使用 WSAEnumProtocolsW()WSAStartup() 不使用TCHAR,只使用char,这就是为什么没有单独的WSAStartupA()WSAStartupW() 函数。

如果您的 JNA 代码无法使 WSAEnumProtocols()WSAGetLastError() 正常工作,则可能是您声明/使用它们错误,但您没有显示任何代码,所以没有人可以肯定地说为什么它不适合你。

更新:试试这样的(我不使用 JNA,所以这可能需要一些调整,但这会给你一个大致的想法):

public interface Ws2_32 extends Library {

    // I don't know how to declare fixed size arrays in JNA,
    // so you will have to adjust these Structue declarations
    // as needed...

    public static class WSAData extends Structure {
        short wVersion;
        short wHighVersion;
        byte  szDescription[257];
        byte  szSystemStatus[129];
        short iMaxSockets;
        short iMaxUdpDg;
        String lpVendorInfo;
    };

    public static class WSAPROTOCOLCHAIN extends Structure {
        int ChainLen;
        int ChainEntries[7];
    };

    public static class WSAPROTOCOL_INFOW extends Structure {
        int dwServiceFlags1;
        int dwServiceFlags2;
        int dwServiceFlags3;
        int dwServiceFlags4;
        int dwProviderFlags;
        GUID ProviderId;
        int dwCatalogEntryId;
        WSAPROTOCOLCHAIN ProtocolChain;
        int iVersion;
        int iAddressFamily;
        int iMaxSockAddr;
        int iMinSockAddr;
        int iSocketType;
        int iProtocol;
        int iProtocolMaxOffset;
        int iNetworkByteOrder;
        int iSecurityScheme;
        int dwMessageSize;
        int dwProviderReserved;
        char szProtocol[256];
    };

    Ws2_32 INSTANCE = (Ws2_32) Native.loadLibrary("ws2_32", Ws2_32.class);

    int WSAStartup(short version, WSADATA lpwsaData);
    int WSAEnumProtocolsW(int[] lpiProtocols, WSAPROTOCOL_INFOW[] lpProtocolBuffer, IntByReference lpdwBufferLength);
    int WSACleanup();
    int WSAGetLastError();
    int WSASetLastError(int iError);
}

.

public static void main(String[] args){
    WSADATA     wsaData = new WSADATA();
    short       version = 2;
    int         result = Ws2_32.INSTANCE.WSAStartup(version, wsaData);

    System.out.println("WSAStartup() returned: " + result);

    if(result == 0){
        System.out.println("WSADATA struct:");
        System.out.println("wVersion: " + wsaData.wVersion);
        System.out.println("wHighVersion: " + wsaData.wHighVersion);

        System.out.print("szDescription: ");
        for(byte b : wsaData.szDescription){
            System.out.print((char) b);
        }
        System.out.print("\n");

        System.out.print("szSystemStatus: ");
        for(byte b : wsaData.szSystemStatus){
            System.out.print((char) b);
        }
        System.out.print("\n");

        System.out.println("iMaxSockets: " + wsaData.iMaxSockets);
        System.out.println("iMaxUdpDg: " + wsaData.iMaxUdpDg");
    }
}

.

public class TestWSAEnumProtocolsA {

    public void start(){

        WSAPROTOCOL_INFOW[]     wsaprotocol_info = new WSAPROTOCOL_INFOW[1];
        WSADATA                 wsaData = new WSADATA();
        IntByReference          dwBufferLength = new IntByReference(628); // sizeof WSAPROTOCOL_INFOW, in bytes
        int                     result = 0;
        short                   version = 2;

        if((result = Ws2_32.INSTANCE.WSAStartup(version, wsaData)) != 0){
            System.out.println("Error #" + result + " at WSAStartup()");
            return;
        }

        System.out.println("WSAStartup() finished correctly.");

        if((result = Ws2_32.INSTANCE.WSAEnumProtocolsW(null, wsaprotocol_info, dwBufferLength)) == -1){
            System.out.println("WSAEnumProtocolsW() returned: " + result);
            System.out.println("dwBufferLength is: " + dwBufferLength.getValue());
            System.out.println("WSAGetLastError() returned: " + Ws2_32.INSTANCE.WSAGetLastError());
        }
    }
}

【讨论】:

  • 感谢您的快速回答和对 ansi 和 unicode 之间区别的澄清也许我在第一篇文章中没有很好地解释自己,但我在一开始只调用了一次 WSAStartup()代码,当它不返回错误时,我调用 WSAEnumProtocolsW()。我已经更新了问题并添加了您询问的代码。再次感谢您的帮助。
  • @H3rnst:在您展示的main() 中,您调用了两次WSAStatup()int result = Ws2_32.INSTANCE.WSAStartup(version,lpwsaData); ... if((resultado = Ws2_32.INSTANCE.WSAStartup(version,lpwsaData)) == 0){。将if 语句改为if (result == 0)
  • @H3rnst:您没有显示您的WSAPROTOCOL_INFO 声明。由于您调用的是WSAEnumProtocolsW(),因此请确保您的WSAPROTOCOL_INFO 的成员与WinSock 的Unicode WSAPROTOCOL_INFOW 结构相匹配。
  • @H3rnst:顺便说一句,LPWSADATA 是一个指向 WSADATA 结构的指针。您需要分配一个实际的WSADATA 并将其传递给WSAStartup() 来填写。看起来您只是分配了一个指针,但没有将它指向任何地方。与WSAEnumProtocolsW() 类似,您只分配了一个WSAPROTOCOL_INFO 实例,但您将lpdwBufferLength 设置为-2,而不是该WSAPROTOCOL_INFO 实例的实际字节大小。如果安装了多个协议,则需要为多个WSAPROTOCOL_INFO分配空间。
  • 你说得对,我现在明白了。但是那段代码只是我做的一个测试,以确保我的结构是正确的,并且我从 WSAStartup() 获得了有效且正确的响应。运行它之后,我得到了我首先发布的输出,然后我在“TestWSAEnumProtocolsA”类(我在更新中发布)中使用了一些代码(忽略名称,我没有更改它,但我正在调用 WSAEnumProtocolsW () 里面)。我发布了运行该类时得到的输出,显示 WSAGetLastError() 和 WSAEnumProtocolsW() 不起作用。
猜你喜欢
  • 2012-09-05
  • 2017-11-06
  • 2011-09-06
  • 1970-01-01
  • 2023-03-10
  • 2020-10-16
  • 2014-01-15
  • 2019-07-29
  • 1970-01-01
相关资源
最近更新 更多