前言
首先看看TCP/IP网络协议和在我们计算机系统层次中的对应关系。
socket的位置
网络通信的本质就是贯穿网络协议层的过程。
局域网数据的封装和解包过程
逻辑上我们认为同层协议之间通信
几乎任何层的协议都会提供一种解包和分用的功能。
几乎任何层的协议,都要在报头中提供,决定将自己的有效载荷交付给上层的哪个协议。我们称为分用。端口号用来标识上层的服务。这是大部分协议的共性。
以太网通信广播的方式。会有一个协议CSMA/CD。
我们日常网络通信的本质:就是进程间通信!看到同一份资源,就是网络资源。
端口号无论对于客户端还是服务器,都能唯一的标识该主机上的一个网络应用层的进程。
在公网上:IP地址能标识唯一的一台主机,端口号port,用来标识该主机上的唯一的一个进程。
IP:Port = 标识全网唯一的一个进程。
[ client_ip:client_port server_ip server_port]标识了全网唯二的进程。我们ip:port称为socket
端口号 vs 进程pid:
pid已经能够标识一台主机上进程的唯一性了,为什么还要一个端口号 1、不是所有的进程都要网络通信,但是所有的进程都要有pid。2、系统和网络功能的解耦。
一个进程可以绑定多个端口号,一个端口号不可以被多个进程绑定。
网络字节序:
发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出 ;接收主机把从网络上接到的字节依次保存在接收缓冲区中 , 也是按内存地址从低到高的顺序保存 ;因此 , 网络数据流的地址应这样规定 : 先发出的数据是低地址 , 后发出的数据是高地址 .TCP/IP 协议规定 , 网络数据流应采用大端字节序 , 即低地址高字节 .不管这台主机是大端机还是小端机 , 都会按照这个 TCP/IP 规定的网络字节序来发送 / 接收数据 ;如果当前发送主机是小端 , 就需要先将数据转成大端 ; 否则就忽略 , 直接发送即可。
网络字节序,主机字节序的相互转换,h代表主机,n代表网络。
套接字的接口:
// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);
// 绑定端口号 (TCP/UDP, 服务器)
int bind(int socket, const struct sockaddr *address,socklen_t address_len);
// 开始监听socket (TCP, 服务器)
int listen(int socket, int backlog);
// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address,socklen_t* address_len);
// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
套接字编程的种类:
1、域间套接字编程 --- 同一个机器内
2、原始套接字编程 --- 编写一些网络工具
3、网络套接字 --- 用户间的网络通信
想将网络接口统一抽象化
socket套接字里面 sockaddr的结构体
udp服务器
Tcp服务器
这两个代码可以到我的码云上查看
gitee链接
守护进程
前后台进程:键盘信号只能发给前台进程,前台和后台进程都可以向屏幕进行打印,前台进程可以接收键盘的数据,所以谁拥有键盘谁就是前台进程。一个操作系统里面可以有一个前台和多个后台进程
在命令行中,前台进程必须一直存在v
组长是多个进程中的第一个,进程组和任务是什么关系呢?任务是让进程组完成的。多个任务,在同一个session内启动的sid是一样的 这里的1351就是bash进程号。
每一次登录都会增加一个bash进程,也就是一个会话。
如果不想受用户退出登录的影响,我们可以将进程守护进程化。windows系统中的注销就会把所有的会话都删除。
自成进程组自成会话的进程,守护进程。
如何做到的:
直接将子进程设置成独立的会话,通过fork来进行,接下来我们编写守护进程的代码
#pragma once
#include <iostream>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <unistd.h>const std::string nulldictname = "/dev/null";
void Daemon(/*参数问题:这里可以传参,如果传入参数更改当前的工作目录,没有就不要更改,默认就可以*/const std::string& dict = "")
{//首先需要忽略信号//捕捉信号,把他设置成忽略signal(SIGPIPE,SIG_IGN);signal(SIGCLD,SIG_IGN);signal(SIGSTOP,SIG_IGN);//把调用进程设置成独立会话if(fork() > 0) exit(0); //父进程直接退出,子进程变成孤儿进程,由pid为1的进程托孤//所以守护进程都是孤儿进程setsid();//改变对应的目录if(!dict.empty()) chdir(dict.c_str());//把标准输入输出标准错误重定向到dev/null文件里,该文件相当于windows系统的回收站int fd = open(nulldictname.c_str(),O_RDWR);if(fd > 0){dup2(fd,0);dup2(fd,1);dup2(fd,2);}
}
这里的守护进程系统提供了一个接口,叫做daemon函数。接下来我们使用这个函数
tcp是全双工通信,因为它有两个缓冲区。发送缓冲区和接收缓冲区