TCP

一、TCP的连接模型
SYN同步???
ACK相应???
 
-------------------------------------------------------------------------------------------------------------------------------------
socket
 
功能:socket 创建socket套接字接口(选择协议族,选择协议)
#include <sys/types.h>
#include <sys/socket.h>
 
int socket(int domain, int type, int protocol);
  1. domain : 协议族 AF_INET
  2. type : 套接字的类型
SOCK_STREAM、SOCK_DGRAM、SOCK_RAW
流式套接字、数据报套接字、原始套接字
  1. protocol : 协议
(一般设置为0表示选择默认协议,只有原始套接字不能写0而需要根据具体协议选择)
 
具体可查看/usr/include/linux/in.h里面协议的定义
 
返回值
成功:非负的套接字描述符
出错:-1
 
 
补充
SOCK_DGRAM:固定长度、无连接、不可靠报文传输
SOCK_RAW:IP协议的数据报接口
SOCK_STEAM:有序、可靠、双向的、面向连接的字节流
SOCK_SEQPACKET:固定长度、有序、可靠面向连接的报文传递
 
一个连接由server_ip, server_port, client_ip, server_port唯一确定。你可用getsockname和getpeername由某个socket取得。
 
关于监听套接字和accept返回的新的套接字的区别
借用以下情景说明。好比你去吃饭,饭馆门迎小姐(监听SOCKET)看到你来后和你打招呼,
然后(ACCEPT)找来一个服务员小姐(NEW SOCKET)伺候你,然后守在门口继续欢迎(监听)下一个。
当然门迎小姐会记录是哪一位服务员小姐招待了你;如再有新客人来,门迎小姐(同一监听SOCKET)
又会安排另一位服务员小姐(NEW SOCKET)伺候。
门迎小姐走了,伺候每一位客人的服务员小姐不受影响。
从以上情景可知连接建立后,客户端用发出连接的那个SOCKET向服务器发数据,
是发给服务器新创建的SOCKET,而不是服务器的监听SOCKET。
服务器的监听SOCKET永远只是用来接受连接请求。
-------------------------------------------------------------------------------------------------------------------------------------
bind
 
功能:bind 绑定IP地址及端口号
#include <sys/types.h>
#include <sys/socket.h>
看见 fd 就是文件描述符啦(这个文件描述符具体是个啥??)
int bind( int sock fd, const struct sockaddr *addr,socklen_t addrlen);
  1. sockfd : 套接字描述符
  2. addr : 绑定的地址
  3. addrlen: 地址的长度
 
返回值
成功:返回0
失败:返回-1
 
地址相关数据结构
1:通用地址结构(每种协议族都有自己的地址格式的表达格式,并不所有的都是ip地址+端口号的形式
所以就定义这么一个通用地址结方便所有协议族使用)
struct sockaddr {
sa_family_t sa_family; 协议族
char sa_data[14]; 地址信息
}
2.ipv4协议族地址格式
struct sockaddr_in {
sa_family_t sin_family; /* address family: AF_INET */
in_port_t sin_port; /* port in network byte order */
struct in_addr sin_addr; /* internet address */
};
 
/* Internet address. */
struct in_addr {
uint32_t s_addr; /* address in network byte order */
};
 
man 手册中对sockadd_in 是这么描述的,但是少了8bit(char sin_zero[8]),主要因为这8bit保留用不到,man手册中为表明
所以以ppt上的为准。
 
sin.sin_family = AF_INET;
sin.sin_port = htons(0); //分配随机端口
sin.sin_addr.s_addr = htonl(INADDR_ANY); //指 0.0.0.0
-------------------------------------------------------------------------------------------------------------------------------------
RTPRIO与nice值
互斥锁只是概念,方式。是一个单独变量,通过这个变量来决定是否向下运行。
listen由内核负责,不会阻塞
工作模式!!!!很重要
-------------------------------------------------------------------------------------------------------------------------------------
listen
 
listen:监听连接的套接字(宣告服务器愿意接受连接请求)
当socket刚刚被建立时,工作在 主动模式 (active),此时一个客户端可以使用该socket发起
connect调用。而TCP服务器可以调用listen将该socket变为 被动模式 (passive),此时就可以
在这个socket上侦听来自客户端的请求了。
#include <sys/types.h>
#include <sys/socket.h>
 
int listen(int sockfd, int backlog);
  1. sockfd : 套接字描述符
  2. backlog: 指定了未完成连接的最大队列长度 5
(它的作用在于处理可能同时出现的几个连接请求。一旦队列满了,系统就会拒绝多余的连接请求。) 等待队列的长度,上限由SOMAXCONN指定,TCP,其默认值128。
 
dos攻击 :拒绝服务式攻击
 
返回值
成功:0
失败:-1
理解backlog参数,我们必须认识到内核为任何一个给定的监听套接字维护两个队列
(1)未完成队列:
已经由某个客户发出并到达服务器,而服务器正在等待完成相应得到TCP三次握手过程。
(2)已完成队列:
每个已完成TCP三次握手过程的客户对应其中一项。
-------------------------------------------------------------------------------------------------------------------------------------
接受请求会阻塞
accept
 
accepct : 接受连接请求并建立连接(从已完成连接队列的队头返回一个已完成的连接)
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
 
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
为什么必须用地址??看下面值-结果参数
  1. sockfd : 套接字描述符
  2. addr :客户端的地址信息
  3. addrlen:指定客户端地址的长度(必须初始化)值-结果参数
 
socklen_t addrlen = sizeof(struct sockaddr);
 
返回值
成功:返回新的(已连接的)套接字描述符
失败:返回-1
 
注意
如果已完成连接队列为空(没有连接请求在等待),accept 会阻塞直到一个请求到来。如果sockfd处于非阻塞模式,accept会返回-1,并且设置出错码。
-------------------------------------------------------------------------------------------------------------------------------------
connect
 
connect :发起连接请求
如果要处理一个面向连接的网络服务,那么在开始交换数据前,需要客户端和服务器建立一个连接。
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
 
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
  1. sockfd : 套接字描述符
  2. address :指定想与之通信的服务器地址信息
  3. addrlen : 地址长度
 
 
返回值
成功:0
失败:-1
 
注意
connect 连接的服务器必须是开启的,并且正在运行,并且服务器的等待队列要有足够的空间
-------------------------------------------------------------------------------------------------------------------------------------
send
 
send 发送数据
#include <sys/types.h>
#include <sys/socket.h>
ssize_t write(int fd, const void *buf, size_t count);
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
 
  1. sockfd : 套接字描述符
  2. buf :缓冲区的首地址
  3. len : 发送数据的长度(字节)
  4. flags : 发送方式 一般为0(以默认模式进行发送)
 
 
返回值
成功:实际发送的字节数
失败:-1
注意事项:
  1. 如果发送消息过长,发送会失败并返回错误码EMSGSIZE,消息也不会被发送出去。
  2. 要注意send函数返回成功,此时这些数据并不一定马上被传到连接的另一端。而仅仅只是将buf中的数据拷贝的socket的发送缓冲区。
  1. send和write 唯一的区别就在于flags,当flags == 0时 write就等价于send
  2. 如果协议在后续的传送过程中出现网络错误的话,那么下一个send函数就会返回SOCKET_ERROR。(tcp循环会遇到)
 
flags参数很少会去用到,所以一般建议大家填0
flags 说明 recv send
MSG_DONTROUTE 绕过路由表 *
MSG_DONTWAIT 仅本操作非阻塞 * *
MSG_OOB 发送或接受外带数据 * *
MSG_PEEK 窥看外来数据 *
MSG_WAITALL 等待所有数据 *
-------------------------------------------------------------------------------------------------------------------------------------
recv
 
recv 接收数据
#include <sys/types.h>
#include <sys/socket.h>
 
ssize_t read(int fd, void *buf, size_t count);
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
  1. sockfd : 套接字描述符
  2. buf :接受缓冲区的首地址
  3. len : 接受数据的长度(字节)
  4. flags : 接受方式 一般为0(以默认模式进行接受)
 
 
返回值
成功:实际接受的字节数
失败:-1
当读端被关闭时返回 0
 
flags参数很少会去用到,所以一般建议大家填0
flags 说明 recv send
MSG_DONTROUTE 绕过路由表 *
MSG_DONTWAIT 仅本操作非阻塞 * *
MSG_OOB 发送或接受外带数据 * *
MSG_PEEK 窥看外来数据 *
MSG_WAITALL 等待所有数据 *
 
注意
recv 默认是阻塞的接受数据,当发送方被关闭,recv会返回0------- 断开连接时
-------------------------------------------------------------------------------------------------------------------------------------
shutdown 关闭部分全双工传输 / close
#include <sys/socket.h>
int shutdown(int sockfd, int howto);
  1. sockfd :套接字描述符
  2. how : 关闭的方式
SHUT_RD == 0
SHUT_WR == 1
SHUT_RDWR == 2 此时 == close
向一个 写端已经 关闭的套接字 写数据,内核会给进程发送 SIGPIPE信号,接收方的读端立刻返回0
从一个读端已经关闭的套接字读数据,read会立刻返回0。
(循环读的时候,会发现一直返回0;此时如果对端发送数据,read依然可以接受。后续会继续返回0)
-------------------------------------------------------------------------------------------------------------------------------------
注意事项:
注意:教材上是PF_开头,而man手册上是AF_开头,这是怎么回事?
参考man手册:
AF是Address Family的缩写,我们叫地址族。PF是Protocol Family的缩写,我们叫协议族。所谓地址族就是一种协议族的地址表达格式,譬如TCP/IP的端点格式是IP地址+port#。Socket的设计人员的设计初衷是,每种协议族在具体表达自己的通信端点的地址格式时会有不止一种格式。即当时以为对于一种协议族,可能会支持多种地址格式,所以就衍生出地址族的概念,定义了两套独立的宏PF_XXX和AF_XXX。但随着技术的发展,现实的结果是,直到目前也没有出现一种PF对应多个AF的情况出现。建议大家在写代码的时候用AF_XXX。
-------------------------------------------------------------------------------------------------------------------------------------
fgets获得指定字节数的输入,不包括(\n)
值-结果(value-result)参数
前言
当把套接口地址结构传递给套接口函数时,总是通过指针来传递的,即传递的是一个指向结构的指针。结构的长度也作为参数来传递,其传递的方式取决于结构的传递方向:从进程到内核,还是从内核到进程。
1、从进程到内核
从进程到内核传递套接口地址结构的函数有3个:bind、connect和sendto,这3个函数的一个参数是指向套接口地址结构的指针,另一个参数是结构的整数大小,例如:
/* example */ struct sockaddr_in serv; /* fill in serv{} */connect(sockfd, ( struct sockaddr *)&serv, sizeof(serv));
由于指针和指针所指结构的大小都传递给内核,所以从进程到内核要确切拷贝多少数据是知道的,如下图所示:
2、从内核到进程
从内核到进程传递套接口地址结构的有4个函数:accept、recvfrom、getsockname和getpeername。这4个函数的两个参数是:指向套接口地址结构的指针和指向表示结构大小的整数的指针,例如:
/* example */ struct sockaddr_un cli; /* Unix domain */socketlen_t len;len = sizeof(cli); /* len is a value */getpeername(unixfd, ( struct sockaddr *)&cli, &len); /* len may have changed */
为何将结构大小由整数改为指向整数的指针呢?
这是因为:当函数被调用时,结构大小是一个值(value,此值告诉内核该结构的大小,使内核在写此结构时不至于越界),当函数返回时,此结构又是一个结果(result,它告诉进程内核在此结构中确切存储了多少信息),这种参数类型叫做 值-结果(value-result)参数,如下图所示:
-------------------------------------------------------------------------------------------------------------------------------------
二、服务器模型
在网络程序里面,通常都是一个服务器处理多个客户机。
为了处理多个客户机的请求, 服务器端的程序有不同的处理方式。
目前最常用的服务器模型.
循环服务器:
循环服务器在同一个时刻只能响应一个客户端的请求
TCP循环服务器模型
UDP循环服务器模型
 
并发服务器:
并发服务器在同一个时刻可以响应多个客户端的请求
TCP并发服务器模型 tcp并发服务器 可以试一下用线程咋写
UDP并发服务器模型
IO多路复用并发服务器模型
 
 
(1)TCP循环服务器模型
特点:TCP循环服务器一次只能处理一个客户端的请求。
只有在当前客户的所有请求都完成后,服务器才能处理下一个客户的连接/服务请求。
 
缺点:如果某个客户端一直占用服务器资源,那么其它的客户端都不能被处理。
优点:搭建简单
以下是伪代码
(2)TCP并发服务器模型
设计思想:是服务器接受客户端的连接请求(accept)后创建子进程(fork)来为客户端服务
优点:TCP并发服务器可以避免TCP循环服务器中客户端独占服务器的情况。
缺点:为了响应客户机的请求,服务器要创建子进程来处理。 如果有多个客户端的话,服务器端需要创建多个子进程。
过多的子进程会影响服务器端的运行效率
 
1-31 非实时信号(发生的信号可能丢失,不支持信号排队)
 
32-64 实时信号 (支持信号排队,发生的都会被接收)
 
/usr/include/i386-linux-gnu/bits/signum.h 信号头文件信号功能
 
关闭不必要的文件描述符
回收僵尸进程
(1)父进程wait
(2)init进程回收
(3)真父委托继父:
父进程 显示忽略SIGCHLD信号(此信号在进程 暂停,唤醒,结束时都会产生
signal(SIGCHLD, SIG_IGN); // 为了不生成僵尸进程 ),系统认为父进程不关心子进程退出状态,这时init会回收僵尸进程
 
目录操作函数:
opendir 打开目录
#include <dirent.h>
 
DIR *opendir(const char *name);
  1. name : 目录名
 
返回值:
成功返回:目录流指针
失败返回:NULL并设置错误码
-------------------------------------------------------------------------------------------------------------------------------------
readdir 读目录
#include <dirent.h>
 
struct dirent *readdir(DIR *dirp);
  1. dirp : 目录流指针
 
返回值:
成功 返回 指向dirent类型的结构体指针
失败 NULL
相关结构体:
注意一次只能读一个文件的信息
-------------------------------------------------------------------------------------------------------------------------------------
closedir 关闭目录
#include <dirent.h>
 
int closedir(DIR *dirp);
  1. dirp : 目录流指针
 
返回值:
成功 返回 0
失败 返回 -1
-------------------------------------------------------------------------------------------------------------------------------------
作业!!!(tcp文件服务器)
 
 
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值