Linux UDP网络编程程序设计-LS12

一、概述
UDP 是 User Datagram Protocol 的简称, 中文名是用户数据报协议,是一个简单的面向数据报的运输层协议,在网络中用于处理数据包,是一种无连接的协议无连接的协议。UDP 不提供可靠性的传输,它只是把应用程序传给 IP 层的数据报发送出去,但是并不能保证它们能到达目的地。由于 UDP 在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快。
UDP 有如下的特点:
1)邮件系统服务模式的抽象(可通过邮件模型来进行对比)
2)每个分组都携带完整的目的地址
3)发送数据之前不需要建立连接
4)不对数据包的顺序进行检查,不能保证分组的先后顺序
5)不进行分组出错的恢复和重传
6)不保证数据传输的可靠性

因此,在网络质量令人十分不满意的环境下,UDP 协议数据包丢失会比较严重。但是由于 UDP 的特性:它不属于连接协议,因而具有资源消耗小,处理速度快的优点,所以通常音频、视频和普通数据在传送时使用 UDP 较多,因为它们即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。比如我们聊天用的 ICQ 和 QQ 就是使用的 UDP

二、UDP 编程的 C/S 架构
这里写图片描述

服务器与客户端
(1)服务器:
UDP网络程序想要收取数据需什么条件?
1)确定的 ip 地址
2)确定的端口(port)

(2)客户端:
UDP 客户端注意点:
1)本地IP、本地端口(我是谁)
2)目的IP、目的端口(发给谁)
3)在客户端的代码中,我们只设置了目的IP、目的端口
4)客户端的本地 ip、本地 port 是我们调用 sendto 的时候 linux 系统底层自动给客户端分配的;分配端口的方式为随机分配,即每次运行系统给的 port 不一样

更详细的介绍请参考一下博文(在此也感谢博主的奉献!)

[1]: http://blog.csdn.net/tennysonsky/article/details/45047133
[2]: http://blog.csdn.net/yueguanghaidao/article/details/7055985

三、函数说明
1 获得文件描述符:执行网络输入输出,一个进程必须做的第一件事就是调用socket函数获得一个文件描述符。
函数名: socket
函数原形:int socket(int family,int type,int protocol)
函数功能:调用socket函数获得一个文件描述符
所属头文件:#include <sys/socket.h>
返回值: 非负描述字:成功; -1:失败
参数说明:
family:协议簇,支持5种协议簇,常用的有AF_INET(IPv4)和AF_INET6(IPv6);
type: 套接口类型, 有三种类型可选:SOCK_STREAM(字节流套接口)、SOCK_DGRAM(数据报套接口)和SOCK_RAW(原始套接口);
protocol: 如果套接口类型不是原始套接口,那么第三个参数就为0。

2 分配一个本地IP和协议端口:为套接口分配一个本地IP和协议端口,对于网际协议,协议地址是32位IPv4地址或128位IPv6地址与16位的TCP或UDP端口号的组合;如指定端口为0,调用bind时内核将选择一个临时端口,如果指定一个通配IP地址,则要等到建立连接后内核才选择一个本地IP地址。
函数名: bind
函数原型:int bind(int sockfd, const struct sockaddr * server, socklen_t addrlen)
函数功
能: 为套接口分配一个本地IP和协议端口
所属头文件: #include <sys/socket.h>
返回值: 接收到数据的长度:成功   -1:失败
参数说明:
sockfd:socket函数返回的套接口描述字;
server:一个指向特定于协议的地址结构的指针
addrlen:地址结构的长度。

3 接收数据:UDP使用recvfrom()函数接收数据。
函数名: recvfrom
函数原ssize_t recvfrom(int sockfd, void buf, size_t len, int flags, struct sockaddr from, size_t *addrlen)能: UDP使用recvfrom ()函数接收数据
所属头文件:#include <sys/types.h> ;#include <sys/socket.h>
返回值 : 接收到数据的长度:成功   -1:失败
参数说明:
sockfd: 要接收套接字的描述符
buf: 接收的数据存在buf指向的空间
len: 希望接收数据的长度(字节数)
flag: 传输控制标志
from: 指明数据将发往的协议地址
addrlen: from指针的大小

4 发送数据:UDP使用sendto()函数发送数据。
函数名: sendto
函数原形: ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr * to, int addrlen)
函数功能: UDP使用sendto()函数发送数据
所属头文件:#include <sys/types.h>;#include <sys/socket.h>
返回值: 发送数据的长度:成功   -1:失败
参数说明:
sockfd: 要发送套接字的描述符
buf: 要发送的数据指针
len: 发送数据长度
flag: 传输控制标志
to: 指明数据将发往的协议地址
addrlen: 指定to指针的大小

四、代码编写:
实现功能:linux系统下编写UDP网络通信程序(客户端:udpclient.c;服务器:udpsercer.c),将两个程序运行在两个进程中,实现两进程间的通信:客户端给服务器发送消息,服务器接收到消息后,按键输入消息发送至客户端。这里会涉及到怎么获取服务器ip的环节,我们可以在终端中输入:ifconfig -a。终端设备会打印本机的ip地址,我们选择回环模式或本地ip都行(自己发给自己),如图所示。
这里写图片描述
服务器端代码:udpserver.c

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

#define PORT 8080  
#define MAXDATASIZE 1024  

main()
{
    char sendbuf[MAXDATASIZE];
    char recvbuf[MAXDATASIZE];
    int len;
    int sockfd;
    struct sockaddr_in server;
    struct sockaddr_in client;
    socklen_t addrlen;
    int num;

    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
    {
        perror("Creatingsocket failed.");
        exit(1);
    }

    bzero(&server, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(PORT);
    server.sin_addr.s_addr = htonl(INADDR_ANY);
    if (bind(sockfd, (struct sockaddr *)&server, sizeof(server)) == -1)
    {
        perror("Bind()error.");
        exit(1);
    }
    addrlen = sizeof(client);
    while (1)
    {
        num = recvfrom(sockfd, recvbuf, MAXDATASIZE, 0, (struct sockaddr*)&client, &addrlen);
        if (num < 0)
        {
            perror("recvfrom() error\n");
            exit(1);
        }
        recvbuf[num] = '\0';
        printf("recvmsg(client): %s-ip:%s-port:%d.\n", recvbuf, inet_ntoa(client.sin_addr), htons(client.sin_port));
        printf("send msg(server):");        
        scanf("%s",sendbuf);
        sendto(sockfd, sendbuf, sizeof(sendbuf), 0, (struct sockaddr *)&client, addrlen);
        if (!strcmp(recvbuf, "bye"))
            break;
    }
    close(sockfd);
}

客户端代码:udpclient.c

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

#define PORT 8080           //设置端口号
#define MAXDATASIZE 1024    //最大接受/发送数据容量

int main(int argc, char *argv[])
{
    char recvbuf[MAXDATASIZE]="my name is zhou xiang ping";
    char sendbuf[MAXDATASIZE];
    char ipaddr[] = "127.0.0.1";//回环调试模式
    //char ipaddr[] = "127.0.0.1";//正常工作模式:ip更改为目标服务器的ip
    int sockfd, recv_num;
    struct hostent *target;     //目标设备ip地址
    struct sockaddr_in server, client;//创建ipv4协议的网络

    target = gethostbyname(ipaddr);
    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)// 创建套接字描述符
    {
        printf("socket() error\n");
        exit(1);
    }

    bzero(&server, sizeof(server)); // 清空结构体内容
    server.sin_family = AF_INET;    //ipv4
    server.sin_port = htons(PORT);  //端口转换
    server.sin_addr = *((struct in_addr *)target->h_addr);  //绑定网卡所用的ip地址
    sendto(sockfd, sendbuf, strlen(sendbuf), 0, (struct sockaddr *)&server, sizeof(server));//发送数据
    socklen_t  addrlen;
    addrlen = sizeof(server);
    while (1)
    {
        /*接收错误*/
        if ((recv_num = recvfrom(sockfd, recvbuf, MAXDATASIZE, 0, (struct sockaddr *)&target, &addrlen)) == -1)
        {
            printf("recvfrom() error\n");
            exit(1);
        }
        /*从其他服务器端接收到数据*/
        if (addrlen != sizeof(server) || memcmp((const void *)&server, (const void *)&target, addrlen) != 0)
        {
            printf("Receive message from otherserver.\n");
            continue;
        }
        /*在接收数据串尾端加上空格*/
        recvbuf[recv_num] = '\0';
        printf("recv msg(server):%s\n", recvbuf);
        /* 发送数据*/
        printf("send msg(client):");
        scanf("%s",sendbuf);
        sendto(sockfd, sendbuf, strlen(sendbuf), 0, (struct sockaddr *)&server, sizeof(server));//发送数据

        if (!strcmp(recvbuf, "bye"))
            break;
    }
    close(sockfd);
}

通信测试图片如图所示:这里写图片描述

如果你能在该模式下顺利完成通信,便可以进行与其他设备的UDP通信的:需要在客户端代码中修改目标端(服务器)的ip地址。
**Problem:本方案设计的程序在发送与接收时的recvfrom与scanf函数都会对进程运行造成阻塞,所以整个通信类似于单工模式,只能完成一发一收,一收一发。
改进方案:在程序设计中进行多线程编程,将接收端与发送端分别放在两个线程里进行!!**

多线程下的udp客户端与服务器的具体代码请查看链接下载:
http://download.csdn.net/download/zxp121127/10114245

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值