分享

C/S 多线程Socket类

 seedoly 2010-09-02
C/S模式
          客户机/服务器模式的建立基于以下两点:首先,建立网络的起因是网络中软硬件资源、运算能力和信息不均等,需要共享,从而造就拥有众多资源的主机提供服务,资源较少的客户请求服务这一非对等作用。其次,网间进程通信完全是异步的,相互通信的进程间既不存在父子关系,又不共享内存缓冲区,因此需要一种机制为希望通信的进程间建立联系,为二者的数据交换提供同步,这就是基于客户机/服务器模式的TCP/IP。
         客户机/服务器模式在操作过程中采取的是主动请求的方式。
基于TCP(面向连接)的socket编程
         服务端进程通过bind方法将其套接字告知系统,以使其他的套接字能找到它。它可通过套接字的“侦听(listen)”来“接收(accept)”发过来的信息。客户端的进程同服务端套接字建立连接然后交换信息。需要的信息都可以从该通道向任一端进行发送。
面向连接的TCP通信过程如下:
服务器:
   创建端点 (socket())
   绑定地址(bind())
   指定队列(listen())
   等待连接 (accept())
   传输数据 (read()/write())
客户端:
   创建端点 (socket())
   链接服务器 (connect())
   传输数据(read()/write())
 
基于UDP(面向无连接)的socket编程
          用无连接协议,双方的套接字都需要用bind方法来告知系统。这是因为每方的信息都会单独处理,所以每次服务端发信息过来时,客户端都需要找到它,反之亦然。每次调用bind方法,都绑定了一个新的端口。当然,如果端口已经被使用了,则是不能被绑定的。如果你指定的端口为0,则系统会把当前可用的端口自动给你一个。由于发送信息的额外任务,进程不会使用read/write方法,而是使用recvfrom/sendto方法。这两个方法的参数一个是要写入的套接字,另一个则是远程计算机上服务的地址。
 
服务端:
   创建端点(socket())
   绑定地址 (bind())
   传输数据(sendto()/recvfrom())
客户端:
   创建端点 (socket())
   绑定地址(bind()) (connect方法可选择调用)
   连接服务端(connect())
   传输数据(sendto()/recvfrom())
 
多线程的设计
         《VC中利用多线程技术实现线程之间的通信》这篇文章比较适合线程的了解,Win32 提供了一系列的API函数来完成线程的创建、挂起、恢复、终结以及通信等工作。MFC中使用线程要注意:
         1、尽量少的使用全局变量、static变量做共享数据,尽量使用参数传递对象。
         2、在MFC中请慎用线程。因为MFC的框架假定你的消息处理都是在主线程中完成的。首先窗口句柄是属于线程的,如果拥有窗口句柄的线程退出了,如果另一个线程处理这个窗口句柄,系统就会出现问题。而MFC为了避免这种情况的发生,使你在子线程中调用消息(窗口)处理函数时,就会不停的出Assert错误,烦都烦死你。典型的例子就时CSocket,因为CSocket是使用了一个隐藏窗口实现了假阻塞,所以不可避免的使用了消息处理函数,如果你在子线程中使用CSocket,你就可能看到assert的弹出了。
         3、不要在不同的线程中同时注册COM组件。
常见问题的解决
1、关闭套接字
         我们在利用IOCP(完成端口)进行程序设计的时候,经常要关闭一些不满足条件的套接字。假如我们直接采用closesocket方法进行关闭的话,绑定到IO端口的此套接字的未发送的数据就会丢失,这种情况是我们不愿意发生的。下面介绍一种合理关闭此套接字的方法:
  首先,利用setsockopt(MSDN)函数设定套接字的选项,我们把此套接字设定为:假如有数据未发送,当数据发送完后再关闭此套接字。
  代码如下:
  LINGER lingerStruct;
  lingerStruct.l_onoff = 1;
  lingerStruct.l_linger = 0;
  setsockopt(Socket, SOL_SOCKET, SO_LINGER,
  (char *)&lingerStruct, sizeof(lingerStruct) );
  // Now close the socket handle. This will do an abortive or graceful close, as requested.
  CancelIo((HANDLE) Socket);
  closesocket(Socket);
  clientSocket = INVALID_SOCKET;
  当在完成端口的数据被发送出去之后,套接字就会被关闭,这样我们就完成了一个套接字的关闭。
 2、解决 Socket API错误代码:WSAECONNRESET (10054)
       出现原因:使用UDP SOCKET时(利用事件触发方式),如果发送端在发送数据时(WSASendTo),接收端没还有创建,那么发送端将会收到一个事件通知,此时调用WSARecv()函数时将会产生调用错误(WSAECONNRESET ),从这以后,这个发送端这个SOCKET无法接受到数据。解决办法:
         a.头文件中加入下面代码: 
               #include <Winsock2.h> 
               #pragma comment(lib,"ws2_32.lib") 
               #define IOC_VENDOR 0x18000000 
               #define _WSAIOW(x,y) (IOC_IN|(x)|(y)) 
               #define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR,12) 
         b.在创建socket之后加入下面代码: 
               DWORD    dwBytesReturned = 0; 
               BOOL        bNewBehavior = FALSE; 
               DWORD    status; 
               status = WSAIoctl(m_hSock, SIO_UDP_CONNRESET, 
                           &bNewBehavior, 
                           sizeof (bNewBehavior), 
                           NULL, 0, &dwBytesReturned, 
                           NULL, NULL); 
      <参考 http://blog.csdn.net/wupangzi/archive/2009/07/27/4384081.aspxhttp://hi.baidu.com/jetqu2003/blog/item/397700031435e9703812bbcc.html>

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多