理解远程控制中的顺序和方法

实在搞不懂老方写的是什么东西了!

Server端:

       启动Server,创建socket变量,socket(AF_INIT, SOCK_STREAM, IPPROTO_IP);三个参数设置:地址规范,socket类型,协议类型。

接下来要bind(SOCKET s, const atruct sockaddr FAR *name, int namelen);三个参数设置:空闲的socket, 指派socket地址, name参数的长度值。对于第二个参数name机构体:需要提前设置其参数:地址规范:name.sin_family = AF_INIT, 端口:name.sin_port = htons(1234), 网络地址:name.sin_addr.S_un.S_addr = inet_addr(“127.0. 0.1” );

但对于name初定义却声明为:SOCKADDR_IN stockaddrin = {0};

所以在bind 时对stockaddrin 需进行强制类型转换即:(sockaddr*&stockaddrin.

所以整体为:

Bind(m_sock_server,sockaddr*&stockaddrin, sizeof(SOCKADDR_IN));

对于错误处理,可用WSAGetLastError()获得当前的错误,如果发生的话!

String CsErr ;

CsErr.Format(_T(“%d”), WSAGetLastError());

将错误标示放入CsErr中显示出来:AfxMssageBox(“Bind Error !”+CSErr);

 

接下来进行listen(socket s, int backlog);

第一个参数:当前的套接字,backlog默认设置为SOMAXCONN,看MSDN说的意思应该是随机分配其最大值(原文:There is no standard provision to find out the actual backlog value.

同样的对于listen 失败也可以用WSAGetLastError()来返回错误!

 

设置好了这些就要创建一个线程做些事情了,

CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes,

DWORD dwStackSize,

LPTHREAD_START_ROUTINE lpStartAddress,

LPVOID lpParameter,

DWORD dwCreationFlags,

LPDWORD lpThreadId);此为函数原型。

第一个参数设置为线程的属性,因为Must be NULL,所以可以暂且将其抛开不论。

第二参数也可以Ignored(不理睬):MSDN原文:Ignored.TheDefault stack size for a thread is determined by the linker setting /STACK.

接下来看第三个参数:语义就是线程开始地址,查MSDN需要我们提供出一个函数的地址,即为:DWORD WINAPI ThreadProc(
LPVOID lpParameter);既然知道要做一个函数出来,现在就继续往后看。
      
      

第四个参数,可以设置一个32位的指针指向当前线程(MSDN原文:Long pointer to a single 32-bit parameter value passed to eht thread,看 孙鑫 老师的教程说:我们可以通过这个参数给创建的新县城传递参。该参数提供了一种将初始化值传递给线程函数的手段。这个参数的值既可以是一个数值,也可以是一个指向其他信息的指针。回过头再看 方 老师的代码将此值设为:this,似乎是要在当前线程中使用当前CserverDlg类的某些信息,但看ThreadProc_Accept函数中并未用到,而在 孙鑫 老师给出的示例中将其设为NULL,此处暂不深究了吧~

5个参数:设置当前创建的线程的控制标记:0:表示创建后立即执行;CREATE_SUSPENDED :表示暂不执行直到ResumeThread (function is called)被调用.

6个参数:是一个返回值,指向一个变量,用来接收线程ID

函数成功则返回该线程的句柄,失败则返回NULL

DWORD dwThread = 0;

HANDLE hAcceptThread = CreatThread(NULL, 0, ThreadProc_Accept, this, 0, &dwTheadId);

 

好了现在要回过头来看第三个参数函数的定义:

DWORD WINAPI ThreadProc_Accept(LPVOID lpParameter)

该线程用于接收客户端的连接,lpParameter参数指向Thread data(MSDN原文:Receives the thread data passed to the function using the lpParameter parameter of the CreateThread or CreateRemoteThread function.).

Ioctlsocket(SOCKET socket, long cmd, u_long FAR *argp);该函数用于设置套接字的I/O模式。

第二个参数MSDN提供出来的可选项有:FIONBIO, FIONREAD , SIOCATMARK.这个三个参数的MSDN解释看的我好吃力,按照老方的说法使用FIONBIO,对于该程序已经够了。

或许我该到网上查下这三个参数的中文释义。做个标记#######设个?

第三个参数Pointer to a parameter for cmd.此处也颇为迷惑为何定义了一个u_long uAsync = 1;同时:ioctlsocket(p->m_sock_server, FIONBIO, &p->uAsync);

接下来就是接收客户端的连接:

SOCKADDR_IN Client_stockaddrin = {0};

int nLenStockAddr = sizeof(SOCKADDR_IN);

SOCKET socket_Client = 0;

SOCKET socket_Client = accept(p->m_sock_server, (SOCKADDR*)&Client_stockaddrin, &nLenStockAddr);

剖析下accept:第一个参数(s)是套接字描述符,该套接字已经通过listen函数将其设置为监听状态;第二个参数(addr)是指向一个缓冲区的指针,该缓冲区用来接收连接实体的地址,也就是当前当客户端发起连接,服务器端接受这个连接时,保存发起连接的这个库护短的IP地址信息和端口信息;第三个参数(addrlen)也是一个返回值,指向一个整型的指针,返回包含地址信息的长度。

class RecvParam

{

public:

  CServerDlg* m_this;

  SOCKET      m_socket_Client;

  in_addr     m_addrin;

};

 

if ( INVALID_SOCKET == socket_Client )

    {

      //异步处理

      if ( p->uAsync == 1)

      {

        Sleep(50);

        continue;

      }

      else

      {

        AfxMessageBox("Accept error!");

        break;

      }

    }

    else

    {

      RecvParam* NewParam = new RecvParam;

      NewParam->m_this = p;

      NewParam->m_socket_Client = socket_Client;

      NewParam->m_addrin = Client_stockaddrin.sin_addr;

 

      DWORD dwTheadId = 0 ;

      HANDLE hRecvThread = 0;

      if ( !(hRecvThread = CreateThread(NULL,0,

            ThreadProc_Recv, NewParam,0,&dwTheadId)))

      {

        AfxMessageBox("Create Recv Thread!");

        break;

      }

     

      //Add List Node

      NodeClient node;

      node.Socket = socket_Client;

      node.Inaddr = Client_stockaddrin.sin_addr;

      node.Recvthreadhandle = hRecvThread;

      node.recvparam = NewParam;

      p->m_ClientList.AddTail(node);

      p->ListClientUsers();

}

/

class NodeClient

{

public:

  SOCKET   Socket;

  in_addr  Inaddr;

  HANDLE   Recvthreadhandle;

  RecvParam* recvparam;

};

//Add List Node

      NodeClient node;

      node.Socket = socket_Client;

      node.Inaddr = Client_stockaddrin.sin_addr;

      node.Recvthreadhandle = hRecvThread;

      node.recvparam = NewParam;

      p->m_ClientList.AddTail(node);

      p->ListClientUsers();

除去所要进行的判断之后,老方又创建了一个线程以进行接收到的数据的处理,同时对所开线程进行了异步处理。

hRecvThread = CreateThread(NULL,0,

            ThreadProc_Recv, NewParam,0,&dwTheadId);

DWORD WINAPI ThreadProc_Recv(LPVOID lpParameter); 在处理接收到的消息函数中进行数据包的解析,解析中用到了Windows Sockets recv函数从一个已连接的套接字接收数据。

int recv(SOCKET s, char FAR* bufint len, int flags);第一个参数是建立连接后准备接收数据的那个套接字,第二个参数是指向缓冲区的指针,用来保存接收的数据,第三个参数是缓冲区长度,第四个参数与send 函数的第四个擦火速类似,通过设置这个值可以影响这写函数调用的行为,一般设为0.

哦,在后边有while(TRUE)循环接收recv()数据,应该是此后传来的数据了,啊,有点意思了,数据包一个一个的传过来,接收一个一个的接收,第一个是数据包里是该数据类型,以后的包都是数据!

那么后边的添加显示消息就好理解的多了,int CServerDlg::GetRecvData1(CDataPack& m_DataPack,

 

 

//recv client data

DWORD WINAPI ThreadProc_Recv(LPVOID lpParameter)

{

  RecvParam * param = (RecvParam*)lpParameter;

  CServerDlg* p = ((RecvParam*)lpParameter)->m_this;

  SOCKET socket_Client = ((RecvParam*)lpParameter)->m_socket_Client;

  in_addr Client_stockaddrin = ((RecvParam*)lpParameter)->m_addrin;

 

  while (TRUE)

  {

//  char buffTotal[1024] = {0};

//  BOOL BGet = p->GetRecvData(socket_Client,buffTotal);

   

    //数据包头部

    CDataPack m_DataPack;

 

    //数据

    char* buffTotal = NULL;

 

    int nGetLen = p->GetRecvData1(m_DataPack,

                    socket_Client,

                    &buffTotal);

    if ( nGetLen >= 0 )

    {

      while(TRUE)

      {

        if ( m_DataPack.nType == CMD_MSG )

        {

          if ( strcmp(buffTotal,"server stop") == 0 )

          {

            //remove client node

            p->RemoveClientList(socket_Client);

            //refresh user login list

            p->ListClientUsers();

            break;

          }

          else if ( strcmp(buffTotal,"quit") == 0 )

          {

            //remove client node

            p->RemoveClientList(socket_Client);

            //refresh user login list

            p->ListClientUsers();

            //print user logout info

            CString csTxt;

            csTxt.Format("%s quit",

              inet_ntoa(Client_stockaddrin));

            p->AddMsg(csTxt);

            break;

          }

          else

          {

            //正常消息

            p->AddMsg(CString(buffTotal));

            break;

          }

        }

        else if ( m_DataPack.nType == CMD_SENDSCREENREQUEST )

        {

          //remove client node

          p->SendMyScreen(socket_Client);

          break;

        }

      }

  

      if ( buffTotal )

      {

        delete buffTotal;

        buffTotal = NULL;

      }

    }

  }

  return 0;

}                            

SOCKET socket_Client,

char** ppNewBuffOut)

{

  char szBuff[10] = {0};

 

  while(TRUE)

  {

    //先接收数据包的类型 CDataPack

    int nRecvNum = recv(socket_Client,(char*)&m_DataPack,

      sizeof(CDataPack),0);

 

    if ( nRecvNum == SOCKET_ERROR )

    {

      if ( uAsync == 1 )

      {

        Sleep(50);

        continue;

      }

      else

      {

        return -1;

      }

    }

 

    if ( nRecvNum != SOCKET_ERROR )

    {

      break;

    }

  }

  //解包CDataPack

  if ( m_DataPack.nLenData == 0 )  return 0;

 

  *ppNewBuffOut = new char[m_DataPack.nLenData];

 

  memset(*ppNewBuffOut,0,m_DataPack.nLenData);

 

  char* pBuffTotal = *ppNewBuffOut;

 

  DWORD nLenRecv = 0;

 

  while (TRUE)

  {

    int nRecvNum = recv(socket_Client,szBuff,10,0);

   

    if ( nRecvNum == SOCKET_ERROR )

    {

      if ( uAsync == 1 )

      {

        Sleep(50);

        continue;

      }

      else

        return  -1;

    }

    else

    {

      memcpy(pBuffTotal,szBuff,nRecvNum);

     

      pBuffTotal += nRecvNum;

 

      nLenRecv += nRecvNum;

 

      if ( m_DataPack.nLenData == nLenRecv  )

        break;

    }

  }

 

  return m_DataPack.nLenData;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值