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

两种协议 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);

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

代码解释:
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);

上述代码是发送端。

代码解释:
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);
  }
  /* 后方高度一致 */

接收端:

  /* 前方一致, 添加额外的 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,但是
  * 一定要注意,只要有一点信息变动,这个软件就可能无法正常的收发信息了。
  */

代码解释
故而实际上,虽然前方的表格显示,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); // 写完直接关闭通信

接收端:

  /* 此为服务器 */
  /* 首先关闭写流 */
  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 服务器端的需要,后续会记录

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

int shutdown(int sock, int howto);

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

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

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

  • struct sockaddr_in6 : 代表的是 IPv6 的地址信息
  • struct addrinfo : 这是一个通用的结构体,里面可以存储 IPv4 或 IPv6 类型地址的信息
  • getaddrinfo : 这是一个十分方便的接口,在上述 UDP 程序中许多手动填写的部分,都能够省去,有该函数替我们完成

改写一下上方的例子:

接收端:

  int sock; /* 套接字 */
  socklen_t addr_len; /* 发送端的地址长度,用于 recvfrom */
  char mess[15];
  char get_mess[GET_MAX]; /* 后续版本使用 */
  struct sockaddr_in host_v4; /* IPv4 地址 */
  struct sockaddr_in6 host_v6; /* IPv6 地址 */
  struct addrinfo easy_to_use; /* 用于设定要获取的信息以及如何获取信息 */
  struct addrinfo *result;  /* 用于存储得到的信息(需要注意内存泄露) */
  struct addrinfo * p;

  /* 准备信息 */
  memset(&easy_to_use, 0, sizeof easy_to_use);
  easy_to_use.ai_family = AF_UNSPEC; /* 告诉接口,我现在还不知道地址类型 */
  easy_to_use.ai_flags = AI_PASSIVE; /* 告诉接口,稍后“你”帮我填写我没明确指定的信息 */
  easy_to_use.ai_socktype = SOCK_DGRAM; /* UDP 的套接字 */
  /* 其余位都为 0 */

  /* 使用 getaddrinfo 接口 */
  getaddrinfo(NULL, argv[1], &easy_to_use, &result); /* argv[1] 中存放字符串形式的 端口号 */

  /* 创建套接字,此处会产生两种写法,但更保险,可靠的写法是如此 */
  /* 旧式方法
  * 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));
  */

  for(p = result; p != NULL; p = p->ai_next) /* 该语法需要开启 -std=gnu99 标准*/
  {
    sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
    if(sock == -1)
     continue;
    if(bind(sock, p->ai_addr, p->ai_addrlen) == -1)
    {
     close(sock);
     continue;
    }
    break; /* 如果能执行到此,证明建立套接字成功,套接字绑定成功,故不必再尝试。 */
  }

  /* 进入接收信息的状态 */
  //recvfrom(sock, mess, 15, 0, (struct sockaddr *)&send_host, &addr_len);
  switch(p->ai_socktype)
  {
   case AF_INET :
    addr_len = sizeof host_v4;
    recvfrom(sock, mess, 15, 0, (struct sockaddr *)&host_v4, &addr_len);
    break;
    case AF_INET6:
     addr_len = sizeof host_v6
     recvfrom(sock, mess, 15, 0, (struct sockaddr *)&host_v6, &addr_len);
     break;
    default:
     break;
  }
  freeaddrinfo(result); /* 释放这个空间,由getaddrinfo分配的 */
  /* 接收完成,关闭套接字 */
  close(sock);

代码解释:

首先解释几个新的结构体

struct addrinfo 这个结构体的内部顺序对于 *nix 和 Windows 稍有不同,以 *nix 为例

 struct addrinfo{
  int ai_flags;
  int ai_family;
  int ai_socktype;
  int ai_protocol;
  socklen_t ai_addrlen;
  struct sockaddr * ai_addr; /* 存放结果地址的地方 */
  char * ai_canonname; /* 忽略它吧,很长一段时间你无须关注它 */
  struct addrinfo * ai_next; /* 一个域名/IP地址可能解析出多个不同的 IP */
 };

ai_family 如果设定为 AF_UNSPEC 那么在调用 getaddrinfo 时,会自动帮你确定,传入的地址是什么类型的
ai_flags 如果设定为 AI_PASSIVE 那么调用 getaddrinfo 且向其第一个参数传入 NULL 时会自动绑定自身 IP,相当于设定 INADDR_ANY

  • ai_socktype 就是要创建的套接字类型,这个必须明确声明,系统没法预判(日后人工智能说不定呢?)
  • ai_protocol 一般情况下我们设置为 0,含义可以自行查找,例如 MSDN 或者 UNP
  • ai_addr 这里保存着结果,可以通过 调用getaddrinfo之后 的第四个参数获得。
  • ai_addrlen 同上
  • ai_next 同上

getaddrinfo 强大的接口函数

int getaddrinfo(const char * node, const char * service,
                    const struct addrinfo * hints, struct addrinfo ** res);
通俗的说这几个参数的作用
node 便是待获取或者待绑定的 域名 或是 IP,也就是说,这里可以直接填写域名,由操作系统来转换成 IP 信息,或者直接填写IP亦可,是以字符串的形式
service 便是端口号的意思,也是字符串形式
hints 通俗的来说就是告诉接口,我需要你反馈哪些信息给我(第四个参数),并将这些信息填写到第四个参数里。
res 便是保存结果的地方,需要注意的是,这个结果在API内部是动态分配内存了,所以使用完之后需要调用另一个接口(freeaddrinfo)将其释放
实际上对于现代的 套接字编程 而言,多了几个新的存储 IP 信息的结构体,例如 struct sockaddr_in6 和 struct sockaddr_storage 等。

其中,前者是后者的大小上的子集,即一个 struct storage 一定能够装下一个 struct sockaddr_in6,具体(实际上根本看不到有意义的实现)

  struct sockaddr_in6{
   u_int16_t sin6_family;
   u_int16_t sin6_port;
   u_int32_t sin6_flowinfo; /* 暂时忽略它 */
   struct in6_addr sin6_addr; /* IPv6 的地址存放在此结构体中 */
   u_int32_t sin_scope_id; /* 暂时忽略它 */
  };
  struct in6_addr{
   unsigned char s6_addr[16];
  }
  ------------------------------------------------------------
  struct sockaddr_storage{
   sa_family_t ss_family; /* 地址的种类 */
   char __ss_pad1[_SS_PAD1SIZE]; /* 从此处开始,不是实现者几乎是没办法理解 */
   int64_t __ss_align;      /* 从名字上可以看出大概是为了兼容两个不同 IP 类型而做出的妥协 */
   char __ss_pad2[_SS_PAD2SIZE]; /* 隐藏了实际内容,除了 IP 的种类以外,无法直接获取其他的任何信息。 */
   /* 在各个*nix 的具体实现中, 可能有不同的实现,例如 `__ss_pad1` , `__ss_pad2` , 可能合并成一个 `pad` 。 */
  };

在实际中,我们往往不需要为不同的IP类型声明不同的存储类型,直接使用 struct sockaddr_storage 就可以,使用时直接强制转换类型即可

改写上方 接收端 例子中,进入接收信息的状态部分

  /* 首先将多于的变量化简 */
  // - struct sockaddr_in host_v4; /* IPv4 地址 */
  // - struct sockaddr_in6 host_v6; /* IPv6 地址
  struct sockaddr_storage host_ver_any; /* + 任意类型的 IP 地址 */
  ...
  /* 进入接收信息的状态部分 */
  recvfrom(sock, mess, 15, 0, (struct sockaddr *)&host_ver_any, &addr_len); /* 像是又回到了只有 IPv4 的年代*/

补充完整上方对应的 发送端 代码

  int sock;
  const char* mess = "Hello Server!";
  char get_mess[GET_MAX]; /* 后续版本使用 */
  struct sockaddr_storage recv_host; /* - struct sockaddr_in recv_host; */
  struct addrinfo tmp, *result;
  struct addrinfo *p;
  socklen_t addr_len;

  /* 获取对端的信息 */
  memset(&tmp, 0, sizeof tmp);
  tmp.ai_family = AF_UNSPEC;
  tmp.ai_flags = AI_PASSIVE;
  tmp.ai_socktype = SOCK_DGRAM;
  getaddrinfo(argv[1], argv[2], &tmp, &result); /* argv[1] 代表对端的 IP地址, argv[2] 代表对端的 端口号 */

  /* 创建套接字 */
  for(p = result; p != NULL; p = p->ai_next)
  {
   sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol); /* - sock = socket(PF_INET, SOCK_DGRAM, 0); */
   if(sock == -1)
    continue;
   /* 此处少了绑定 bind 函数,因为作为发送端不需要讲对端的信息 绑定 到创建的套接字上。 */
   break; /* 找到就可以退出了,当然也有可能没找到,那么此时 p 的值一定是 NULL */
  }
  if(p == NULL)
  {
   /* 错误处理 */
  }
  /* -// 设定对端信息
  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, p->ai_addr, p->ai_addrlen);
  /* 完成,关闭 */
  freeaddrinfo(result); /* 实际上这个函数应该在使用完 result 的地方就予以调用 */
  close(sock);

到了此处,实际上是开了网络编程的一个初始,解除了现代的 UDP 最简单的用法(甚至还算不上完整的使用),但是确实是进行了交互。
介绍 UDP 并不是因为它简单,而是因为他简洁,也不是因为它不重要,相反他其实很强大。
永远不要小看一个简洁的东西,就像 C语言

ARP 协议

最简便的方法就是找一个有 WireShark 软件或者 tcpdump 的 *nix 平台,前者你可以选择随意监听一个机器,不多时就能看见 ARP 协议的使用,因为它使用的太频繁了。
对于 ARP 协议而言,首先对于一台机器 A,想与 机器B 通信,(假设此时 机器A 的高速缓存区(操作系统一定时间更新一次)中 没有 机器B的缓存),
那么机器A就向广播地址发出 ARP请求,如果 机器B 收到了这个请求,就将自己的信息(IP地址,MAC地址)填入 ARP应答 中,再发送回去就行。
上述中, ARP请求 和 ARP应答 是一种报文形式的信息,是 ARP协议 所附带的实现产品,也是用于两台主机之间进行通信。
这是当 机器A 和 机器B 同处于一个网络的情况下,可以借由本网络段的广播地址 发送请求报文。
对于不同网络段的 机器A 与 机器B 而言,想要通过 ARP协议 获取 MAC地址 ,就需要借助路由器的帮助了,可以想象一下,路由器(可以不止一个)在中间,机器A 和 机器B 分别在这些路由器的两边(即在不同子网)
由于 A 和 B 不在同一个子网内,所以没办法通过通过直接通过广播到达,但是有了路由器,就能进行 ARP代理 的操作,大概就是将路由器当成机器B, A向自己的本地路由器发送 ARP请求
之后路由器判断出是发送给B的ARP请求,又正好 B 在自己的管辖范围之内,就把自己的硬件地址 写入 ARP应答 中发回去,之后再有A向B 的数据,就都是A先发送给路由器,再经由路由器发往B了

ICMP协议

这个协议比较重要。
请求应答报文 和 差错报文 ,重点在于差错报文。
请求应答报文在 ICMP 的应用中可以拿来查询本机的子网掩码之类的信息,大致通过向本子网内的所有主机发送该请求报文(包括自己,实际上就是广播),后接收应答,得到信息
差错报文在后续中会有提到,这里需要科普一二。
首先对于差错报文的一大部分是关于 xxx不可达 的类型,例如主机不可达,端口不可达等等,每次出现错误的时候,ICMP报文总是第一时间返回给对端,(它一次只会出现一份,否则会造成网络风暴),但是对端是否能够接收到,就不是发送端的问题了。
这点上 套接字的类型 有着一定的联系,例如 UDP 在 unconnected 状态下是会忽略 ICMP报文的。而 TCP 因为总是 connected 的,所以对于 ICMP报文能很好的捕捉。
ICMP差错报文中总是带着 出错数据报中的一部分真实数据,用于配对。

(0)

相关推荐

  • 12种实现301网页重定向方法的代码实例(含Web编程语言和Web服务器)

    为什么需要使用301重定向: 1. 保留搜索引擎的排名: 301 重定向是最有效的方法,不会影响到搜索引擎对页面的排名. 2. 保留访客和流量: 如果你将页面链接到大量方法可以访问过的地址,如果不是用重定向的话你就会失去这些用户(不解)原文:If you move your popular page to which a lot of visitors have already linked, you may lose them if you don't used redirect method

  • 12种最常用的网页编程语言简介(值得收藏)

    如今,随着网站的越来越普及,与Web相关的开发技术持续热门,从前端到后端,从标记语言到开发语言,各种技术交相辉映,沉沉浮浮,从开始简单的html到复杂的web开发语言asp.asp.net.php.jsp等等,在此,我就借助SEO马龙博客的平台跟大家简单的介绍一下常见的12种网页编程语言 1.PHP PHP是一个嵌套的缩写名称,是英文"超级文本预处理语言"(PHP:Hypertext Preprocessor)的缩写.PHP是一种HTML内嵌式的语言,与微软的ASP颇有几分相似,都是一

  • Go语言编程入门超级指南

    1.序言 Golang作为一门出身名门望族的编程语言新星,像豆瓣的Redis平台Codis.类Evernote的云笔记leanote等. 1.1 为什么要学习 如果有人说X语言比Y语言好,两方的支持者经常会激烈地争吵.如果你是某种语言老手,你就是那门语言的"传道者",下意识地会保护它.无论承认与否,你都已被困在一个隧道里,你看到的完全是局限的.<肖申克的救赎>对此有很好的注脚: [Red] These walls are funny. First you hate 'em,

  • 浅谈C语言编程中程序的一些基本的编写优化技巧

    大概所有学习C语言的初学者,都被前辈说过,C语言是世界上接近最速的编程语言,当然这并不是吹牛,也并不是贬低其他语言,诚然非C语言能写出高速度的代码,但是C语言更容易写出高速的程序(高速不代表高效),然而再好的工具,在外行人手中也只能是黯淡没落. 对于现代编译器,现代CPU而言,我们要尽量迎合CPU的设计(比如架构和处理指令的方式等),虽然编译器是为程序员服务,并且在尽它最大的能力来优化程序员写出的代码,但是毕竟它还没有脱离电子的范畴,如果我们的代码不能让编译器理解,编译器无法帮我们优化代码,那么

  • Linux下C语言实现C/S模式编程

    由标题可知,这篇文章主要讲如何用C语言实现一个C/S模式的程序. 主要功能:时间回送. 客户机发出请求,服务器响应时间,并返回服务器时间,与客户机进行同步. 废话不多说,下面直接贴出源代码. 代码如下: #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <time.h> #

  • C语言编程中的联合体union入门学习教程

    联合体(union)在C语言中是一个特殊的数据类型,能够存储不同类型的数据在同一个内存位置.可以定义一个联合体使用许多成员,但只有一个部件可以包含在任何时候给定的值.联合体会提供使用相同的存储器位置供多用途的有效方式. 定义联合体 要定义联合体,必须使用union语句很相似于定义结构.联合体声明中定义了一个新的数据类型,程序不止一个成员.联合体声明的格式如下: union [union tag] { member definition; member definition; ... member

  • C语言编程入门之程序头文件的简要解析

    头文件是扩展名为.h的文件,其中包含C函数的声明和宏定义,也可以多个源文件之间共享.有两种类型的头文件:程序员编写的文件,和编译器中附带的文件. 要求使用头文件的程序,包括通过它,使用C语言预处理指令#include就像所看到的包含stdio.h头文件,它随着编译器自带. 包括一个头文件等于复制头文件的内容,但我们不这样做,因为这很容易出错,一个好主意是我们不复制头文件的内容,特别是包括多个程序的源文件. 在C或C++程序的简单做法是,我们把所有的常量,宏全系统全局变量和函数原型在头文件,其中包

  • Python语言的面相对象编程方式初步学习

    词语练习 class:告诉python创造一个新的东西 object:两个意思:最基本的东西和任何实例化的东西. instance:创建一个类得到的东西. def:在类中创建一个函数. self:在类里面的函数中使用,是实例和object能访问的变量. inheritance:继承,一个类可以继承另一个类,像你和你的父母. composition:一个类可以包含另外一个类,就像汽车包含轮胎. attribute:一个属性类,通常包括变量. is-a:表示继承关系 has-a:包含关系 通过卡片记

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

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

  • java实现基于UDP协议网络Socket编程(C/S通信)

    一.前言:认识UDP UDP,全称User Datagram Protocol(用户数据报协议),是Internet 协议集支持一个无连接的传输协议.UDP 为应用程序提供了一种无需建立连接就可以发送封装的 IP 数据包的方法. UDP主要用于不要求分组顺序到达的传输中,分组传输顺序的检查与排序由应用层完成,提供面向报文的简单不可靠信息传送服务.UDP 协议基本上是IP协议与上层协议的接口,适用端口分别运行在同一台设备上的多个应用程序. 二.UDP的特点(与TCP相比) 正是UDP提供不可靠服务

  • java实现基于TCP协议网络socket编程(C/S通信)

    一.前言:TCP原理简介 首先,保证文章完整性,TCP的理论原理还是需要简介一下,略显枯燥๑乛◡乛๑. TCP(传输控制协议,Transmission Control Protocol)是一种面向连接的.可靠的.基于字节流的传输层通信协议.TCP旨在适应支持多网络应用的分层协议层次结构.也就是说,TCP是为了在不可靠的互联网络上提供可靠的端到端字节流而专门设计的一个传输协议. 连接到不同但互连的计算机通信网络的主计算机中的成对进程之间依靠TCP提供可靠的通信服务. 以上TCP的特点,也正是与UD

  • Java基于Tcp协议的socket编程实例

    本文实例讲述了Java基于Tcp协议的socket编程方法,分享给大家供大家参考.具体分析如下: 以下是一对一的通信编程实现,后续会继续学习一个服务器监听多个客户端的实现. 这里用到的主要步骤如下: 第一步:以特定端口(如4800)新建socket对象 第二步:以系统输入设备构造BufferedReader对象,该对象用于接收系统键盘输入的字符 第三步:以socket对象 得到输出流来构造PrintWriter 第四步:以socket对象得到输入流来构造相应的BufferedReader对象,该

  • Linux中使用C语言实现基于UDP协议的Socket通信示例

    linux下udp服务器端源码示例: #include <errno.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdlib.h> #include <string.h> #include <netinet/in.h> #include <stdio.h> #include <un

  • 详解Android 基于TCP和UDP协议的Socket通信

    本来想讲一下基础的网络通信方面的知识点,发现太枯燥乏味了,不过笔试中也经常会问到这方面的问题,所以关于通信方面的知识点,小编会放到面试中去,因为实战中也就面试会用到这方面知识点 Android与服务器的通信方式主要有两种,一是Http通信,一是Socket通信.两者的最大差异在于,http连接使用的是"请求-响应方式",即在请求时建立连接通道,当客户端向服务器发送请求后,服务器端才能向客户端返回数据. 而Socket通信中基于TCP/IP协议的通信则是在双方建立起连接后就可以直接进行数

  • Java编程实现基于TCP协议的Socket聊天室示例

    本文实例讲述了Java编程实现基于TCP协议的Socket聊天室.分享给大家供大家参考,具体如下: 这里使用Socket套接字进行编程,完成的是基于TCP可靠服务实现服务器与客户端的双通信. Server服务器端: package com.han; import java.awt.Container; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.Win

  • Python+Socket实现基于UDP协议的局域网广播功能示例

    本文实例讲述了Python+Socket实现基于UDP协议的局域网广播功能.分享给大家供大家参考,具体如下: 服务器端: # udp_gb_server.py '''服务端(UDP协议局域网广播)''' import socket s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) PORT = 1060 network = '<b

  • Java基于UDP协议实现简单的聊天室程序

    最近比较闲,一直在抽空回顾一些Java方面的技术应用. 今天没什么事做,基于UDP协议,写了一个非常简单的聊天室程序. 现在的工作,很少用到socket,也算是对Java网络编程方面的一个简单回忆. 先看一下效果: 实现的效果可以说是非常非常简单,但还是可以简单的看到一个实现原理.  "聊天室001"的用户,小红和小绿相互聊了两句,"聊天室002"的小黑无人理会,在一旁寂寞着. 看一下代码实现: 1.首先是消息服务器的实现,功能很简单: •将客户端的信息(进入了哪一

  • Java 基于UDP协议实现消息发送

    发短信:不用连接,但需要知道对方的地址,客户端.服务端没有明确的界限,可以说没有客户端.服务端一说. 发送端 package lesson03; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; /** * 发送端 */ public class UdpClientDemo1 { public static void main(String[] args) th

随机推荐