19 datagram

本文详细解析了数据报socket(UDP)与流socket(TCP)的工作原理,介绍了数据报编程的sendto和recvfrom函数,以及通过dgrecv.c和dgsend.c示例展示了如何创建简单的数据报服务器和客户端。重点突出了UDP的无连接特性、数据包的传输和处理方式。
摘要由CSDN通过智能技术生成

1、数据报socket和流socket的不同

socket有两种不同的选择,一种是基于流的socketTCP,它是用来连接不相关的进程的,另一种被称为数据报socketUDP。流socket传送数据就跟电话网中传送声音一样,客户先建立连接,然后使用该连接进行单向、双向或类似管道的字节流传送。数据报通信则与从一个邮箱到另一个邮箱发送包裹类似,客户不必建立连接,只要向特定的地址发送消息,而服务器进程在该地址接收消息。流socket使用的网络协议叫TCP即传输控制协议(Transmission Control Protocol),数据报socket叫UDP即用户数据报协议(User Datagram Protocol)。

流socket对传送负责,数据报socket则不。流socket的接收端检查数据片的顺序来确保数据完整到达,接收端提示发送端丢失的数据片编号,并等待丢失数据片的重传。数据报socket并不检查丢失数据报,也不要求重传,如果丢失则不可达。UDP相比于TCP更快更简单,给网络较少的负荷。

2、数据报编程

数据报编程主要涉及两个系统调用sendto和recvfrom:

  • sendto():从数据报socket发送消息,nchars=sendto(int socket,const void *msg,size_t len,int flags,const struct sockaddr* dest,socklen_t dest_len),socket是socket id标识,msg是发送的字符类型数组,len是发送的字符数,flags是比特的集合设置发送属性0表示普通,dest指向远端socket地址的指针,dest_len远端地址长度,最后返回实际发送的字符数。sendto从源socket发送数据报到目的socket。前3个参数和write参数类似:发送的socket、发送字符串数组、发送字符数,返回实际发送的字符数。flags参数表明发送的各种属性。最后两个参数给出发送目的地的socket地址,如果是Internet类型地址包含目的主机IP和端口号。
  • recvfrom():从数据报socket接收信息,nchars=recvfrom(int socket,const void *msg,size_t len,int flags,const struct sockaddr* sender,socklen_t *sender_len),socket是socket id标识,msg是存放字符类型数组,len是接收的字符数,flags是接收属性的比特的集合0表示普通,sender指向远端socket地址的指针,sender_len远端地址长度,最后返回接收的字符数。recvfrom从socket读取数据报。前3个参数和read参数类似:读取的socket、存放字符串数组、读取的字符数,返回实际接收的字符数。flags参数指出了接收时所用的各种属性。最后两个参数可以获得发送者的socket地址,地址长度必须提供给recvfrom,如果为空,发送者地址不被记录。

3、编写程序代码

dgrecv.c

简单的数据报socket例子的服务器

#include<stdio.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<netdb.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#define oops(m,x) {perror(m);exit(x);}
#define HOSTLEN 256
int make_dgram_server_socket(int portnum)
{
    struct sockaddr_in saddr;
    char hostname[HOSTLEN];
    struct hostent *hp;
    int sock_id;
    sock_id=socket(PF_INET,SOCK_DGRAM,0);
    if(sock_id==-1)
        oops("socket",1);
    bzero((void*)&saddr,sizeof(saddr));
    gethostname(hostname,HOSTLEN);
    hp=gethostbyname(hostname);
    bcopy((void*)hp->h_addr,(void*)&saddr.sin_addr,hp->h_length);
    saddr.sin_port=htons(portnum);
    saddr.sin_family=AF_INET;
    if(bind(sock_id,(struct sockaddr *)&saddr,sizeof(saddr))!=0)
        oops("bind",2);
    return sock_id;
}
void say_who_called(struct sockaddr_in *addrp)
{
    char host[BUFSIZ];
    int port;
    strncpy(host,inet_ntoa(addrp->sin_addr),BUFSIZ);
    port=ntohs(addrp->sin_port);
    printf("from:%s:%d\n",host,port);
}
int main(int argc,char *argv[])
{
    int port;
    int sock;
    char buf[BUFSIZ];
    size_t msglen;
    struct sockaddr_in saddr;
    socklen_t saddrlen=sizeof(saddr);
    if(argc!=2 || (port=atoi(argv[1]))<=0)
    {
        fprintf(stderr,"usage:dgrecv portnum\n");
        exit(1);
    }
    if((sock=make_dgram_server_socket(port))==-1)
        oops("cannot make sock",3);
    while((msglen=recvfrom(sock,buf,BUFSIZ,0,(struct sockaddr *)&saddr,&saddrlen))>0)
    {
        buf[msglen]='\0';
        printf("dgrecv:got a message:%s\n",buf);
        say_who_called(&saddr);
    }
    return 0;
}

dgsend.c

简单的数据报socket例子的客户端

#include<stdio.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<netdb.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#define oops(m,x) {perror(m);exit(x);}
#define HOSTLEN 256
int make_dgram_client_socket(char *host,int portnum,struct sockaddr_in *servadd)
{
    struct hostent *hp;
    int sock_id;
    sock_id=socket(PF_INET,SOCK_DGRAM,0);
    if(sock_id==-1)
        oops("socket",1);
    bzero((void *)servadd,sizeof(struct sockaddr_in));
    hp=gethostbyname(host);
    if(hp==NULL)
        oops(host,2);
    bcopy((void*)hp->h_addr,(void*)&servadd->sin_addr,hp->h_length);
    servadd->sin_port=htons(portnum);
    servadd->sin_family=AF_INET;
    return sock_id;
}
int main(int argc,char *argv[])
{
    int sock;
    char *msg;
    struct sockaddr_in saddr;
    if(argc!=4)
    {
        fprintf(stderr,"usage:dgsend host portnum message\n");
        exit(1);
    }
    if((sock=make_dgram_client_socket(argv[1],atoi(argv[2]),&saddr))==-1)
        oops("cannot make socket",2);
    if(sendto(sock,argv[3],sizeof(argv[3]),0,(const struct sockaddr *)&saddr,sizeof(saddr))==-1)
        oops("sendto",3);
    return 0;
}

dgrecv2.c

带回复的数据报socket例子的服务器

#include<stdio.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<netdb.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#define oops(m,x) {perror(m);exit(x);}
#define HOSTLEN 256
int make_dgram_server_socket(int portnum)
{
    struct sockaddr_in saddr;
    char hostname[HOSTLEN];
    struct hostent *hp;
    int sock_id;
    sock_id=socket(PF_INET,SOCK_DGRAM,0);
    if(sock_id==-1)
        oops("socket",1);
    bzero((void*)&saddr,sizeof(saddr));
    gethostname(hostname,HOSTLEN);
    hp=gethostbyname(hostname);
    bcopy((void*)hp->h_addr,(void*)&saddr.sin_addr,hp->h_length);
    saddr.sin_port=htons(portnum);
    saddr.sin_family=AF_INET;
    if(bind(sock_id,(struct sockaddr *)&saddr,sizeof(saddr))!=0)
        oops("bind",2);
    return sock_id;
}
void say_who_called(struct sockaddr_in *addrp)
{
    char host[BUFSIZ];
    int port;
    strncpy(host,inet_ntoa(addrp->sin_addr),BUFSIZ);
    port=ntohs(addrp->sin_port);
    printf("from:%s:%d\n",host,port);
}
void reply_to_sender(int sock,char *msg,struct sockaddr_in *addrp,socklen_t len)
{
    char reply[BUFSIZ+BUFSIZ];
    sprintf(reply,"Thanks for your %d char message\n",strlen(msg));
    if(sendto(sock,reply,strlen(reply),0,addrp,len)==-1)
        oops("sendto",4);
}
int main(int argc,char *argv[])
{
    int port;
    int sock;
    char buf[BUFSIZ];
    size_t msglen;
    struct sockaddr_in saddr;
    socklen_t saddrlen;
    if(argc!=2 || (port=atoi(argv[1]))<=0)
    {
        fprintf(stderr,"usage:dgrecv portnum\n");
        exit(1);
    }
    if((sock=make_dgram_server_socket(port))==-1)
        oops("cannot make sock",3);
    saddrlen=sizeof(saddr);
    while((msglen=recvfrom(sock,buf,BUFSIZ,0,(struct sockaddr *)&saddr,&saddrlen))>0)
    {
        buf[msglen]='\0';
        printf("dgrecv: got a message: %s\n",buf);
        say_who_called(&saddr);
        reply_to_sender(sock,buf,&saddr,saddrlen);
    }
    return 0;
}

dgsend2.c

带回复的数据报socket例子的客户端

#include<stdio.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<netdb.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#define oops(m,x) {perror(m);exit(x);}
#define HOSTLEN 256
int make_dgram_client_socket(char *host,int portnum,struct sockaddr_in *servadd)
{
    struct hostent *hp;
    int sock_id;
    sock_id=socket(PF_INET,SOCK_DGRAM,0);
    if(sock_id==-1)
        oops("socket",1);
    bzero((void *)servadd,sizeof(struct sockaddr_in));
    hp=gethostbyname(host);
    if(hp==NULL)
        oops(host,2);
    bcopy((void*)hp->h_addr,(void*)&servadd->sin_addr,hp->h_length);
    servadd->sin_port=htons(portnum);
    servadd->sin_family=AF_INET;
    return sock_id;
}
int main(int argc,char *argv[])
{
    int sock;
    char *msg;
    char buf[BUFSIZ];
    size_t msglen;
    struct sockaddr_in saddr;
    if(argc!=4)
    {
        fprintf(stderr,"usage:dgsend host portnum message\n");
        exit(1);
    }
    if((sock=make_dgram_client_socket(argv[1],atoi(argv[2]),&saddr))==-1)
        oops("cannot make socket",2);
    if(sendto(sock,argv[3],sizeof(argv[3]),0,(const struct sockaddr *)&saddr,sizeof(saddr))==-1)
        oops("sendto",3);
    msglen = recvfrom(sock,buf,BUFSIZ,0,NULL,NULL);
	buf[msglen] = '\0';
	printf("dgsend: got a message: %s\n", buf);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值