一、网络编程的基本概念
1.网络的配置
windows下的ipv4属性配置:
IP
表现形式
192.168.5.47 点分十进制
11101010 101010101 10101010 101010101 二进制
分类:
IPv4: 32位 (2^32 40亿)
IPv6: 128位
IPV4分类:
A类:
0开头,8位网络段,24位主机段
00000000 - 01111111 0 – 126
127为本地回环地址,本机地址(一般用于测试使用)!!!
127.0.0.1—127.255.255.255
B类:
10开头,16位网络段,16主机段
10000000 00000000 - 10111111 11111111 128.0 - 191.255
C类:
110开头,24位网络段.8为主机段
11000000 00000000 00000000 - 11011111 11111111 11111111 192.0.0 - 223.255.255
D类:
1110开头 32位网络段,没有主机段 (组播地址)
11100000 000000000 000000000 00000000 - 11101111 11111111 11111111 11111111 224.0.0.0 - 239.255.255.255
E类:保留
注意:
主机段全为0,网络本身
主机段全为1,广播地址 128.168.5.255
子网掩码
自动生成,主机段全为0,网络全为1:255.255.255.0
(24位网络段,8位主机段)
用处:
(1) &255.255.255.0
192.168.5.23 <-------------------->192.168.5.24
192.168.5.0 192.168.5.0
通信双方都按位与上自己的子网掩码,可以判断,通信双方是否在同一网络段
(2) 255.255.255.248
11111111 11111111 11111111 11111000
网关
192.168.5.1 tracert:跳转IP;
DNS域名解析服务器
域名解析
www.baidu.com<---------->IP
域名 | IP
本地域名解析: C:\Windows\System32\drivers\etc\hosts
hosts文件修改例子:
二、TCP|IP体系架构(每一层为上层提供服务,享受下层服务)
FTP:文件传输协议
TCP:建立连接的通信传输协议,保证数据无错误,无丢失,无重复的稳定安全的通讯协议。
UDP:不建立连接,不保证数据无错误,无丢失,无重复的不稳定不安全的高效的通讯方式。
IP:网络数据传输
TCP/IP通讯协议
一个协议族,所有在TCP/IP体系架构中使用的协议,都属于TCP/IP协议.
抓包解析图
开始:三次握手,结束:四次挥手
SERVER与CLIENT连接的时候:
.1. client向server发送标志位SYN (同步)为1的包,并且进入SYN SEND状态,等待服务器确认
2. server向client 发送标志位SYN, ACK为1的包,表示server的SYN被确认,且服务器进入SYN RECV状态
3. client接收到server的STN+ACK报文后,向server发送发送确认ACK报文, 客户端和服务器进入ESTABLISHED状态, 完成TCP连接
帧的解析
以太网头+IP头+TCP头+FTP头+冗余校验位
以太网头:以太网的类型 + 源MAC + 目标MAC
TP头:源IP + 目标IP + IP类型
TCP头:源端口号 + 目标端口号
FTP头:文件的信息
冗余校验位:检验数据是否错误
UDP
概述:UDP即用户数据包协议,是一种面向无连接的不可靠传输协议,不需要通过三次握手建立一个链接,同时,一个UDP应用可以同时作为应用的客户或者服务器方。(更好的解决实时性)
协议的选择
协议的选择应该考忠到数据的可靠性、应用的实时性和网络的可靠性。
对数据可靠性要求高的应用需选择TCP协议,对数据的可靠性要求不那么高
的应用可选择UDP传送。
TCP协议中的3次握手、重传确认等手段可以保证数据传输的可靠性,但使用
TCP协议会有较大的时延,因此不适合对实时性要求较高的应用:而UDP协
议则有很好的实时性。
网络状况不是很好的情况下需选用TCP协议(如在广域网等情况),网络状况
很好的情况F选择UDP胁议可以减少网络负荷。
二、预备知识
1.socket SOCK_STREAM,SOCK_DGRAM
(1)是一个网络编程接口,是一种特殊的文件描述符,主要位于应用层与传输层之间。
(2)Socket种类:
流式套接字(SOCK_STREAM) --------------->TCP
数据报套接字(SOCK_DGRAM) --------------->UDP
原始套接字(SOCK_RAW)
2.字节序
大端序:数据的高位存在低地址,低位存在高地址
小端序:数据的高位存在高地址,低位存在低地址
测试:(小端序)
#include <stdio.h>
int main()
{
short a = 0x1234;
char *p = (char *)&a;
printf(“p[0] = 0x%x p[1] = 0x%x\n”,p[0],p[1]);
return 0;
}
farsight@ubuntu:/mnt/hgfs/1908网络编程/1_day/code$ ./a.out
p[0] = 0x34 p[1] = 0x12
3.端口号 端口号转换函数:htons(),ntohs()
(1)端口号作用:确定是发给哪一个进程
(2)端口号取值范围1~65535 (16位无符号整型)
1-5000 : 系统调用
5000-65535:随便使用
(3)端口号转换函数:
man 3 htons
#include <arpa/inet.h>
uint16_t htons(uint16_t hostshort);//主机转端口号
uint16_t ntohs(uint16_t netshort);//端口号转主机
这里的h代表host,n代表network
s代表着short,l代表着long
#include <stdio.h>
int main()
{
short a = 0x1234;
char *p = (char *)&a;
printf(“p[0] = 0x%x p[1] = 0x%x\n”,p[0],p[1]);
short b = htons(a);
p = (char *)&b;
printf(“p[0] = 0x%x p[1] = 0x%x\n”,p[0],p[1]);
short c = ntohs(b);
p = (char *)&c;
printf(“p[0] = 0x%x p[1] = 0x%x\n”,p[0],p[1]);
return 0;
}
farsight@ubuntu:/mnt/hgfs/1908网络编程/1_day/code$ ./a.out
p[0] = 0x34 p[1] = 0x12
p[0] = 0x12 p[1] = 0x34
p[0] = 0x34 p[1] = 0x12
4.IP 结构体in_addr,网络字节序函数inet_addr(),inet_ntoa()
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
typedef uint32_t in_addr_t;
struct in_addr {
in_addr_t s_addr;
};
int inet_aton(const char *cp, struct in_addr *inp);
功能:将CP保存的点分十进制IP转化网络字节序的二进制IP保存在inp指向的结构体.
参数:
cp:点分十进制的IP地址
inp:转化之后的得到的IP地址的存储位置
返回值:
成功:非0
失败:0
例:
struct in_addr addr;
int ret = inet_aton(“192.168.5.25”, &addr);
if(ret == 0){
perror(“inet_aton”);
}
in_addr_t inet_addr(const char *cp); //!!!
功能:将CP保存的点分十进制IP转化为网络字节序的二进制IP
参数:
CP:点分十进制的IP地址
返回值:
成功:返回网络字节序的二进制IP
失败:-1 (255.255.255.255)
例:
typedef uint32_t in_addr_t;
struct in_addr {
in_addr_t s_addr;
};
struct in_addr addr;
addr.s_addr = inet_addr(“192.168.5.25”);
char *inet_ntoa(struct in_addr in); //!!!
功能:将网络字节序的二进制IP转化为字符串形式的点分十进制IP
参数:
in:保存网络字节序的二进制IP
返回值:
成功:返回点分十进制的字符串形式IP
例:
struct in_addr addr;
addr.s_addr = “11111111 11111111 11111111 11111111”
char buf[50] = {};
buf = inet_ntoa(addr);
安装ctags插件
1.sudo apt-get update
2.sudo apt-get install ctags
3.cd /usr/include
4.sudo ctags -R
5.vim -t (你要查找的内容)
三、TCP通信 (cs模型)客户端(client)服务器(server)模型
socket():
该函数用于建立-一个套接字,一条通信路线的端点。在建立了socket
之后,可对sockaddr或sockaddr_in结构进行初始化,以保存所建立的socket
地址信息。
int socket(int domain, int type, int protocol);
功能:创建一个通信端点,返回一个文件描述符
参数:
domain:选择本地地址类型
AF_UNIX, Local communication unix(7)
AF_LOCAL
AF_INET IPv4 Internet protocols ------------------->TCP,UDP都选用IPv4
AF_INET6 IPv6 Internet protocols
type:选择套接字种类
流式套接字(SOCK_STREAM) --------------->TCP
数据报套接字(SOCK_DGRAM) --------------->UDP
原始套接字(SOCK_RAW)
protocol:一般为0
返回值:
成功:文件描述符
失败:-1
例:
int fd = socket(AF_INET,SOCK_STREAM,0)
if(fd < 0){
perror("socket");
}
bind():
该函数用于将sockaddr结构的地址信息与套接字进行绑定。它主要用
于TCP的连接,而在UDP的连接中则没有必要(但可以使用)。
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
功能:给socket产生的文件描述符的一些描述信息
参数:
sockfd:文件描述符;
addr:保存,IP,端口号,本地协议的结构体
addrlen:结构体长度
返回值:
成功:0
失败:-1;
例:
struct sockaddr_in server; //IPV4专用结构体
server.sin_family = AF_INET;
server.sin_port = htons(10000);
server.sin_addr.s_addr = inet_addr("192.168.5.20")
int ret = bind(fd,(struct sockaddr *)&server,sizeof(server));
//将IPV4专用结构体装换成通用结构体赋予函数使用
if(ret < 0){
perror("bind");
}
函数中的调用的通用结构体
typedef unsigned short int sa_family_t;
struct sockaddr { //通用结构体
sa_family_t sa_family; //2byte
char sa_data[14]; //14byte
}
#define 16 /* sizeof(struct sockaddr) */
IPV4专用的结构体
struct sockaddr_in { //IPv4专用结构体
__kernel_sa_family_t sin_family; /* 本地协议 */(IPv4/IPv6) 2byte
__be16 sin_port; /* 端口号*/ 2byte
struct in_addr sin_addr; /* IP地址 */ 4byte
unsigned char __pad[8]; 8byte
};
typedef unsigned short __kernel_sa_family_t;
typedef unsigned short __u16;
IPV4转用结构体存储信息后结构体强制类型转化为网络通用结构体来使用:
1.为什么要强制类型转化
因为通用结构体不好赋值
2.为什么可以强制类型转化
通用结构体和专用结构体空间的大小相同
listen():
在服务器端程序成功建立套接字和与地址进行绑定之后,还需要准备
在该套接字上接收新的连接请求。此时调用listen()函数来创建一一个等 待队列,
在其中存放未处理的客户端连接请求。
int listen(int sockfd, int backlog);
功能:将套接字类型转化为被动套接字,等待其他套接字的链接
参数:
sockfd:套接字文件描述符
backlog:可以同时请求链接的个数(一般填10以内)
假如你的fd链接了10个客户端,此时backlog设置为5,这个时候,如果有5个同时连接,就会一个一个处理,如果说有6个同时发起链接,就会报错,或者一会儿重新链接
在ARM板上,最大8个同时发起链接,在ARM板上一般设置为5
返回值:
成功:0
失败:-1
例:
int ret = listen(fd,5);
if(ret < 0){
perror("listen");
}
accept():
服务器端程序调用listen()函数创建等待队列之后,调用ackept0函数
等待并接收客户端的连接请求。它通常从由listen()所创建的等待队列中取出第
一个未处理的连接请求。
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
功能:待有连接后分配,然后建立连接
链接请求队列上的第一个请求,并返回一个新的套接字文件描述符.
参数:
sockfd:套接字文件描述符(旧的)
addr:链接的客户端基本信息结构体
addrlen:结构体长度地址
返回值:
成功:新的文件描述符
失败:-1;
用法1:(关心对方的IP和端口号)
struct sockaddr_in client;
int len = sizeof(client);
int newfd = accept(fd,(struct sockaddr *)&client,&len);
if(newfd < 0){
perror("accept");
}
printf("client port = %d\n",ntohs(client.sin_port));
printf("client IP = %s\n",inet_ntoa(client.sin_addr));
用法2:(不关心对方的IP和端口号)
int newfd = accept(fd,NULL,NULL);
if(newfd < 0){
perror("accept");
}
connect():
客户端通过一一个未命名套接字(未使用bind()函数)和服务器监听
套接字之间建立连接的方法来连接到服务器。该工作客户端通过使用connect()
函数来实现。
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
功能:发起链接,链接addr这个结构体里面的信息(IP,端口号)
参数:
sockfd:文件描述符
addr:你要链接的对象IP,端口号保存的结构体
addrlen:长度
返回值:
成功:0
失败:-1;
send()和recv(): 与write和read类似
这两个函数分别用于发送和接收数据,可以用在TCP中,
也可以用在UDP中。当用在UDP中时,可以在connect()函数建立连接之
后再使用。
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
功能:向文件描述符写/读东西
参数:
sockfd:套接字文件描述符
buf:数据缓冲区
len:写/读的长度(strlen(buf));
flags:一般为0
返回值:
成功:返回写入/读取的字符数
失败:-1;
sendto()和reevfrom():
这两个函数的作用与send0和recv0函数类似,也可以用
在TCP和UDP中。当用在TCP中时,后面的几个与地址有关参数不起作用,
函数作用等同于send0和recv0;当用在UDP中时,可以用在之前没有使用
connect)的情况下,这两个函数可以自动寻找指定地址并进行连接。
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
TCP服务器模型