数据报套接字提供一种无连接、不可靠的服务。该服务并不能保证数据传输的可靠性,数据有可能在传输过程中丢失或出现数据重复,且无法保证顺序地接收到数据。使用UDP协议进行数据的传输。
服务端代码
//服务端
#include <stdio.h>
#include <winsock2.h>
#pragma comment(lib,"ws2_32.lib")
int main(int argc, char* argv[]) {
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
SOCKET sockServer = socket(AF_INET, SOCK_DGRAM, 0);
if(sockServer==INVALID_SOCKET)//如果套接字创建失败,则打印错误信息并退出程序。
{
printf("sock error!");
return 0;
}
SOCKADDR_IN addrServer;
addrServer.sin_addr.s_addr = INADDR_ANY;
addrServer.sin_family = AF_INET;
addrServer.sin_port = htons(6000);
if(bind(sockServer, (SOCKADDR*)&addrServer, sizeof(SOCKADDR))==SOCKET_ERROR)//如果绑定失败,则打印错误信息并退出程序。
{
printf("bind error!");
closesocket(sockServer);
return 0;
}
SOCKADDR_IN addrClient;
int len=sizeof(addrClient);
while(true)
{
char recvData[255];
int ret=recvfrom(sockServer,recvData,255,0,(sockaddr *)&addrClient,&len);//接收来自客户端的UDP数据包
if (ret>0)
{
recvData[ret]=0x00;
printf("接收到一个连接:%s\r\n",inet_ntoa(addrClient.sin_addr));
printf(recvData);//将接收到的数据打印出来
}
char * sendData="一个来自服务端的UDP数据包\n";//向客户端发送一个消息
sendto(sockServer,sendData,strlen(sendData),0,(sockaddr *)&addrClient,len);
}
closesocket(sockServer);//关闭服务端监听套接字
WSACleanup();
printf("success!\n");//表示正常运行
return 0;
}
客户端代码
//客户端
#include <stdio.h>
#include <winsock2.h>
#pragma comment(lib,"ws2_32.lib")
int main(int argc, char* argv[])
{
WSADATA wsaData;
WSAStartup(MAKEWORD(2,2),&wsaData);
SOCKET sockClient=socket(AF_INET,SOCK_DGRAM,0);
//设置要连接的服务端的地址信息
SOCKADDR_IN addrServer;
addrServer.sin_family=AF_INET;
addrServer.sin_addr.s_addr=inet_addr("127.0.0.1");
addrServer.sin_port=htons(6000);
int tolen=sizeof(addrServer);
char * sendData="一个来自客户端的UDP数据包\n";
sendto(sockClient,sendData,strlen(sendData),0,(sockaddr *)&addrServer,tolen);
char recvData[255];
int ret = recvfrom(sockClient,recvData,255,0,(sockaddr *)&addrServer,&tolen);
if(ret>0)
{
recvData[ret]=0x00;
printf(recvData);
}
closesocket(sockClient);
WSACleanup();
printf("success!\n");
return 0;
}
先运行服务端,然后运行客户端
服务端结果如下
客户端结果如下
1、数据包套接字相较于流式套接字特有的两个函数
recvfrom(SOCKET s,char *buf,int len,int flags,struct sockaddr *from,int *fromlen)
s: 套接字描述符,指定要接收数据的套接字。
buf: 指向存放接收数据的缓冲区。
len: 缓冲区的大小,即最大接收数据的长度。
flags: 接收操作的标志,通常设置为0。
from: 指向一个 struct sockaddr 类型的指针,用于存放发送数据的地址信息。
len: 一个指向整型变量的指针,用于指定 from 缓冲区的大小
sendto(SOCKET s,char *buf,int datalen,int flags,sockaddr *to,int tolen)
s: 套接字描述符,指定要发送数据的套接字。
buf: 指向待发送数据的缓冲区。
datalen: 待发送数据的长度。
flags: 发送操作的标志,通常设置为0。
to: 指向一个sockaddr 类型的指针,包含目标地址信息。
len: to缓冲区的大小
2、数据包套接字有两种模式
对等模式
Socket()-->bind()-->recvfrom()-->sendto()-->close()
Socket()-->bind()-->recvfrom()-->sentdo()-->close()
非对等模式(C/S模式)
Socket()-->bind()-->recvfrom()-->sendto()-->close()
Socket()-->sentdo()-->recvfrom()-->close()
跟流式套接字对比,服务器端少了listen()和accept(),客户端少了connect()
C/S模式的无连接套接字编程模型具有以下特点。
(1)应用程序双方是不对等的,服务器要先行启动,处于被动的等待访问的状态,而客户机则可以随时主动地请求访问服务器。两者在进行网络通信时,服务器要经过创建套接字、绑定套接字、交换数据和关闭套接字4个阶段,而客户机不需要进行套接字的绑定。
(2)服务器进程将套接字绑定到众所周知的端口,或事先指定的端口,并且,客户机端必须确切地知道服务器端套接字使用的网络地址。
(3)客户机端套接字使用动态分配的自由端口,不需要进行绑定,服务器端事先也不必知道客户机端套接字使用的网络地址。
(4)客户机端必须首先发送数据报,并在数据报中携带双方的地址;服务器端收到后,才能知道客户机端的地址,才能给客户机端回送数据报。
(5)服务器可以接收多个客户机端的数据。