ScoKet套接字编程
1 寻址方式和字节顺序
1.1 寻址方式
Winsock(Socket API)中,可以使用TCP/IP地址家族中统一的套接字地质结构解决
TCP/IP寻址中可能出现的问题,套接字地址结构定义如下
struct sockaddr_in{
short sin_family; //地址格式,AF_INET表示TCP/IP
unsigned shory sin_port;//端口号码
struct in_addr sin_addr;//IP地址
char sin_zero[8]; //备用
}
该结构变量sin_addr表示32位的IP地址结构。其结构定义如下
struct in_addr{
union{
struct{
unsigned char s_b1,s_b2,s_b3,s_b4;
}S_un_b;
struct{
unsigned short s_w1,s_w2;
}S_un_w;
unsigned long S_addr;
}S_un;
};
1.2 字节顺序
网络字节顺序与主机字节顺序直接的转换函数,如下
u_short htons(u_short hostshort);//将一个u_short类型的IP地址从主机字节顺序转换到网络字节顺序
u_long htonl(u_long hostlong);//讲一个u_long类型的IP地址从主机字节顺序转换到网络字节顺序
u_long ntohl(u_long netlong);//将一个u_long类型的IP地址从网络字节顺序转换到主机字节顺
u_short ntohs(u_short netshort);//讲一个u_short类型的IP地址从网络字节顺序转换到主机字节顺序
unsigned long inet_addr(const char FAR *cp);//讲一个字符串IP转换到以网络字节顺序排列的IOP地址
char FAR *inet_ntoa(struct in_addr in);//讲一个以网络字节顺序排列的IP地址转换为一个字符串IP
2 Socket相关函数
Windows网络程序开发均是基于Windows套接字实现,介绍MFC中的CSocket类函数
2.1创建套接字
CSocket sock;
CSocket *sock;
sock=new CSocket;
2.2绑定地址信息
BOOL Bind(const SOCKADDR* lpSockAddr,int nSockAddrLen);
lpSockAddr指定将要绑定的服务器地址结构
nSockAddrLen表示地址结构的长度
2.3服务器监听客户端连接请求
BOOL Listen(int nConnectionBacklog=5);
nConnectionBacklog表示套接字监听客户端请求的最大数目,有效范围为1-5,表示该套接字最多只能监听
5个客户端所发送的请求。
2.4连接服务器
客户端创建套接字成功后,可以调用函数Connect向服务端发送连接请求
BOOL Connect(const SOCKADDR* lpSockAddr,int nSockAddrLen);
lpSockAddr表示将连接的服务器地址结构
nSockAddrLen表示地址结构的长度大小
2.5数据交换
服务器和客户端都是通过函数Send()和Receive()进行数据交换
virtual int Send(const void* lpBuf,int nBufLen,int nFlags=0);
virtual int Receive(void* lpBuf,int nBufLen,int nFlags=0);
Send()用于发送指定缓冲区的数据,函数Receive()用于接收对方发送的数据,并将数据
存放在指定缓冲区中
参数lpBuf表示数据缓冲区地址
参数nBufLen表示数据缓冲区大小
参数nFlag表示数据发送或接收的标志
2.6关闭套接字对象
sock.Close();
3 Winsock网络程序开发流程
如果在应用程序第二步没有选择项目支持Windows Socket功能,则在程序中手动添加以下头文件
#include
3.1Winsock编程流程
3.1.1 初始化和释放套接字库
所有的Winsock函数均是从动态链接库WS2_32.DLL中导出的,但是VC并没有默认与该库进行连接,所以
在工程设置中,在库模块中添加动态链接库WS2_32.DLL,这样永华就可以调用WinSock函数了。
用户首先须从动态链接库中调用函数WSAStartup()对该库进行初始化,之后才能继续正确调用其他的winSock函数
int WSAStartup(WORD wVersionRequested,LPWSAData);
wVersionRequested表示当前套接字库版本号,lpWSAData指向结构体WSADATA的指针变量
初始化套接字库
WSAData data;
WORD wVersionRequested=MAKEWORD(2,0);
::WSAStartup(wVersionResquested,&data);
当程序退出时,还要调用WSACleanup()释放套接字库
::WSACleanup();
3.1.2 创建套接字句柄
Socket API中,创建套接字句柄函数为scoket().
SOCKET socket(
int af,//指定套接字所用的地址格式,TCP/IP为AF_INET
int type,//套接字类型
int protocol//如果参数type已经指定为套接字类型为TCP或者UDP,则该参数可以设置为0
)
创建流式套接字句柄:
SOCKET s;
s=::socket(AF_INET,SOCK_STREAM,0);
3.1.3绑定地址信息
int bind(
SOCKET s,//套接字句柄
const struct sockaddr FAR* name,//地址结构信息
int namelen//地址结构大小
)
3.1.4 服务器监听
int listen(
SOCKET s,//实现监听功能的套接字句柄
int backlog //指定监听的最大连接数量
)
3.1.5 客户端连接
int Connect(
SOCKET s,//套接字句柄
const struct sockaddr FAR*name,//将要连接的服务器地址信息结构指针
int namelen//地址信息结构体长度
)
3.1.6 数据收发
int send(SOCKET s,const char FAR *buf,int len,int flags)//发送数据函数
int recv(SOCKET s,char FAR*buf,int len,int flags)//接受数据函数
buf表示指向数据缓冲区的指针变量,参数flag通常设置为0
3.1.7 关闭套接字
int closesocket(
SOCKET s//将关闭的套接字句柄
)
TCP服务端编程
#include//包含头文件
#include
#include
#pragma comment(lib,"WS2_32.lib")//显式连接套接字库
int main()
{
WSADATA data;//定义WSADATA结构体对象
WORD w=MAKEWORD(2,0);//定义版本号码
char sztext[]="欢迎你\r\n";//定义并初始化发送到客户端的字符数组
::WSAStartup(w,&data);//初始化套接字库
SOCKET s,s1;//定义连接套接字哭和数据收发套接字句柄
s=::socket(AF_INET,SOCK_STREAM,0);//创建TCP套接字
sockaddr_in addr,addr2;//定义套接字地质结构
int n=sizeof(addr2);//获取套接字地址大小
addr.sin_family=AF_INET;//初始化套接字结构
addr.sin_port=htons(75);
addr.sin_addr.S_un.S_addr=INADDR_ANY;
::bind(s,(sockaddr*)&addr,sizeof(addr));//绑定套接字
::listen(s,5);//监听套接字
printf("服务器已经启动\r\n");//输出提示信息
while(true)
{
s1=::accept(s,(sockaddr*)&addr2,&n);//接受连接请求
if (s1!=NULL)
{
printf("%s 已经连接上\r\n",inet_ntoa(addr2.sin_addr));
::send(s1,sztext,sizeof(sztext),0);//向客户端发送字符数组
}
::closesocket(s);//关闭套接字句柄
::closesocket(s1);
::WSACleanup();//释放套接字库
if (getchar())
{
return 0;//正常结束程序
}
else
{
::Sleep(100);//程序睡眠0.1秒
}
}
}
TCP客户端编程
#include//包含头文件
#include
#include
#pragma comment(lib,"WS2_32.lib")//显式连接套接字库
int main()
{
WSADATA data;//定义WSADATA结构体对象
WORD w=MAKEWORD(2,0);//定义版本号码
::WSAStartup(w,&data);//初始化套接字库
SOCKET s;//定义连接套接字和数据收发套接字句柄
char sztext[10]={0};
s=::socket(AF_INET,SOCK_STREAM,0);//创建TCP套接字
sockaddr_in addr;//定义套接字地址结构
addr.sin_family=AF_INET;//初始化地址结构
addr.sin_port=htons(75);
addr.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
printf("客户端已经启动\r\n");
::connect(s,(sockaddr*)&addr,sizeof(addr));//连接服务器端
::recv(s,sztext,sizeof(sztext),0);//接受信息
printf("%s\r\n",sztext);
::closesocket(s);//关闭套接字句柄
::WSACleanup();//释放套接字库
if (getchar())//如果有输入,则关闭程序
{
return 0;
}
else
{
::Sleep(100);//程序睡眠0.1秒
}
}
先运行服务端程序,再运行客户端程序
如果您觉得这篇博文有用,请访问我的个人站:http://www.stubbornhuang.com,更多博文干货等着您。