基于UDP的socket套接字的网络编程,客户端/服务端模式

数据的网络传输是一个比较复杂的问题,有诸多的复杂性在里面,硬件、软件的差异更是增加了问题的复杂性。所以统一的规则或标准肯定是很重要的。另外一个方面,如何将复杂的问题简单化也是需要考量的,就如同编程的分治法(面向对象的对象或类,面向过程的函数都是分治法思想的体现)一样,所以,在诸多的实践中,有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-

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20181217A16ZIR00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券