• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号
您的位置:首页 > 程序设计 >C语言 > 解析C语言基于UDP协议进行Socket编程的要点

解析C语言基于UDP协议进行Socket编程的要点

作者: 字体:[增加 减小] 来源:互联网 时间:2017-05-28

通过本文主要向大家介绍了c socket udp,linux c socket udp,socket udp,socket udp编程,linux udp socket编程等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

两种协议 TCP 和 UDP
前者可以理解为有保证的连接,后者是追求快速的连接。
当然最后一点有些 太过绝对 ,但是现在不需熬考虑太多,因为初入套接字编程,一切从简。
稍微试想便能够大致理解, TCP 追求的是可靠的传输数据, UDP 追求的则是快速的传输数据。
前者有繁琐的连接过程,后者则是根本不建立可靠连接(不是绝对),只是将数据发送而不考虑是否到达。
以下例子以 *nix 平台的便准为例,因为 Windows平台需要考虑额外的加载问题,稍作添加就能在 Windows 平台上运行UDP。

UDP

这是一个十分简洁的连接方式,假设有两台主机进行通信,一台只发送,一台只接收。
接收端:

  int sock; /* 套接字 */
  socklen_t addr_len; /* 发送端的地址长度,用于 recvfrom */
  char mess[15];
  char get_mess[GET_MAX]; /* 后续版本使用 */
  struct sockaddr_in recv_host, send_host;

  /* 创建套接字 */
  sock = socket(PF_INET, SOCK_DGRAM, 0);

  /* 把IP 和 端口号信息绑定在套接字上 */
  memset(&recv_host, 0, sizeof(recv_host));
  recv_host.sin_family = AF_INET;
  recv_host.sin_addr.s_addr = htonl(INADDR_ANY);/* 接收任意的IP */
  recv_host.sin_port = htons(6000); /* 使用6000 端口号 */
  bind(sock, (struct sockaddr *)&recv_host, sizeof(recv_host));

  /* 进入接收信息的状态 */
  recvfrom(sock, mess, 15, 0, (struct sockaddr *)&send_host, &addr_len);

  /* 接收完成,关闭套接字 */
  close(sock);

</div>

上述代码省略了许多必要的 错误检查 ,在实际编写时要添加

代码解释:
PF_INET 代表协议的类型,此处代表 IPv4 网络协议族, 同样 PF_INET6 代表 IPv6 网络协议族,这个范围在后方单独记录,不与IPv4混在一起(并不意味着更复杂,实际上更简便)。
AF_INET 代表地址的类型,此处代表 IPv4 网络协议使用的地址族, 同样有 AF_INET6 (在操作系统实现中 PF_INET 和 AF_INET 的值一样,但是还是要写宏更好,而不应该直接用数字或者,混淆使用)
htonl 和 htons 两个函数的使用涉及到 大端小端问题, 这里不叙述,需要记住的是在网络编程时一定要使用这种函数将必要信息转为 大端表示法 。
(struct sockaddr *) 这个强制转换是为了参数的必须,但不会出错,因为 sizeof(struct sockaddr_in) == sizeof(struct sockaddr) 具体可以查询相关信息,之所以这么做是为了方便编写套接字程序的程序员。
发送端:

  int sock;
  const char* mess = "Hello Server!";
  char get_mess[GET_MAX]; /* 后续版本使用 */
  struct sockaddr_in recv_host;
  socklen_t addr_len;
  /* 创建套接字 */
  sock = socket(PF_INET, SOCK_DGRAM, 0);
  /* 绑定 */
  memset(&recv_host, 0, sizeof(recv_host));
  recv_host.sin_family = AF_INET;
  recv_host.sin_addr.s_addr = inet_addr("127.0.0.1");
  recv_host.sin_port = htons(6000);
  /* 发送信息 */
  /* 在此处,发送端的IP地址和端口号等各类信息,随着这个函数的调用,自动绑定在了套接字上 */
  sendto(sock, mess, strlen(mess), 0, (struct sockaddr *)&recv_host, sizeof(recv_host));
  /* 完成,关闭 */
  close(sock);
</div>

上述代码是发送端。

代码解释:
inet_addr 函数是用于将字符串格式的 IP地址 转换为 大端表示法的 地址类型,即 s_addr 的类型 in_addr_t
与之相反,同样也有功能相反的函数 inet_ntoa 用于将 in_addr_t 类型转为字符串,但是使用时一定要记住及时拷贝返回值 char addr[16]; recv_host.sin_addr.s_addr = inet_addr("127.0.0.1"); strcpy(addr, inet_ntoa(recv_host.sin_addr.s_addr));
从上述代码看出, UDP 协议的使用十分简洁,几乎就是 创建套接字->准备数据->装备套接字->发送/接收->结束
其中,都没有连接的操作,但是实际上这是为了方便 UDP 随时和 不同的主机 进行通信所默认的设置,如果需要和相同主机一直通信呢?
此中的原由暂时不需要知道,记录方法,即长时间使用 UDP 和同一主机通信时,可以使用 connect 函数来进行优化自身。此时 假设两台主机的实际功能一致,既接收也发送
发送端:

  /* 前方高度一致,将 bind函数替换为 */
  connect(sock, (struct sockaddr *)&recv_host, sizeof(recv_host); // 将对方的 IP地址和 端口号信息 注册进UDP的套接字中)
  while(1) /* 循环的发送和接收信息 */
  {
   size_t read_len = 0;
   /* 原先使用的 sendto 函数,先择改为使用 write 函数, Windows平台为 send 函数 */
   write(sock, mess, strlen(mess));      /* send(sock, mess, strlen(mess), 0) FOR Windows Platform */
   read_len = read(sock, get_mess, GET_MAX-1); /* recv(sock, mess, strlen(mess)-1, 0) FOR Windows Platform */
   get_mess[read_len-1] = '\0';
   printf("In Client like Host Recvive From Other Host : %s\n", get_mess);
  }
  /* 后方高度一致 */
</div>

接收端:

  /* 前方一致, 添加额外的 struct sockaddr_in send_host; 并添加循环,构造收发的现象*/
    while(1)
  {
   size_t read_len = 0;
   char sent_mess[15] = "Hello Sender!"; /* 用于发送的信息 */
   sendto(sock, mess, strlen(sent_mess), 0, (struct sockaddr *)&recv_host, sizeof(recv_host));
   read_len = recvfrom(sock, mess, 15, 0, (struct sockaddr *)&send_host, &addr_len)
   mess[read_len-1] = '\0';
   printf("In Sever like Host Recvive From other Host : %s\n", mess);
  }
  /* 后方高度一致 */
  /*
  * 之所以只在接收端使用 connect 的原因,便在于我们模拟的是 客户端-服务器 的模型,而服务器的各项信息是不会随意变更的
  * 但是 客户端就不同了,可能由于 ISP(Internet Server Provider) 的原因,你的IP地址不可能总是固定的,所以只能
  * 保证 在客户端 部分注册了 服务器 的各类信息,而不能在 服务器端 注册 客户端 的信息。
  * 当然也有例外,例如你就想这个软件作为私密软件,仅供两个人使用, 且你有固定的 IP地址,那么你可以两边都connect,但是
  * 一定要注意,只要有一点信息变动,这个软件就可能无法正常的收发信息了。
  */
</div>

代码解释
故而实际上,虽然前方的表格显示,UDP 似乎并没有 connect 的使用必要,但是实际上还是有用到的地方。
就 *nix 的 API 来说,sendto 和 write 的区别十分明显,便是一个需要在参数中提供目标主机的各类信息,而后者则不需要提供。同样的道理recvfrom和read也是如此。
这个代码只是做演示而已,所以将代码置于无限循环当中,现实中可以自行定义出口条件。
以上是 UDP 的一些简单说明,入门足矣,并未详细叙述某些 函数 的具体用法,而是用实际例子来体现。 在 记录 TCP 之前,还是需要讲一个函数 shutdown
shutdown 与 close(closesocket)

首先要知道,网络通信一般而言是双方的共同进行的,换而言之就是双向的,一个方向只用来发送消息,一个方向只用来读取消息。
这就导致了,在结束套接字通信的时候,需要关闭两个方向的通道(暂时叫它们通道),那同时关闭不行吗?可以啊
close(sock); // closesocket(sock); FOR Windows PlatForm 就是这么干的,同时断开两个方向的连接。
简单的通信程序或者单向通信程序这么做的确无甚大碍,但是万一在结束通信的时候需要接收最后一个信息那该怎么办?
假设通信结束,客户端向服务器发送 "Thank you"
服务器需要接收这个信息,之后才能关闭通信
问题就在这之间,服务器并不知道客户端会在通信结束后的什么时刻传来信息
所以我们选择在通信完成后先关闭 服务器的 发送通道(写流),等待客户端发来消息后,关闭剩下的 接收通道(读流)
发送端:

  /* 假设有一个 TCP 的连接,此为客户端 */
  write(sock, "Thank you", 10);
  close(sock); // 写完直接关闭通信
</div>

接收端:

  /* 此为服务器 */
  /* 首先关闭写流 */
  shutdown(sock_c, SHUT_WR);
  read(sock_c, get_mess, GET_MAX);
  printf("Message : %s\n", get_mess);
  close(sock_c);
  close(sock_s); // 关闭两个套接字是因为 TCP 服务器端的需要,后续会记录
</div>

代码解释
shutdown 函数的作用就是 可选择的关闭那个方向的输出

int shutdown(int sock, int howto);
</div>

sock 代表要操作的套接字
howto有几个选择

  • * nix ** : SHUT_RD SHUT_WR SHUT_RDWR
  • Windows : SD_RECEIVE SD_SEND SD_BOTH


下面,有几个结构体,以及一个接口十分重要及常用:

  • struct sockaddr_in6 : 代表的是 IPv6 的地址信息
  • struct addrinfo
分享到:QQ空间新浪微博腾讯微博微信百度贴吧QQ好友复制网址打印

您可能想查找下面的文章:

  • Linux中使用C语言实现基于UDP协议的Socket通信示例
  • 解析C语言基于UDP协议进行Socket编程的要点
  • Linux网络编程之UDP Socket程序示例

相关文章

  • 2017-05-28深入剖析Android中init进程实现的C语言源码
  • 2022-04-30C语言加减乘除运算
  • 2017-05-28C语言求连续最大子数组和的方法
  • 2017-05-28C++设置系统时间及系统时间网络更新的方法
  • 2017-05-28C语言实现奇数阶魔方阵的方法
  • 2017-05-28c++动态内存空间示例(自定义空间类型大小和空间长度)
  • 2017-05-28C++中的类型转换static_cast、dynamic_cast、const_cast和reinterpret_cast总结
  • 2017-05-28虚函数与纯虚函数(C++与Java虚函数的区别)的深入分析
  • 2017-05-28利用C++实现矩阵的相加/相称/转置/求鞍点
  • 2017-05-28引用numpy出错详解及解决方法

文章分类

  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号

最近更新的内容

    • C语言+win32api写窗体应用程序
    • C++求逆序对的方法
    • VC下实现fopen支持中文的方法
    • 深入遍历二叉树的各种操作详解(非递归遍历)
    • 使用C语言实现最小生成树求解的简单方法
    • C++的sstream标准库详细介绍
    • 利用C语言实现顺序表的实例操作
    • C语言栈的表示与实现实例详解
    • C语言线性表的顺序表示与实现实例详解
    • C语言中字符串和数字的相互转换实现代码

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有