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