UDP网络编程

一、UDP的概念

1.1、UDP——面向无连接(无连接,是因为UDP里有了对方的地址,直接发就好)

特点:

          1、邮件系统服务模式的抽象
          2、每个分组都携带完整的目的地址
          3、不能保证分组的先后顺序
          4、不进行分组出错的恢复和重传
          5、不保证数据传输的可靠性 

1.2、UDP协议

 面向无连接的用户数据报协议,在传输数据前不需要先建立连接;目地主机的运输层收到 UDP 报文后,不需要给出任何确认。

UDP 其它特点
                 1、相比 TCP 速度稍快些
                 2、简单的请求/应答应用程序可以使用 UDP
                 3、对于海量数据传输不应该使用 UDP
                 4、广播和多播应用必须使用 UDP

UDP 应用
                DNS(域名解析)、NFS(网络文件系统)、RTP(流媒体)等

二、网络编程接口socket

2.1、socket 作用

            提供不同主机上的进程之间的通信

2.2、socket 特点

1、socket 也称“套接字”
2、是一种文件描述符,代表了一个通信管道的一个端点
3、类似对文件的操作一样,可以使用 read、write、close 等函数对 socket 套接字进行网络  数据的收取和发送等操作。
4、得到 socket 套接字(描述符)的方法调用 socket()

三、UDP编程C/S架构

四、创建 socket 套接字

头文件:

             #include <sys/types.h>
             #include <sys/socket.h>

函数:

            int socket(int domain, int type, int protocol);
功能:

           创建一个套接字,返回一个文件描述符
参数:
            domain:通信域,协议族
                           AF_UNIX     本地通信
                           AF_INET     ipv4网络协议(常用)
                           AF_INET6   ipv6网络协议
                           AF_PACKET 底层接口

             type:套接字的类型 
                          SOCK_STREAM 流式套接字(TCP)
                          SOCK_DGRAM 数据报套接字(UDP)本章学习UDP
                          SOCK_RAW 原始套接字(用于链路层)
             protocol:附加协议,如果不需要,则设置为0
 返回值:
               成功:文件描述符
               失败:‐1

 特点:

           创建套接字时,系统不会分配端口
           创建的套接字默认属性是主动的,即主动发起服务的请求;当作为服务器时,往往需要修改为被动的。

五、创建UDP套接字

#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <stdlib.h>

 int main(int argc, char const *argv[])
 {
  //使用socket函数创建套接字
  //创建一个用于UDP网络编程的套接字
   int sockfd;
   if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == ‐1)
 {
    perror("fail to socket");
    exit(1);
   }

    printf("sockfd = %d\n", sockfd);

    return 0;
 }

运行结果

sockfd = 3 

六、UDP编程——发送、绑定、接收数据

6.1、发送数据——sendto()函数

头文件

             #include <sys/types.h>
             #include <sys/socket.h>
函数:

            ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
功能:

            发送数据
参数:
                sockfd:文件描述符,socket的返回值
                     buf:要发送的数据
                     len:buf的长度
                  flags:标志位
                               0 阻塞   (基本都设置为0)
                              MSG_DONTWAIT 非阻塞
           dest_addr:目的网络信息结构体(需要自己指定要给谁发送)
               addrlen:dest_addr的长度
返回值:
                   成功:发送的字节数
                   失败:‐1

此函数,重点 :const struct sockaddr *dest_addr   如何构建

通过man手册查询:sockaddr通用结构体

头文件:

                 #include <netinet/in.h>

函数结构体:
                      struct sockaddr
                   {
                       sa_family_t sa_family;   // 2字节
                       char sa_data[14]           //14字节
                    };

 但是,在网络编程中经常使用的结构体sockaddr_in

6.2、sockaddr_in(重点)

头文件:

                #include <netinet/in.h>

 函数结构体:

 
 struct sockaddr_in
    {
        sa_family_t sin_family;            //协议族 2字节
        in_port_t sin_port;                    //端口号 2字节
        struct in_addr sin_addr;       //ip地址 4字节
        char sin_zero[8]                       //填充,不起什么作用 8字节
          };

   struct in_addr
      {
      in_addr_t s_addr;                       //ip地址 4字节
             };

 6.2.1、sockaddr_in使用说明

           在定义源地址和目的地址结构的时候,选用struct sockaddr_in;
  例:
           struct sockaddr_in my_addr;
          当调用编程接口函数,且该函数需要传入地址结构时需要用struct sockaddr进行强制转换
例:
         bind(sockfd,(struct sockaddr*)&my_addr,sizeof(my_addr));

七、向"网络调试助手"发送消息(作为服务器)

        设置网络调试助手(在windows下运行的软件)中的属性


注意:ip地址不能随意设置,必须是当前windows的ip地址

 八、unbantu下客户端发送给windows下运行"网络调试助手"

#include<stdio.h>
#include <sys/types.h>         
#include <sys/socket.h>
#include<stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include<string.h>


#define N 128



int main(int argc, char **argv)
{
	if(argc<3)
  {
	  fprintf(stderr,"%s ip post",argv[0]);
	   exit(1);
	   }

	int sockfd;

	if((sockfd=socket(AF_INET, SOCK_DGRAM,0))==-1)
	{
	   perror("fail to sockfd");
	   exit(1);
		
		}	
     
	printf("sockfd = %d \n",sockfd);
    
	struct sockaddr_in  myaddr;

	myaddr.sin_family=AF_INET;
	myaddr.sin_port =htons(atoi(argv[2]));
	myaddr.sin_addr.s_addr = inet_addr(argv[1]);
    
    socklen_t addrlen=sizeof(myaddr);
    
	char buf[N]="";

	while(1)
	{	
      fgets(buf,N,stdin);

  //    buf[strlen(buf)-1]='\0';   //有这句话,不会换行

      if(sendto(sockfd,buf,N,0,(struct sockaddr*)&myaddr,addrlen)==-1)
	{
		perror("sendto fail");
		exit(1);
		
		}	
  	
	   }
	close(sockfd);
	return 0;
	}

运行结果

 

九、unbantu作为服务器接收数据,网络调试助手作为客户端发送数据 

 9.1、服务器为客户端服务,因此需要有固定的ip和端口号——bind函数绑定

头文件:

            #include <sys/types.h>
            #include <sys/socket.h>
函数:

          int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
功能:

          将套接字与网络信息结构体绑定
参数:
           sockfd:文件描述符,socket的返回值
              addr:网络信息结构体
    struct sockaddr:通用结构体(一般不用
            使用网络信息结构体 sockaddr_in

结构体头文件

               #include <netinet/in.h>
 结构体函数

               struct sockaddr_in
               addrlen:addr的长度
 返回值:
               成功:0
               失败:‐1

 9.2、sockaddr_in使用说明

头文件:

                #include <netinet/in.h>

 函数结构体:

 
 struct sockaddr_in
    {
        sa_family_t sin_family;            //协议族 2字节
        in_port_t sin_port;                    //端口号 2字节
        struct in_addr sin_addr;       //ip地址 4字节
        char sin_zero[8]                       //填充,不起什么作用 8字节
          };

   struct in_addr
      {
      in_addr_t s_addr;                       //ip地址 4字节
             };

 9.3、bind的绑定示例

#include<stdio.h>
#include <sys/types.h>         
#include <sys/socket.h>
#include<stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>


int main(int argc , char **argv[])
{
	
	int sockfd;

    if((sockfd=socket(AF_INET,SOCK_DGRAM,0))==-1)
	{
		perror("fail to sockfd");
		exit(1);
		
		}
     printf("sockfd= %d\n",sockfd);

    //bind的函数	 
	struct sockaddr_in mysockaddr;

	mysockaddr.sin_family=AF_INET;
	mysockaddr.sin_port =htons(8080);
	mysockaddr.sin_addr.s_addr = inet_addr("10.152.203.29");
    
    socklen_t addrlen = sizeof(mysockaddr);

   	if(bind(sockfd,(struct sockaddr *)&mysockaddr,addrlen)==-1)
	{
		perror("fail to bind");
		exit(1);
		
		}
	
	}

补充知识:

                 inet_addr

头文件:

              #include <sys/socket.h>
              #include <netinet/in.h>
              #include <arpa/inet.h>
函数:
             in_addr_t    inet_addr(const char *cp);
 功能:

             将点分十进制ip地址转化为整形数据

              此转换的整形数据也是网络字节序
 参数:
              cp:点分十进制的IP地址
 返回值:
              成功:整形数据
 

 atoi函数(字符串转化成整数)

 头文件

                  #include<stdlib.h>
函数

               int atoi(const char *nptr);
功能
              把字符串转化为int类型
返回

             返回转换后的整型数。

端口号,在我们输入时候,是以字符串形式表达,而我们的结构sockaddr_in 接收的是 整形

因此需要:  atoi(端口号)     变为整形数。 

只是转为整形数,还不行,由于不同计算机之间通讯,需要转换自己的字节序为网络字节序

因此需要: htons(atoi(端口号));

十、接收到数据——recvfrom函数 

头文件

                  #include <sys/types.h>
                  #include <sys/socket.h>
函数
         ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,5 struct sockaddr *src_addr, socklen_t *addrlen);
功能:

            接收数据
参数:

               sockfd:文件描述符,socket的返回值
                    buf:保存接收的数据
                    len:buf的长度
                 flags:标志位
                                        0 阻塞(基本都设置0)
                                        MSG_DONTWAIT 非阻塞
                 src_addr:源的网络信息结构体(自动填充,定义变量传参即可

                 会自动接收对方的ip地址,和端口号,存放此结构体中
                 addrlen:src_addr的长度
 返回值:
               成功:接收的字节数
                失败:‐1

 10.1、recvfrom函数示例

#include<stdio.h>
#include <sys/types.h>         
#include <sys/socket.h>
#include<stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define N 128

int main()
{
 char buf[N]="";

	 struct sockaddr_in recvfrom_addr;
     socklen_t recvfrom_addrlen = sizeof(recvfrom_addr);

     while(1)
{		 
     if(recvfrom(sockfd,buf,N,0,(struct sockaddr *)&recvfrom_addr,&recvfrom_addrlen)==-1)
		{
			
			perror("fail to recvfrom");
			exit(1);
			}

			
        printf("%s\n",buf);	
 }
  return 0;
}

十一、unbantu作为服务端 (代码示例)

#include<stdio.h>
#include <sys/types.h>         
#include <sys/socket.h>
#include<stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>


#define N 128
int main(int argc , char *argv[])
{
    if(argc<3)
	{
	   	
	  fprintf(stderr, "%s ip port ",argv[0]);
	  exit(1);
		
		}	
  

	int sockfd;

    if((sockfd=socket(AF_INET,SOCK_DGRAM,0))==-1)
	{
		perror("fail to sockfd");
		exit(1);
		
		}
     printf("sockfd= %d\n",sockfd);
	 
	struct sockaddr_in mysockaddr;

	mysockaddr.sin_family=AF_INET;
	mysockaddr.sin_port =htons(atoi(argv[2]));
	mysockaddr.sin_addr.s_addr = inet_addr(argv[1]);
    
    socklen_t addrlen = sizeof(mysockaddr);
       
    
   	if(bind(sockfd,(struct sockaddr*)&mysockaddr,addrlen)<0)
	{
		perror("fail to bind");
		exit(1);
		
		}
       
	 
	 char buf[N]="";
	 struct sockaddr_in recvfrom_addr;
     socklen_t recvfrom_addrlen = sizeof(recvfrom_addr);

     while(1)
{		 
     if(recvfrom(sockfd,buf,N,0,(struct sockaddr *)&recvfrom_addr,&recvfrom_addrlen)==-1)
		{
			
			perror("fail to recvfrom");
			exit(1);
			}

			
        printf("%s\n",buf);	
 }
  
	return 0;
	}

 运行结果

 注意:当我们关掉unbantu服务端程序时,如果在运行同一个端口号,会遇到运行错误。需要换一个端口号才可以运行成功。

当我们发送信息时,先把发送的内容写进buf里,然后再传输给对方。当我们再继续发信息时,原先buf里面装的内容不会清除,如果发的信息多,能覆盖之前buf内容,则输出正常。如果发的信息少,不能覆盖原先的buf的内容,只覆盖前面几个数据,后面原先的数据,照常发送给对方。

十二、客户端发信息并能接收服务端回信息(更完善) 

12.1、客户端示例代码

#include<stdio.h>
#include <sys/types.h>         
#include <sys/socket.h>
#include<stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include<string.h>


#define N 128



int main(int argc, char **argv)
{
	if(argc<3)
  {
	  fprintf(stderr,"%s ip post",argv[0]);
	   exit(1);
	   }

	int sockfd;

	if((sockfd=socket(AF_INET, SOCK_DGRAM,0))==-1)
	{
	   perror("fail to sockfd");
	   exit(1);
		
		}	
     
	printf("sockfd = %d \n",sockfd);
    
	struct sockaddr_in  myaddr;

	myaddr.sin_family=AF_INET;
	myaddr.sin_port =htons(atoi(argv[2]));
	myaddr.sin_addr.s_addr = inet_addr(argv[1]);
    
    socklen_t addrlen=sizeof(myaddr);
    
	char buf[N]="";
	

	while(1)
	{
	  char context[50]="";	
      fgets(buf,N,stdin);

  //    buf[strlen(buf)-1]='\0';  

      if(sendto(sockfd,buf,N,0,(struct sockaddr*)&myaddr,addrlen)==-1)
	{
		perror("sendto fail");
		exit(1);
		
		}	
  	  
	 

      if(recvfrom(sockfd,context,sizeof(context),0,(struct sockaddr*)&myaddr,&addrlen)==-1)
	  {
		   perror("fail to recvfrom");
		   exit(1);
		   }
	  
	  printf("from server: %s\n", context);


	   }
	   close(sockfd);
	
	return 0;
	}

12.2、服务端示例代码

#include<stdio.h>
#include <sys/types.h>         
#include <sys/socket.h>
#include<stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include<string.h>

#define N 128
int main(int argc , char *argv[])
{
    if(argc<3)
	{
	   	
	  fprintf(stderr, "%s ip port ",argv[0]);
	  exit(1);
		
		}	
  

	int sockfd;

    if((sockfd=socket(AF_INET,SOCK_DGRAM,0))==-1)
	{
		perror("fail to sockfd");
		exit(1);
		
		}
     printf("sockfd= %d\n",sockfd);
	 
	struct sockaddr_in mysockaddr;

	mysockaddr.sin_family=AF_INET;
	mysockaddr.sin_port =htons(atoi(argv[2]));
	mysockaddr.sin_addr.s_addr = inet_addr(argv[1]);
    
    socklen_t addrlen = sizeof(mysockaddr);
       
    
   	if(bind(sockfd,(struct sockaddr*)&mysockaddr,addrlen)<0)
	{
		perror("fail to bind");
		exit(1);
		
		}
       
	
	 char buf[N]="";
	 struct sockaddr_in recvfrom_addr;
     socklen_t recvfrom_addrlen = sizeof(recvfrom_addr);

     while(1)
{		
         char text[50]=""; 
     if(recvfrom(sockfd,buf,N,0,(struct sockaddr *)&recvfrom_addr,&recvfrom_addrlen)==-1)
		{
			
			perror("fail to recvfrom");
			exit(1);
			}

          printf("ip:%s ,port:%d\n", inet_ntoa(recvfrom_addr.sin_addr), ntohs(recvfrom_addr.sin_port));	
          printf("%s\n",buf);

          strcat(text, " *_*");

         if(sendto(sockfd, text, sizeof(text), 0, (struct sockaddr *)&recvfrom_addr, addrlen) < 0)
           {
            perror("fail to sendto");
                exit(1);
                }
	
 }
    close(sockfd);
  
	return 0;
	}

 运行结果

可以实现多并发 

 

  • 2
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值