对于socket的用途,大家都对此很熟悉了。socket的编程总体分为服务端的编程和客户端的编程。类型我们常用的有TCP、UDP两种类型。
服务端的编程步骤:
1.初始化socket库。
2.绑定本机地址和端口。(服务端特有)
3.监听端口,等待客户端连接。
4.当有客户端连接,进行处理,但后继续监听或者结束程序。
5.退出程序,关闭socket,终止对socket库的使用。
1、这里我采用的MFC框架,首先在StdAfx.h头文件添加socket头文件和lib文件
#include "winsock2.h"
#pragma comment(lib,"ws2_32.lib")
2、初始化socket库。这里主要的工作就是定义了socket的版本,并将信息返回到wsadata结构体中
WSADATA wsd;
int ret=WSAStartup(MAKEWORD(2,2),&wsd);
if (ret!=0)
{
return FALSE;
}
if (HIBYTE(wsd.wVersion)!=2||LOBYTE(wsd.wVersion)!=2)
{
AfxMessageBox("初始化Socket失败!");
WSACleanup();
return FALSE;
}
3、创建socket。在创建socket的时候,第一个参数定义了协议域,AF_IET表示采用IPV4(32位)与端口号(16位)的组合;第二个参数是socket类型,sock_stream流式指的是TCP、sock_dgram指的是UDP;第三个参数是协议类型常用协议IPPROTO_TCP、IPPROTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议*/,我们可以填写0这样就可以自适应。
//创建
MySocket=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if (MySocket==INVALID_SOCKET)
{
int iError=WSAGetLastError();
CString strMsg;
strMsg.Format("Failed socket(),Error Code is %d\n",iError);
AfxMessageBox(strMsg);
return ;
}
4、设置服务端信息。我们用到了sockaddr_in这个结构体,需要关注的三个参数,第一个是IP地址,也就是要服务器要绑定的IP地址,可以自己指定也可以设置成这样。saddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY); //定义IP地址;第二个参数是协议类型,更上面创建的时候对应;第三个就是端口号了。
//设置服务器信息
sockaddr_in ServerAddr;
CString localIP; //获取本地IP地址
char hostname[MAX_PATH] = {0};
gethostname(hostname,MAX_PATH);
hostent* ptent = gethostbyname(hostname);
if (ptent)
{
localIP = inet_ntoa(*(in_addr*)ptent->h_addr_list[0]);
}
ServerAddr.sin_addr.S_un.S_addr=inet_addr(localIP.GetBuffer(0));
ServerAddr.sin_family=AF_INET;
ServerAddr.sin_port=htons(4600);//1024 ~ 49151:普通用户注册的端口号
5、绑定。第一个参数是服务器端的socket,第二个参数存储了服务器的一些信息上面定义好的。
//绑定
if (bind(MySocket,(SOCKADDR *)&ServerAddr,sizeof(SOCKADDR))==SOCKET_ERROR)
{
int iError=WSAGetLastError();
CString strMsg;
strMsg.Format("Failed Bind,Error Code is %d\n",iError);
AppendText(strMsg);
}
6、监听客户端的连接。第一个参数的服务器的socket,第二个参数是可以连接的客户端最大个数。开始,我一直不太理解第二个参数,认为是连接一个客户端后的参数,经过我测试证明,确实是可以连接客户端的最大个数。
//监听客户端连接
if(listen(MySocket,2)==SOCKET_ERROR)
{
int iError=WSAGetLastError();
CString strMsg;
strMsg.Format("Failed listen,Error Code is %d\n",iError);
AppendText(strMsg);
}
7、等待客户端的连接。如果有多个客户端同时连接,在此处可以开辟多个线程去处理。一个线程对应于一个客户端,accept()函数,第一个参数为Server端socket,第二个为SOCKADDR结构体。如果执行成功,则会返回客户端的socket和存有客户端socket信息的结构体
//中断等待连接
SOCKADDR_IN ClientAddr;
int len=sizeof(sockaddr);
pDlg->ClientSocket=accept(pDlg->MySocket,(SOCKADDR *)&ClientAddr,&len);
if (pDlg->ClientSocket==INVALID_SOCKET)
{
int iError=WSAGetLastError();
CString strMsg;
strMsg.Format("Failed accept,Error Code is %d\n",iError);
pDlg->AppendText(strMsg);
}
8、发送数据。send函数第一个参数客户端socket,第二个参数发送的数据,返回发送成功的字符个数。这里需要注意的是发送的时候,需要加1。把字符串最后的'\0'发送过去。
char SendBuf[1024];
sprintf(SendBuf,"%s",str);
if(send(ClientSocket,SendBuf,strlen(SendBuf)+1,0)==SOCKET_ERROR)
{
int iError=WSAGetLastError();
CString strMsg;
strMsg.Format("Failed send,Error Code is %d\n",iError);
AppendText(strMsg);
}
9、接收数据。recv函数,第一个参数为客户端socket,返回的是接收的字符个数。如果,接收失败,则关闭客户端socket
num=recv(pDlg->ClientSocket,RecBuf,1024,0);
if (num>=0)
{
RecBuf[num]='\0';
CString str;
str.Format("收到:%s\r\n",RecBuf);
pDlg->AppendText(str);
}
else
{
int iError=WSAGetLastError();
CString strMsg;
strMsg.Format("Failed recv,Error Code is %d\n",iError);
pDlg->AppendText(strMsg);
closesocket(pDlg->ClientSocket);
以上就是服务端的程序。