第14章 网络编程
Windows Sockets的实现:Windows Sockets是Windows的网络程序设计接口,它是从Berkeley Sockets扩展而来的,以动态链接库的形式提供使用。它与Berkeley Sockets都基于TCP/IP协议,它们中很多函数都是一致的,如果采用双方共有的这些函数编写网络程序,那么这些网络程序将会很容易地移植到其他系统下。
套接字的类型:
①流式套接字(SOCK_STREAM):提供面向连接、可靠的数据传输服务,数据无差错、无重复的发送,且按发送顺序接收。流式套接字实际上是基于TCP协议实现的;
②数据报式套接字(SOCK_DGRAM):提供无连接服务。数据包以独立包形式发送,不提供无错保证,数据可能丢失或重复,并且接收顺序混乱。数据报式套接字实际上是基于UDP协议实现的。
③原始套接字(SOCK_RAW)
基于TCP(面向连接)的socket编程流程:
服务器端:
①创建套接字;
②将套接字绑定到一个本地地址和端口上;
③将套接字设为监听模式,准备接收客户请求;
④等待客户请求到来;当请求到来后接受连接请求,返回一个新的对应于此次连接的套接字;
⑤用返回的套接字和客户端进行通信;
⑥返回,等待另一客户请求;
⑦关闭套接字;
客户端:
①创建套接字;
②向服务器发出连接请求;
③和服务器端进行通信;
④关闭套接字;
基于UDP(面向无连接)的socket编程流程:
服务器端:
①创建套接字;
②将套接字绑定到一个本地地址和端口上;
③等待接收数据
④关闭套接字;
客户端:
①创建套接字;
②向服务器发送数据;
③关闭套接字;
代码解析:
基于TCP(面向连接)的socket编程流程:
服务器端:
#include <Winsock2.h>
#include <stdio.h>
void main()
{
///加载套接字库
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 1, 1 );
// MAKEWORD(x,y)宏,x、y分别为高低位字节;
err = WSAStartup( wVersionRequested, &wsaData );
// WSAStartup函数用来加载套接字库并进行套接字库的版本协商
if ( err != 0 ) return;
//判断是否我们请求的winsocket版本,如果不是则调用WSACleanup终止winsocket的使用并返回
if ( LOBYTE( wsaData.wVersion ) != 1 ||HIBYTE( wsaData.wVersion ) != 1 )
{
WSACleanup( );
return;
}
///创建套接字并绑定到本地地址与端口上
SOCKET sockSrv = socket(AF_INET, //对于TCP/IP协议的套接字,地址族只能是AF_INET
SOCK_STREAM, //创建流式套接字
0); //零表示自动选择协议
SOCKADDR_IN addrSrv; //定义一个地址结构体的变量;SOCKADDR_IN结构体中除了sa_family //成员以外,其他成员都是按网络字节顺序表示的
addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
//用htonl()方法将INADDR_ANY转换为网络字节序;将地址指定为INADDR_ANY将允许套接字向任何分配给本地机器的IP地址发送或接收数据
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(22222); //指定端口号;为所编写的网络程序指定端口号时,要使用1024以上的端口号;htons把u_short类型从主机字节序转换为网络字节序
bind(sockSrv,(SOCKADDR *) &addrSrv,sizeof(SOCKADDR));
//SOCKADDR大小写是一样的
///设置监听
listen(sockSrv,5);
// listen函数将指定的套接字设置为监听模式,第二个参数是等待连接队列的最大长度
SOCKADDR_IN addrClient;//用来接收连接实体的地址
int len = sizeof(SOCKADDR);
while(1)
{ SOCKET sockConn = accept(sockSrv, (SOCKADDR *) &addrClient,&len);
//返回一个用于当次通信的SOCKET
char sendBuf[100];
sprintf (sendBuf,"Welcome %s to \
http://blog.csdn.net/teshorse",
inet_ntoa(addrClient.sin_addr));
// inet_ntoa函数将一个in_addr结构体类型的参数转换成以点分十进制格式表示的IP地址字符串,相反功能的函数为inet_addr;
send(sockConn,sendBuf,strlen(sendBuf)+1,0);
char recvBuf[100];
recv(sockConn,recvBuf,100,0);
printf("%s\n",recvBuf);
closesocket(sockConn);
…
}
WSACleanup( );
}
客户端:
void main()
{
///加载套接字库…
///创建套接字并连接服务器端,无须绑定
SOCKET sockClient = socket (AF_INET, SOCK_STREAM, 0);
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
//设定服务器端IP地址,"127.0.0.1"是本地回路地址
//不管本地主机上有没有网卡,都可以用这个IP测试网络
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(22222); //端口号要与服务器端保持一致
connect(sockClient,(SOCKADDR *)&addrSrv,sizeof(SOCKADDR));
///接收服务器端发送的数据,并且向服务器端发送数据
char recvBuf[100];
recv(sockClient,recvBuf,100,0);
printf("%s \n", recvBuf);
getchar();
send(sockClient,"I'm teshorse",strlen("I'm teshorse")+1,0);
///关闭套接字,释放资源,并且终止对套接字库的使用
closesocket(sockClient);
WSACleanup();
}//endof main()
基于UDP(面向无连接)的socket编程流程:
服务器端:
#include <Winsock2.h>
#include <stdio.h>
void main()
{
///加载套接字库…
///创建套接字并在本地地址与端口绑定
SOCKET sockSrv = socket (AF_INET,
SOCK_DGRAM/*数据报套接字*/, 0);
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(6000);
bind(sockSrv, (SOCKADDR *)&addrSrv,sizeof(SOCKADDR));
///接收数据
SOCKADDR_IN addrClient;
int len=sizeof(SOCKADDR);
char recvBuf[100];
recvfrom(sockSrv,recvBuf,100,0,
(SOCKADDR*)&addrClient,&len);
printf("%s\n",recvBuf);
closesocket(sockSrv);
WSACleanup();
}
客户端:
///加载套接字库…
///创建套接字并发送消息
SOCKET sockClient = socket(AF_INET,SOCK_DGRAM,0);
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(6000);
sendto(sockClient,"hello",strlen("hello")+1,0,
(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));
closesocket(sockClient);
WSACleanup();