数据的网络传输是一个比较复杂的问题,有诸多的复杂性在里面,硬件、软件的差异更是增加了问题的复杂性。所以统一的规则或标准肯定是很重要的。另外一个方面,如何将复杂的问题简单化也是需要考量的,就如同编程的分治法(面向对象的对象或类,面向过程的函数都是分治法思想的体现)一样,所以,在诸多的实践中,有OSI分层的标准,通过分层让问题简单化,在诸多的网络互联协议中,有TCP/IP 协议(名称只是用其中有代表性的两层代表整个四层的诸多协议),TCP/IP协议是将OSI的七层简化为四层,不管是七层还是四层,每层解决各自独立的问题,彼此独立,成立彼此相邻层的输入输出。
应用层:什么样的网络应用(不同的网络应用需要考虑下层,也就是运输层不同的选择)?如http、smtp、ftp等;
运输层:怎样做到可靠传输?怎样做到实时或简单传输?如TCP、UDP等;
网络层:两端地址如何确定?路径如何选择最优?如IP、ARP等;
链路层:实际的比特流如何流动?如Eternet等;
所以,网络应用程序可以用TCP实现,也可以用UDP实现,只是可靠性、实时性和复杂度不一样,视需求面定。
编程要面对复杂的协议,复杂度可想而知,出于简化的目的,从应用层和传输层之间抽象出了socket套接字作为接口层,只要熟悉十几个函数接口即可。
基于UDP(面向无连接)的socket编程的服务器端也叫接收端,对于基于UDP(面向无连接)的套接字编程来说,它的服务器端和客户端这种概念不是很强化,我们也可以把服务器端,即先启动的一端称为接收端,发送数据的一端称为发送端,也称为客户端。
我们先看一下接收端程序的编写:
1 创建套接字(socket)。2 将套接字绑定到一个本地地址和端口上(bind)。3 等待接收数据(recv from)。4 关闭套接字。
代码:
//UDP server
#include
#include
#pragma comment(lib,"ws2_32.lib")
void main()
{
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD( 1, 1);
int err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ){ return; }
if ( LOBYTE( wsaData.wVersion ) != 1 ||HIBYTE( wsaData.wVersion ) != 1 ) {
WSACleanup( ); return; }
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));
char sendBuf[100],recvBuf[100],temp[200];
SOCKADDR_IN addrClent;
int len=sizeof(SOCKADDR);
while(1) {
recvfrom(sockSrv,recvBuf,100,0,(SOCKADDR*)&addrClent,&len);
if('q'==recvBuf[0]) {
sendto(sockSrv,"q",strlen("q")+1,0,(SOCKADDR*)&addrClent,len);
printf("chat end!\n");
break;
}
sprintf(temp,"%s:%s",inet_ntoa(addrClent.sin_addr),recvBuf);
printf("%s\n",temp);
printf("please input data:\n");
gets(sendBuf);
sendto(sockSrv,sendBuf,strlen(sendBuf)+1,0,(SOCKADDR*)&addrClent,len);
}
closesocket(sockSrv);
WSACleanup();
}
要在控制台使用套接字,需要加入头文件 #include 和库函数ws2_32.lib
添加ws2_32.lib:工程设置连接,添加该库(前面要有空格),或//#pragma comment(lib,"ws2_32.lib")
与TCP的socket编程相比,主要有以下不同:
1 建立套接字的参数不同
SSOCKET sockClient=socket(AF_INET,SOCK_DGRAM,0);
// SOCK_STREAM表示TCP连接,SOCK_DGRAM表示UDP连接
2 发送和接收消息的接口
sendto(); //有6个参数,TCP的send()只有4个参数;recvfrom() //有6人参数,TCP的recv()只有4个参数;
//接收端代码
#include
#include
#pragma comment(lib,"ws2_32.lib")
void main()
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 1, 1);
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 )
{
return;
}
if ( LOBYTE( wsaData.wVersion ) != 1 ||
HIBYTE( wsaData.wVersion ) != 1 ) {
WSACleanup( );
return;
}
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);
char sendBuf[100];
char recvBuf[100];
char temp[200];
int len=sizeof(SOCKADDR);
while(1)
{
printf("please input data\n");
gets(sendBuf);
sendto(sockClient,sendBuf,strlen(sendBuf)+1,0,(SOCKADDR*)&addrSrv,
len);
recvfrom(sockClient,recvBuf,100,0,(SOCKADDR*)&addrSrv,
&len);
if('q'==recvBuf[0])
{
sendto(sockClient,"q",strlen("q")+1,0,(SOCKADDR*)&addrSrv,len);
printf("chat end!\n");
break;
}
sprintf(temp,"%s:%s",inet_ntoa(addrSrv.sin_addr),recvBuf);
printf("%s\n",temp);
}
closesocket(sockClient);
WSACleanup();
}
-End-
领取专属 10元无门槛券
私享最新 技术干货