UDP网络编程

166 阅读9分钟

目录

1 字节序

如何判断当前字节的字节序

2 字节序转换函数

3 ip地址转换函数

inet_pton //字符串ip地址转整型数据

inet_ntop //整型数据转字符串格式ip地址

inet_addr()和inet_ntoa()

4 UDP概述

5 网络编程接口socket

6 UDP编程C/S架构

7 创建socket套接字

8 ipv4套接字地址结构

9 发送数据-sendto函数

10 向“网络调试助手”发送消息

ubnunt下客户端代码的编写


1 字节序

指多字节数据存储的存储顺序

主机字节顺序HBO: 不同的机器HBO不相同,与CPU设计有关,数据的顺序是由cpu决定的,而与操作系统无关。(大端和小端模式与cpu设计有关而和操作系统无关)

如 Intel x86结构下, short型数0x1234表示为34 12, int型数0x12345678表示为78 56 34 12  
如 IBM power PC结构下, short型数0x1234表示为12 34, int型数0x12345678表示为12 34 56 78


分类
小端格式:将低位字节数据存储在低地址
大端格式:将高位字节数据存储在地址

LSB:低地址 数字小的是低地址 0x01
MSB:高地址 数字大的是高地址 0x04

例如: int i = 0x12345678
12是高字节序(靠近0x)
78是低字节序(远离0x)

如何判断当前字节的字节序

//用来判断当前字节的字节序
#include <stdio.h>

union un
{//公用体
    int a; 
    char b;
};

int main(int argc, char const *argv[])
{
    union un myun;
    myun.a = 0x1234567;
    printf("a = %#x\n", myun.a);
    printf("b = %#x\n", myun.b);

    if(myun.b == 0x78)
    {
        printf("小端存储\n");
    }
    else
    {
        printf("大端存储\n");
    }
    return 0;
}

2 字节序转换函数

特点:
1、网络协议指定了通讯字节序---大端
2、只有在多字节数据处理时才需要考虑字节序
3、运行在同一台计算机上的进程相互通信时,一般不用考虑字节序
4、异构计算机之间通讯,需要转换自己的字节序转化为网络字节序

在需要字节序转换的时候一般调用特定字节序转换函数

host --> network//主机字节顺序转换为网络字节顺序
#include<arpa/inet.h>
1--htonl
2--htons
htonl 是针对32位,4个字节而言
htons 是针对16位,2个字节而言


network -->host//网络字节顺序转换为主机字节顺序
3 --ntohl
4 --ntohs
#include <stdio.h>
#include <arpa/inet.h>
 
int main(int argc, char const *argv[])
{
    int a = 0x12345678;
    short b = 0x1234;

    printf("%#x\n",htonl(a));
    printf("%#x\n",htons(b));
    
    return 0;
}

3 ip地址转换函数

人为识别的时候,ip地址是点分十进制的字符串类型

计算机/网络识别的时候,ip地址是无符号的整型数据

所以需要转化

inet_pton //字符串ip地址转整型数据

#include <stdio.h>
#include <arpa/inet.h>

int main(int argc, char const *argv[])
{
    char ip_str[]="192.138.3.103";
    unsigned int ip_int = 0;
    unsigned char *ip_p = NULL;

    //将点分十进制的ip地址转换位32位无符号整型数据
    inet_pton(AF_INET,ip_str,&ip_int);

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

    ip_p = (char *)&ip_int;
    printf("in_uint = %d,%d,%d,%d\n",*ip_p,*(ip_p+1),*(ip_p+2),*(ip_p+3));
    
    return 0;
}

inet_ntop //整型数据转字符串格式ip地址

#include <stdio.h>
#include <arpa/inet.h>

int main(int argc, char const *argv[])
{
    unsigned char ip_int[] = {192, 168, 3, 103};
    char ip_str[16] = "";
    //将32位无符号整型数据换成点分十进制的ip地址转
    inet_ntop(AF_INET,&ip_int,ip_str,16);

    printf("ip_s = %s\n",ip_str);

    return 0;
}

inet_addr()和inet_ntoa()

inet_addr:将点分十进制的ip地址转换为整型数据,只能用在ipv4地址之间的转化

inet_ntoa:将整型数据转化位点分十进制的ip地址,只能用在ipv4地址之间的转化

4 UDP概述

面向无连接;不建立连接(效率比TCP高);限制传输64k;不可靠协议(比TCP协议简单);输层协议

功能
提供不同主机上的进程通讯

特点
1、发送数据之前不需要建立链接,相对TCP速度稍快些
2、不对数据包的顺序进行检查
3、没有错误检测和重传机制

  • 简单的请求/应答应用程序可以使用UDP
  • 对于海量数据传输不应该使用UDP
  • 广播和多播应用必须使用UDP


服务对象
主要用于“查询-应答”的服务
如:NFS(网络文件系统)、RTP(流媒体)、DNS(域名解析)

一般的语音视频通话都是使用udp来通讯的

5 网络编程接口socket

网络通信要解决的是不同主机进程间的通讯
首要问题是网络进程识别问题
以及多重协议的识别问题

随着UNIX以及类UNIX操作系统的广泛应用,socket成为了最流行的网络程序开发接口

socket作用
提供不同主机上的进程之间的通信
socket特点
socket也称“套接字”
是一个文件描述符,代表了一个通信管道的一个端点
类似对文件的操作一样,可以使用read,wirte,close等函数对socket套接字进行网络数据的收取和发送等操作
得到socket套接字(描述符)的方法,调用socket函数

socket分类
SOCK_STREAM,流式套接字,用于TCP
SOCK_DGRAM,数据报套接字,用于UDP
SOCK_RAW,原始套接字,对于其他层次的协议操作时需要使用这个类型

6 UDP编程C/S架构

7 创建socket套接字

#include <sys/socket.h>

int socket(int domain, int type, int protocol)
功能:创建一个用于网络通信的socket套接字
参数:
  domain:通信域,协议族
    AF_UNIX 本地通信
    AF_INET ipv4网络协议
    AF_INET6 ipv6网络协议
    AF_PACKET 底层接口
 type:socket分类      SOCK_STREAM,流式套接字,用于TCP
       SOCK_DGRAM,数据报套接字,用于UDP
      SOCK_RAW,原始套接字,对于其他层次的协议操作时需要使用这个类型
  protocol:附加协议,如果不需要,则设置于0
返回值:
   成功:文件描述符
   失败:-1
特点:
 在创建套接字的时候,系统不会分配端口
 创建的套接字默认属性是主动,即主动发起服务的请求;当作为服务器时,往往需要修改位被动
#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 tp socket");
        exit(1);
    }

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

8 ipv4套接字地址结构

#include <netinet/in.h>

/* Internet address.  */
typedef uint32_t in_addr_t;
struct in_addr
  {
    in_addr_t s_addr;//ip地址
  };

/* Structure describing an Internet socket address.  */
struct sockaddr_in
  {
    __SOCKADDR_COMMON (sin_);       //协议族        
    in_port_t sin_port;			/* Port number. 端口号 */
    struct in_addr sin_addr;		/* Internet address. IP地址 */

    /* Pad to size of `struct sockaddr'.  */
    unsigned char sin_zero[sizeof (struct sockaddr) -
			   __SOCKADDR_COMMON_SIZE -
			   sizeof (in_port_t) -
			   sizeof (struct in_addr)];
  };

为了使不同格式地址能传入套接字函数,地址必须要强制转换位通用套接字地址结构

9 发送数据-sendto函数

10 向“网络调试助手”发送消息

ps:需要使用在Windows下使用的网络调试助手

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

ubnunt下客户端代码的编写

#include <stdio.h> //printf
#include <stdlib.h>//exit
#include <sys/types.h>
#include <sys/socket.h>//socket
#include <netinet/in.h>//sockaddr_in
#include <arpa/inet.h>//hton inet_addr
#include <unistd.h> //close
#include <string.h>

#define N 512
int main(int argc, char const *argv[])
{
    //第一步,创建套接字
    int sockfd;
    if((sockfd = socket(AF_INET,SOCK_DGRAM,0))==-1)
    { 
        perror("fail tp socket");
        exit(1);
    }
    printf("sockfd = %d\n",sockfd);
    //第二步,填充网络服务器网络信息结构体, sockaddr_in
    struct sockaddr_in serveraddr;
    socklen_t addrlen = sizeof(serveraddr);
    serveraddr.sin_family = AF_INET;//协议族,ipv4
    serveraddr.sin_addr.s_addr = inet_addr("172.31.192.1");//ip地址
    serveraddr.sin_port = htons(8080);//sin_port是uint16,所以用htons
    //第三步,发送数据
    char buf[N] = "";
    while (1)
    {
        fgets(buf,N,stdin);
        buf[strlen(buf)-1] = '\0';//把buf字符串中的\n转换为\0
        if(sendto(sockfd,buf,N,0,(struct sockaddr *)&serveraddr,addrlen) == -1)
        {
            perror("fail tp sendto");
            exit(1);
        }
    }
    
    //第四步,关闭套接字文件描述符
    close(sockfd);

    return 0;
}
  • 修改为比较通用的情况,在gcc后,在终端中输入 ./a.out ip port
#include <stdio.h> //printf
#include <stdlib.h>//exit
#include <sys/types.h>
#include <sys/socket.h>//socket
#include <netinet/in.h>//sockaddr_in
#include <arpa/inet.h>//hton inet_addr
#include <unistd.h> //close
#include <string.h>

#define N 512
int main(int argc, char const *argv[])
{
    if(argc<3)
    {
        fprintf(stderr,"Usage: %s ip port\n",argv[0]);
        exit(1);
    }
    //第一步,创建套接字
    int sockfd;
    if((sockfd = socket(AF_INET,SOCK_DGRAM,0))==-1)
    { 
        perror("fail tp socket");
        exit(1);
    }
    printf("sockfd = %d\n",sockfd);
    //第二步,填充网络服务器网络信息结构体, sockaddr_in
    struct sockaddr_in serveraddr;
    socklen_t addrlen = sizeof(serveraddr);
    serveraddr.sin_family = AF_INET;//协议族,ipv4
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);//ip地址
    serveraddr.sin_port = htons(atoi(argv[2]));//sin_port是uint16,所以用htons
    //第三步,发送数据
    char buf[N] = "";
    while (1)
    {
        fgets(buf,N,stdin);
        buf[strlen(buf)-1] = '\0';//把buf字符串中的\n转换为\0
        if(sendto(sockfd,buf,N,0,(struct sockaddr *)&serveraddr,addrlen) == -1)
        {
            perror("fail tp sendto");
            exit(1);
        }
    }
    
    //第四步,关闭套接字文件描述符
    close(sockfd);

    return 0;
}