两种协议 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 随时和 不同的主机 进行通信所默认的设置,如果需要和相同主机一直通信呢?