欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

MFC之Socket服务端编程

程序员文章站 2024-01-31 16:10:16
...

       这些天由于工作需要,需要用到MFC的Socket编程,于是和这个许久未见的老朋友C++又有了接触,虽然彼此有些生疏,但还算顺利,哈哈。。。。,经过百度谷歌一番,着实发现资料很多,但是有些会将我们带入误区,特别如果你是个初学者。所以就自己这次的经验总结下吧,闲着也是闲着。

       一、假如你是个初学者,那就让我们先了解了解Socket的一些基本知识

       1、什么是TCP/IP、UDP?

       TCP/IP(Transmission Control Protocol/Internet Protocol)即传输控制协议/网间协议,是一个工业标准的协议集,它是为广域网(WANs)设计的。

  UDP(User Data Protocol,用户数据报协议)是与TCP相对应的协议。它是属于TCP/IP协议族中的一种。

       2 、TCP/IP、UDP之间的区别

       A、TCP是面向连接的传输控制协议,而UDP提供了无连接的数据报服务;
       B、TCP具有高可靠性,确保传输数据的正确性,不出现丢失或乱序;UDP在传输数据前不建立连接,不对数据报进行检查与修改,无须等待对方的应答,所以会出现分组丢失、重复、乱序,应用程序需要负责传输可靠性方面的所有工作;
       C、也正因为以上特征,UDP具有较好的实时性,工作效率较TCP协议高;
       D、UDP段结构比TCP的段结构简单,因此网络开销也小。

       3、Socket是什么?

       Socket熟称“套接字”,是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面(Facade)模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。     

       4、Socket编程有哪些类型?

       常用的Socket类型有:SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等。这里主要讲下SOCK_STREAM和SOCK_DGRAM,其中SOCK_STREAM是基于TCP/IP协议传输的,SOCK_DGRAM就是基于UDP的,两张协议不可相互通信,所以在创建Socket的时候要注意,服务端和客户端要采用同一种协议。

       二、Socket服务端,启动Socket和监听客户端的连接

BOOL CPostClientDlg::InitSocket()
{
	//m_socket=socket(AF_INET,SOCK_DGRAM,0); //基于udp
	m_socket=socket(AF_INET,SOCK_STREAM,0);//基于tcp
	
	if(INVALID_SOCKET==m_socket)
	{
		MessageBox("套接字创建失败!");
		return FALSE;
	}
	SOCKADDR_IN addrSock;
	addrSock.sin_family=AF_INET;
	addrSock.sin_port=htons(6000);
	addrSock.sin_addr.S_un.S_addr=htonl(INADDR_ANY);

	int retval;
	retval=bind(m_socket,(SOCKADDR*)&addrSock,sizeof(SOCKADDR));
	
	if(SOCKET_ERROR==retval)
	{
		closesocket(m_socket);
		MessageBox("绑定失败!");
		return FALSE;
	}

	//创建一线程监听
	RECVPARAM *pRecvParam=new RECVPARAM;
	pRecvParam->sock=m_socket;
	pRecvParam->hwnd=m_hWnd;
	HANDLE hThread=CreateThread(NULL,0,RecvProc,(LPVOID)pRecvParam,0,NULL);
	CloseHandle(hThread);
	
	return TRUE;
}

 

DWORD WINAPI CPostClientDlg::RecvProc(LPVOID lpParameter)
{
	SOCKET sock=((RECVPARAM*)lpParameter)->sock;
	HWND hwnd=((RECVPARAM*)lpParameter)->hwnd;
	delete lpParameter;	//释放内存的操作
	
	if(listen(sock,5) == SOCKET_ERROR)
    	{
    	    //监听客户端,如果是基于UDP的,则不需要listen
	    return 0;
    	} 

	SOCKADDR_IN addrFrom;
	int len=sizeof(SOCKADDR);

	char recvBuf[200]={0};//获取客户端发送的消息
	int retval;
	while(TRUE)
	{
		SOCKET ConnectSocket = accept(sock,(sockaddr*)&addrFrom,&len); //得到客户端的IP地址。 
		retval=recv(ConnectSocket,recvBuf,200,0); 
		if(SOCKET_ERROR==retval)
			break;

	}
	return 0;
}

 其中结构体RECVPARAM定义如下:

struct RECVPARAM
{
 SOCKET sock;
 HWND hwnd;
};

 

 就这么简单,服务端就ok了。

 之前在网上看到的大部门创建服务端的Socket的基本上都是在主线程里加监听,犹如

BOOL CPostClientDlg::InitSocket()
{
	//m_socket=socket(AF_INET,SOCK_DGRAM,0); //基于udp
	m_socket=socket(AF_INET,SOCK_STREAM,0);//基于tcp
	
	if(INVALID_SOCKET==m_socket)
	{
		MessageBox("套接字创建失败!");
		return FALSE;
	}
	SOCKADDR_IN addrSock;
	addrSock.sin_family=AF_INET;
	addrSock.sin_port=htons(6000);
	addrSock.sin_addr.S_un.S_addr=htonl(INADDR_ANY);

	int retval;
	retval=bind(m_socket,(SOCKADDR*)&addrSock,sizeof(SOCKADDR));
	
	if(SOCKET_ERROR==retval)
	{
		closesocket(m_socket);
		MessageBox("绑定失败!");
		return FALSE;
	}

	//监听
	if(listen(m_socket,5) == SOCKET_ERROR)
    	{
    	    //监听客户端,如果是基于UDP的,则不需要listen
	    return FALSE;
    	} 
	while(TRUE)
	{
		SOCKET ConnectSocket = accept(m_socket,(sockaddr*)&addrFrom,&len); //得到客户端的IP地址。 
		.....

	}
	
	return TRUE;
}

 

这样会导致主线程得不到释放程序会假死现象。

当然这是比较原始的Socket编程了,现在对Socket封装的类也有很多,编程起来也很方便,比如用的比较多的是CAsyncSocket,CSocket等等,但是如果要学习的话还是原始的好,毕竟被包装过的,看不到他的真面目,哈哈!

       服务端创建Socket就这么简单,希望对你有所帮助!