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

来源:互联网 发布:机械演示制作软件 编辑:程序博客网 时间:2024/06/07 01:58

一、概述
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

原创粉丝点击