linux网络编程
A: osi七层:
应用层 用
表示层 户
会话层 态
********************************
传输层 内
网络层 核
数据链路层 态
物理层
a1: 传输层协议:
tcp(传输控制协议):可靠的,面向连接的(连接,通信,断开连接)
** tcp连接(三次握手连接)
tcp四次握手连接
udp(用户数据报协议):面向无连接,不安全
端口号(默认80):找到接收数据的地址
IP:找到接收数据的机器
a2: 网络层协议:ip(网际协议)
a3: 数据链路层标准:以太网
B: 主机字节序和网络字节序:
低位存放低地址,小端对齐(主机字节序)
低位存放高地址,大端对齐(网络字节序),使用htonl,htons,ntohs,ntohl函数,h:host to:change n:network s:short l:long
C: IP地址的转换:
字符串类型:192.168.2.123
数值类型:
in_addr_t inet_addr(const char *cp);
功能:将字符串类型ip地址转换为数值型ip地址,并且转换字节序
参数:cp----字符串类型ip地址
返回值:数值型ip地址
in_addr_t inet_network(const char *cp);
功能、参数、返回值同上,不同的是未做字节序转换
char *inet_ntoa(struct in_addr in);
功能:将数值类型ip地址转为字符串型ip地址
参数:in
struct in_addr
{
in_addr_t s_addr;
};
D: 文件描述符:
open close read write //linux提供的系统调用
分配原则:当前系统最小,并空闲的
ssize_t read(int fd, void *buf, size_t count);
功能:从文件读取内容
参数:fd----文件描述符
buf----存放读取数据的缓冲区
count----缓冲区的大小
返回值:读取的字节数 0----读取结束 -1----失败
ssize_t write(int fd, const void *buf, size_t count);
功能:往文件写入数据
参数:fd----文件描述符
buf----写入数据的缓冲区
count----缓冲区的大小
返回值:写入数据的字节数 -1-----失败
E: 被动端/服务端函数:
e1: int socket(int domain, int type, int protocol);
功能:创建网络版的文件描述符(套接字)
参数:domain-----AF_INET(ipv4协议族)
type-----套接字的类型 SOCK_STREAM=面向连接(tcp协议) SOCK_DGRAM=面向无连接(udp协议)
protocol:协议 0---根据协议族和套接字类型自动填写协议
或者:IPPROTO_TCP IPPROTO_UDP
返回值:套接字 -1-----失败
e2: int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
功能:将IP地址和端口号与套接字绑定
接收者:只能接收绑定地址为目的地址,绑定端口号为目的端口号的报文
发送者:设置源ip地址和源端口号
参数:sockfd----套接字
addr:
struct sockaddr_in {
short sin_family; //协议族:AF_INET
short sin_port; //端口号
short in_addr sin_addr; //ip地址
}
addrlen------地址结构体的长度
返回值:0---成功 -1----失败
e3: int listen(int sockfd, int backlog);
功能:将套接字的状态修改为被动态,并建议设置内核中的完成连接队列的长度
参数:sockfd----套接字
backlog----建议值
返回值:0---成功 -1----失败
e4: int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
功能:阻塞函数,从内核的连接完成队列中取出一个连接状态
参数:sockfd----套接字
addr----出参,带回主动端的ip地址和端口号
addrlen------地址结构体的长度
返回值:用于通信的套接字 -1----失败
F: 主动端/客户端函数:
f1: int socket(int domain, int type, int protocol);
f2: int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
功能:发起三次握手连接
参数:sockfd----套接字
addr---被动端的ip地址和端口号
addrlen----地址结构体长度
返回值:0---成功 -1----失败
注意:
当read读取tcp数据时返回值为0表示写端/发送端关闭
当接收端关闭,发送数据端将被信号SIGPIPE杀死
H: 接收数据(属性都默认是0):
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
I: 线程操作时注意:
临界资源(多个进程(共享内存)或线程访问的资源(局部变量、全局变量))和互斥(在同一时间只有并仅有一个进程或线程可以访问临界资源)
J: 多路IO:
j1: select监听的步骤:
1.将监听的文件描述符添加到文件描述符集合 中
eg:fd_set rdfds;
FD_ZERO(&rdfds);
FD_SET(0,&rdfds);
FD_SET(confd,&rdfds);
2.调用select监听活跃的文件描述符
3.判断监听的文件描述符是否活跃
eg:FD_ISSET(0,&rdfds);
int select(int nfds,
fd_set *readfds,
fd_set *writefds,
fd_set *exceptfds,
struct timeval *timeout);
功能:监听多个I/O端口(文件描述符),采用轮询的方式监听文件描述符,所以随着监听的文件描述符的增多而使效率降低
参数:nfds---监听文件描述符中的最大值+1
readfds、writefds、exceptfds(执行的文件描述符)---监听的文件描述符的集合。这三个文集描述符既是入参(监听的文件描述符集合),也是出参(带回活跃的文件描述符集合)
timeout----监听超时的时间
返回值:监听文件描述符集合中活跃的文件描述符的个数 -1-----失败
操作文件描述符集合:
将文件描述符fd从文件描述符集合set中删除:
void FD_CLR(int fd, fd_set *set);
判断文件描述符fd在文件描述符集合set中:
int FD_ISSET(int fd, fd_set *set);
1----存在 0------不存在
将文件描述符fd添加到文件描述符集合set中:
void FD_SET(int fd, fd_set *set);
将文件描述符集合set清空:
void FD_ZERO(fd_set *set);
j2: poll()工作模式和select相同---轮询监听:
int poll(struct pollfd fds[],
nfds_t nfds,
int timeout);
参数:fds---监听的文件描述符的数组
struct pollfd {
int fd; /*监听的文件描述符*/
short events; /*感兴趣的事件*/
short revents; /*活跃的事件*/
};
nfds---数组的个数
timeout---超时时间,不超时就是 -1
返回值:监听文件描述符集合中活跃的文件描述符的个数 -1---- -失败
j3: epoll():
工作模式:只关心活跃的文件描述符,所以它不会随着监听文件描述符的增多而效率下降
int epoll_create(int size);
功能:创建epoll文件描述符
参数:size----epoll通知内核epoll最多监听的文件描述符的个数
返回值:返回文件描述符 -1-----失败
int epoll_ctl(int epfd,
int op,
int fd,
struct epoll_event *event);
功能:epoll操作监听的文件描述符
参数:epfd----epoll文件描述符
op----操作:EPOLL_CTL_ADD(将文件描述符添加到epoll中)
EPOLL_CTL_MOD(修改在epoll监听的文件描述符的事件)
EPOLL_CTL_DEL(将文件描述符从epoll中删除)
fd----要操作的文件描述符
event:
struct epoll_event {
__uint32_t events; /*感兴趣的事件*/
epoll_data_t data; /*私有数据*/
};
typedef union epoll_data {
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;
返回值:0----成功 -1-----失败
int epoll_wait(int epfd,
struct epoll_event *events,
int maxevents,
int timeout);
功能:监听文件描述符,并带回活跃的文件描述符
参数:epfd----epoll文件描述符
events------出参,带回多个活跃的文件描述符的数组
maxevents---最多可以带回多少个活跃的文件描述符
timeout-----超时时间
返回值:活跃的文件描述符的个数 -1---失败 0----超时
K: 处理“addr already in use”问题:
setsockopt(sock_fd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(int));
L: UDP的函数:
l1: int socket(AF_INET, SOCK_DGRAM, int 0);
l2: ssize_t sendto(int sockfd,
const void *buf,
size_t len,
int flags,
const struct sockaddr *dest_addr,
socklen_t addrlen);
功能:发送udp数据
参数:sockfd----套接字
buf------发送数据
len-----数据的长度
flags-----0--->默认属性
dest_addr:目的端的ip地址和端口号
addrlen:地址结构体长度
返回值:发送的字节数 -1---失败
l3: ssize_t recvfrom(int sockfd,
void *buf,
size_t len,
int flags,
struct sockaddr *src_addr,
socklen_t *addrlen);
功能:接收数据
参数:sockfd----套接字
buf----接收数据的缓冲区
len----缓冲区的长度
flags----0-->默认属性
src_addr----出参,带回发送端的ip地址和端口号
addrlen:地址结构体的长度
返回值:收到的信息 -1---失败
注意:
在udp中只要调用recvfrom,就必须设置源端口号(bind)
M: udp广播:
打开广播功能:
setsockopt(sockfd,
SOL_SOCKET,
SO_BROADCAST,
&on,
sizeof(int)); 192.168.2.255
N: 组播(多播):
组播ip地址:224.0.0.0~239.255.255.255
特殊的组播地址:
224.0.0.1:所有具有组播功能的节点(主机 路由器)
224.0.0.2:所有具有组播功能的路由器
支持组播功能的命令:
route add -net 224.0.0.0 netmask 240.0.0.0 dev em1(em1---连接网卡的名称)
将本机IP地址添加到组播地址中:
setsockopt(sockfd,
IPPROTO_IP,
IP_ADD_MEMBERSHIP,
&mreq,
sizeof(struct ip_mreq));
struct ip_mreq
{
struct in_addr imr_multiaddr; //组播地址
struct in_addr imr_interface; //本机地址
};
O: 关闭防火墙:
# service iptables stop //临时关闭防火墙
#chkconfig iptables off
#vim /etc/selinux/config 修改SELINUX = disabled
P: 登录:
char *getpass(const chat *prompt);
功能:接收密码
参数:prompt-----提示信息
返回值:接收的密码
struct spwd *getspnam(const char *name);
功能:根据用户名name返回/etc/shadow文件的内容
char *crypt(const char *key, const char *salt);
功能:将key加密
参数:key----明码
salt-----参照
返回值:加密后的密码
Q: ftp功能:
1.登录(服务器端的系统用户名称和密码)
2.pwd:显示服务器端当前的操作目录
用户登录成功后的路径:登录用户的家目录
3.cd:切换命令的名称(../..)
cd命令的结果:250 Directory successfully changed.
550 Failed to change directory.
4.ls:浏览服务器端的当前目录下的内容
ls命令的结果:文件类型、文件权限、硬链接数、用户id、用户组的id、大小、时间、文件名称
5.get:从服务器端下载文件,下载的文件存放到当前登录服务器的目录下
6.put:将客户端的文件上传到服务器端上,存放上传文件的目录是当前服务器的目录
7.quit:客户端结束运行
客户端功能:
接收命令和参数,将命令和参数发送给服务器,并等待命令的结果
命令:enum ftp_cmd
{
CMD_LOGIN,
CMD_PWD,
CMD_CD,
CMD_LS,
CMD_PUT,
CMD_GET,
CMD_QUIT,
CMD_MAX
};
struct arg_login
{
char login_user[512];
char login_pwd[512];
};
struct cli_to_ser
{
int ftp_cmd; //命令
union
{
struct arg_login argLogin;
char argCd[1024];
char argGet[1024];
char argPut[1024];
}ftp_arg;
};
等待结果:struct ser_to_cli
{
int result; //0---登录成功 -1----失败 -2---Failed to change directory. 1----Directory successfully changed. 2---ls命令完毕 3--get/put失败
char string_result[512];
};
服务器功能:1.接收进程-----负责接收数据
| struct recv_to_opt
| {
(无名管道) | struct cli_to_ser data;
| struct sockaddr_in cli_addr;
| };
2.处理进程-----负责查看客户端是否以前登录过服务器,如果没有登录服务器,则记录客户端的登录信息,并创建子进程为客户端服务。如果登录过服务器,直接将数据封装完毕后发送给客户端对应的子进程。
|
| (每个子进程---处理进程:创建一个众所周知的管道)
| 或者
| (消息队列)
|
3.子进程集----负责处理命令,并将处理结果发送给对应的客户端
注意:
1.文件传输时使用tcp,客户端是被动端,服务器端子进程是主动端
2.当服务器端子进程接收到quit命令时,子进程将登录表中的客户端对应的信息删除,子进程向客户端发送quit命令执行成功,子进程消亡,该子进程的父进程一定为其收尸,避免僵尸进程。
客户端登录表(临界资源,在共享内存---互斥(信号量)中开辟空间,以数组的方式存储):
struct cli_login_table
{
struct in_addr cli_addr;
pid_t child_pid;
};
R: ipv6:
1.查看是否有ipv6模块
lsmod | grep ipv6
2.开启ipv6功能:
vim /etc/sysconfig/network--------》NETWORKING_IPV6=yes
3.ipv6的地址:
ipv4的地址长度是32b(4个bytes)
ipv6的地址长度是128b(16个bytes)
fe80::d6be:d9ff:fed4:90be/64-----64区分网络段和主机段,所以ipv6没有子网掩码
4.特殊的ipv6地址:
0:0:0:0:0:0:0:0------>INADDR_ANY
::1--------->127.0.0.1
5.临时设置ipv6地址
ifconfig em1 add ipv6地址
长久设置:
vim /etc/sysconfig/network-scripts/ifcfg-em1---------》IPV6INIT=yes IPV6ADDR=IPV6地址
6.ipv6地址结构体
struct sockaddr_in6
{
sin6_family;
sin6_port;
sin6_addr;
};
S: unix域套接字(不是协议):
应用于:进程间通信
协议族:AF_LOCAL
地址结构体:struct sockaddr_un
{
short sun_family; //AF_LOCAL--tcp AF_UNIX---udp
char sun_path[]; //地址就是一个文件
};
计算地址结构体的长度:SUN_LEN(&address);
T: 设置tags文件:
1.进入要设置tags的目录
2.生成tags文件:ctags -R
3.把tags文件添加到vim中:vim /etc/vimrc 加入 set tags=/usr/include/tags