您的位置:首页 > 理论基础 > 计算机网络

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

2017-11-10 16:49 323 查看
一、概述

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[/code] 
三、函数说明

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位I
4000
Pv4地址或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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: