C/S 多线程Socket类

来源:百度文库 编辑:神马文学网 时间:2024/05/01 16:08:46
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())
多线程的设计         《》这篇文章比较适合线程的了解,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;
当在完成端口的数据被发送出去之后,套接字就会被关闭,这样我们就完成了一个套接字的关闭。
<参考http://www.examda.com/ncre2/cpp/fudao/20090107/091252332.html>
2、解决 Socket API错误代码:WSAECONNRESET (10054)
出现原因:使用UDP SOCKET时(利用事件触发方式),如果发送端在发送数据时(WSASendTo),接收端没还有创建,那么发送端将会收到一个事件通知,此时调用WSARecv()函数时将会产生调用错误(WSAECONNRESET ),从这以后,这个发送端这个SOCKET无法接受到数据。解决办法:
a.头文件中加入下面代码:
#include
#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.aspx及http://hi.baidu.com/jetqu2003/blog/item/397700031435e9703812bbcc.html>