前言:
在前面的文章里面,给大家举例了一些代码案例,但是代码里面的系统接口,并没有给大家做详细的说明,所有本篇文章会给大家做一个详细的说明!
一、先来看socket()接口:
通过前面的文章,我们现在知道了,在进行网络通信之前,我们会调用了socket()接口来做准备,具体做什么准备呢?那就是向系统申请系统资源,这样我们才可以进行网络编程:
int socket(int domain, int type , int protocol);
同时我们也知道socket是一个具有多功能的通信能力的,比如:
- 网络编程
- 本地的进程间通信
如果我不想要网络编程功能了,想进行本地的进程间通信,那该怎么做呢?那就是调用socket()接口的时候,替换掉传的参数即可。
所以下面就来具体看一下socket接口传的参数含义了:
- domain:套接字中使用的协议族信息,比如说:PF_INET,这个是代表什么意思呢?它代表的意思就是基于IPV4的互联网通信协议族,当然也有IPV6的互联网通信协议族,但是由于它现在还没有普及,所以文章当中主要以IPV4作为重点来进行讲解。下面是常见的协议族汇总,注意不同协议中的地址表现形式可能不同,网络编程时地址类型必须和协议类型匹配,比如说IPV4的ip地址形式是4字节的整型,而IPV6的IP地址就不是这样的了:
-PF_INET:IPv4互联网协议族
-PF_INET6:IPv6互联网协议族
-PF_LOCAL:本地通信的协议族
-PF_PACKET:底层数据收发协议族
-PF_IPX:Novell专用协议(互联网分组交换协议)
- type:套接字数据传输类型信息:
-1、SOCK_STREAM: 流式数据(TCP)
-2、SOCK_UGRAM:报文式数据(UDP)
- protocol:设备间通信使用的协议信息
domain和type几乎可以唯一确定一种协议,因此,这个参数通常为0
,这个0代表的domain和type指定后的默认协议
通过上面的详细介绍,大家现在应该对socket()这个接口非常熟悉了!
二、关于端口号和IP地址:
- 端口号是一个2字节无符号整数数据
- 0 - 1023作为特定端口(包括1024 -2048)被预定义(分配给特定应用程序),这些特定的端口号,不能为我们程序员自由使用,这点要特别注意!
- IP地址是一个4字节地址族(可分为5类地址,这里少画了E类地址,不过后面有补充哈):
1、深入解析IP地址:
- IP地址分为网络标识和主机标识两部分:什么是网络标识呢?标识网络主机设备所在的网络;什么是主机标识?标识网络主机设备的具体地址。那么问题就来了,一个IP地址就4个字节,那么如何区分网络标识和主机标识呢?
我们来看下面的一幅图:
一般来说:
- IP地址和子网掩码配合使用区分网络标识和主机标识
- 子网掩码的表现形式也是一个4字节的整型数
- 子网掩码用于从IP地址中提取网络标识
那我们怎样去提取网络标识呢?一般的做法是通过按位与的操作方式来提取网络标识,比如下面:
这里我们来深入理解一下子网掩码的作用?比如说现在:
假设:子网掩码是M.N.P.Q;则子网可用IP地址n=(256-M)(256-N)(256-P)*(256-Q)
例如现在IP地址为:211.99.34.33;掩码为:255.255.255.248;则可以使用的子网IP地址为8个IP地址,并且211.99.34.33所在的子网地址为211.99.34.32,广播地址为211.99.34.39:
这里稍微注意一下,211.99.34.33~211.99.34.38是我们可以用的哈!
下面我们来看一下一些特殊的IP地址:
- 0.0.0.0:表示保留,常用于代表“缺省网络”,在服务端编程中,如果使用这个地址,表示的是本机中每一个网卡上面连接的数据都要进行处理操作!
- 127.0.0.0:回环地址,常用于本地软件回送测试;
- 255.255.255.255:表示广播地址
然后是一些私有地址:不在公网使用,只在内网中使用:
- 10.0.0.0 ~10.255.255.255
- 172.16.0.0~172.31.255.255
- 192.168.0.0~192.168.255.255
2、地址数据类型解析:
为了讲清楚这个地址类型的知识点,我们先来看下面这副图:
看到这幅图,大家是不是明白了之前写的代码里面有很多接口老是有一个地址转换(把strcut socket addr_in 强制类型转换为struct sockaddr):
int bind(int sock, struct sockaddr *addr , socklen_t addlen);
好了,这里因为我们是以IPV4为重点讲解,所以我们还是看这个结构体:
strcut sockaddr_in
{
sa_family_t sa_family;//具体的哪种地址类型,比如有IPV4地址类型,那么赋值的时候就是AF_INET
u_int16_t sin_port;//指定端口号
struct in_addr sin_addr;//指定具体的地址
}
而结构体struct in_addr具体为:
struct in_addr
{
u_int32_t s_addr;//这个具体的地址,是4字节的无符号整型数据类型
}
那么这里就有一个问题,涉及到字符串地址"192.168.3.2"和整型IP地址的转换问题,这里的主要是通过函数转换来解决这个问题:
#include <arpa/inet.h>
in_addr_t inet_addr(const char * strptr);//将IP字符转串转换为符合网络字节序的整数
int inet_aton(const char* cp, struct in_addr*inp);//将IP字符串转换为符合网络字节序的整数,成功返回1,失败返回0
char * inet_ntoa(struct in_addr in);//将符合网络字节序的整数地址转化为字符串形式
这里有一个小技巧来区分后俩个函数,a表示地址,n表示网络,所以就很好区分了。
下面举例一个实际代码案例:
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet.in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <malloc.h>
int main()
{
unsigned int addr = inet_addr("1.2.3.4");
struct in_addr addr1 = {
0x09080706};
struct in_addr addr2 = {
0x05040302};
char *s1 = strcpy(malloc(32),inet_ntoa(addr1));
char *s2 =strcpy(malloc(32),inet_ntoa(addr2));
printf("addr = %x\n",addr);
printf("addr1 = %s\n",s1);
printf("addr2 = %s\n",s2);
printf("s1 = %s\n",s1);
printf("s2 = %s\n",s2);
if(inet_aton("1.2.3.4"),&addr1))
{
printf("addr1 = %x\n",addr1.s_addr);
}
free(s1);
free(s2);
return 0;
}
运行结果如下:
root@ubuntu:/home/hao/socket# ./a.out
addr = 4030201
addr1 = 6.7.8.9
addr2 = 2.3.4.5
s1=6.7.8.9
s2=2.3.4.5
addr1 = 4030201