采用 SOCKET 参数的 C++ 线程

Posted

技术标签:

【中文标题】采用 SOCKET 参数的 C++ 线程【英文标题】:C++ Threading which takes a SOCKET parameter 【发布时间】:2014-03-07 01:40:05 【问题描述】:

我目前正在做我的高级项目,该项目几乎完成了,但是我需要在不支持 C++11 的 VisualStudio 2010 中实现多线程。因此,我将其用作多线程的源(由我的讲师建议)http://msdn.microsoft.com/en-us/library/windows/desktop/ms682516(v=vs.85).aspx,我完全不知道如何使用 SOCKET 实现它。

我的问题是如何将我已经编码的套接字与多线程一起使用。以下是我到目前为止所拥有的。

typedef struct SenderData 

SOCKET socConnection;

 SENDERDATA, *PSENDERDATA;


DWORD WINAPI SenderThreadFunction( LPVOID lpParam) 
HANDLE hStdout;
PSENDERDATA pDataArray;

TCHAR msgBuf[BUF_SIZE];
size_t cchStringSize;
DWORD dwChars;

// Make sure there is a console to receive output results. 

hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
if( hStdout == INVALID_HANDLE_VALUE )
    return 1;

// Cast the parameter to the correct data type.
// The pointer is known to be valid because 
// it was checked for NULL before the thread was created.

pDataArray = (PSENDERDATA)lpParam;

return 0; 
 

此函数具有在 telnet 中启动会话并从文件发送消息的所有命令

DWORD WINAPI Sender(LPVOID lpParam) 

以下创建连接,我最初在 main() 中创建了该连接,但现在不确定将其放置在哪里

WSADATA wsaData; // Creates wsaData object
WSAStartup(MAKEWORD(2, 2), &wsaData); //Initializes Winsock
//Creates the socket object named "soc(Connection"
SOCKET socConnection = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

SOCKADDR_IN Addr;
HOSTENT* Host = gethostbyname("smtp.com");
Addr.sin_addr.s_addr = inet_addr(inet_ntoa(*(in_addr*)Host->h_addr_list[0]));
Addr.sin_family = AF_INET;
Addr.sin_port = htons(25);

如果我不使用线程,这将在 main() 中调用

if (connect(Connection, (SOCKADDR*)&Addr, sizeof(SOCKADDR_IN)) == SOCKET_ERROR)
    cout << "Connection Failed" << endl;    

在 main() 中执行以下代码时,我将如何放置前面的代码来启动? 我是 C++ 的新手,非常感谢任何帮助。提前谢谢你。

hThreadArray[0] = CreateThread( 
        NULL,                   // default security attributes
        0,                      // use default stack size  
        Sender,       // thread function name
        pDataArray[0],          // argument to thread function 
        0,                      // use default creation flags 
        &dwThreadIdArray[0]);   // returns the thread identifier 

【问题讨论】:

您遇到的实际问题是什么? @RemyLebeau 我不知道如何在 Sender 线程中调用连接函数,同时也已经创建了连接。基本上有没有办法调用Sender函数,它会创建连接并发送数据? 在我看来,C++11 根本不会让你的任务变得更容易,而且 VS2010 也有一些 C++11 的特性。您的问题与多线程相关,学习多线程不是几个小时或几天的课程,尤其是在 C/C++ 中。您链接的示例也是 C 示例而不是 C++ 示例。正确的 C++ 实现会将线程封装到一个线程类中,这会使事情变得更容易。 【参考方案1】:

您要在线程中运行的代码需要在您传递给CreateThread()Sender() 函数中。这就是你所有的套接字调用(WSAStartup()WSACleanup() 除外,它们只能调用一次)。

类似这样的:

DWORD WINAPI Sender( LPVOID lpParam)
 
    // Make sure there is a console to receive output results. 
    HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
    if( hStdout == INVALID_HANDLE_VALUE )
        return 1;

    HOSTENT* Host = gethostbyname("smtp.com");
    if (!Host)
    
        cout << "Unable to resolve smtp.com" << endl;
        return 1;
    

    //Creates the socket object named "soc(Connection"
    SOCKET socConnection = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (socConnection == INVALID_SOCKET)
    
        cout << "Socket Failed" << endl;
        return 1;
    

    SOCKADDR_IN Addr = 0;
    Addr.sin_family = AF_INET;
    Addr.sin_addr = * (in_addr*) Host->h_addr_list[0];
    Addr.sin_port = htons(25);

    if (connect(socConnection, (SOCKADDR*)&Addr, sizeof(Addr)) == SOCKET_ERROR)
    
        cout << "Connection Failed" << endl;
        closesocket(socConnection);
        return 1;
    

    ...

    closesocket(socConnection);
    return 0; 
 

int main()

    WSADATA wsaData; // Creates wsaData object
    WSAStartup(MAKEWORD(2, 2), &wsaData); //Initializes Winsock

    DWORD dwThreadId;
    HANDLE hThread = CreateThread( 
        NULL,                   // default security attributes
        0,                      // use default stack size  
        &Sender,                // thread function name
        NULL,                   // argument to thread function 
        0,                      // use default creation flags 
        &dwThreadId);

    ...

    WaitForSingleObject(hThread, INFINITE);

    DWORD ExitCode = 0;
    GetExitCodeThread(hThread, &ExitCode);

    CloseHandle(hThread);

    if (ExitCode != 0)
    
        ...
    

    ...

    WSACleanup();
    return 0;

【讨论】:

非常感谢你!对此,我真的非常感激!我整天都在为此头疼。你的男人!【参考方案2】:

还有一点关于如何将线程封装到类中以获得真正面向对象和更可重用的代码的帮助:

#include <windows.h>
#include <stdio.h>
#include <assert.h>

class CThread

public:
    void Start()
    
        assert(!m_Handle);
        DWORD thread_id;
        m_Handle = ::CreateThread(NULL, 0, StaticThreadProc, this, 0, &thread_id);
        // this should be a fatal error/exit instead of a simple assert...
        assert(m_Handle);
    

    void Join()
    
        assert(m_Handle);
        ::WaitForSingleObject(m_Handle, INFINITE);
    

protected:
    virtual void Execute() = 0;

    CThread() : m_Handle(NULL) 
    ~CThread()
    
        if (m_Handle)
        
            Join();
            ::CloseHandle(m_Handle);
            m_Handle = NULL;
        
    

private:
    static DWORD WINAPI StaticThreadProc(LPVOID param)
    
        CThread* thread = reinterpret_cast<CThread*>(param);
        thread->Execute();
        return 0;
    

private:
    HANDLE m_Handle;
;

class CMyThread : public CThread

public:
    CMyThread()
    
        m_Sock = NULL;
        m_Whatever = 0;
        m_OtherParam = 0;
        m_Result = 0;
    

    // Use this to initialize the parameters for your thread before starting it...
    // You could initialize from your constructor too if you wouldn't store your
    // threads in an array by value...
    void Init(SOCKET sock, int whatever_parameter_you_need, int other_param)
    
        m_Sock = sock;
        m_Whatever = whatever_parameter_you_need;
        m_OtherParam = other_param;
    

    int GetResult() const
    
        return m_Result;
    

protected:
    virtual void Execute() override
    
        // Use m_Sock, m_Whatever, m_OtherParam ....
        // Fill out m_Result before returning from the Execute() method of the thread.

        // You can also create the socket inside the thread if you want,
        // noone forces you to pass it here as an init parameter.
        m_Result = 5;
    

private:
    SOCKET m_Sock;
    int m_Whatever;
    int m_OtherParam;
    int m_Result;
;


int main()

    // TODO: network init (WSAStartup)

    const int NUM_THREADS = 3;
    CMyThread threads[NUM_THREADS];

    // 1. Initializing threads with incoming parameters to work with
    for (int i=0; i<NUM_THREADS; ++i)
    
        //threads[i].Init(...)
    

    // 2. Starting threads
    for (int i=0; i<NUM_THREADS; ++i)
        threads[i].Start();

    // 3. Waiting for threads to finish...
    for (int i=0; i<NUM_THREADS; ++i)
        threads[i].Join();

    // 4. Processing results if needed
    int result0 = threads[0].GetResult();
    printf("%d\n", result0);

    // TODO: Network Cleanup (WSACleanup)

    return 0;

【讨论】:

以上是关于采用 SOCKET 参数的 C++ 线程的主要内容,如果未能解决你的问题,请参考以下文章

c++中如何将子线程的参数传回主线程

C++ 函数透明地采用左值和右值参数

lua socket c++ socket效率怎样

带参数的 C++ 简单线程(无 .net)

C++单例模式实现(线程安全&支持多参数构造)

C++并发与多线程 3_线程传参数详解,detach 注意事项