这些天由于工作需要,需要用到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就这么简单,希望对你有所帮助!