一些关于网络知识的笔记,便于以后翻阅

栈存放:局部变量,先入后出,系统自己分配和释放。
  函数参数
  
堆存放:数组
  少于200字节的数组

pthread_join等待线程函数结束
pthread_create创建线程函数,线程间同步操作。

select的作用:网络中当存在阻塞时,为了不影响别的程序运行,选择了select
 read / recv 阻塞函数,
accept返回值时一个新的socketID;

文件描述符为什么要加1?
 因为文件描述符是从0 开始的。 0 1 2。。。。。最大文件描述符是2, 但是实际上
监听的文件描述符是3个

进程的内存结构:
  在创建进程时,会给它分配内存。4G
  一般情况下分为四个段,代码段,数据段,堆段,栈段
 代码段时只读的;数据段一般分为bss段(未初始化的全局变量)
 数据段(已初始化的全局变量、静态变量)和常量区
 堆段(malloc / new)
 栈段(局部变量,参数,保存现场)  

1.UDP
打开socket
绑定自己的ip,port
通信recvfrom/set dest addr, sendto-->addr
关闭socket

2. UDP download

server:
打开socket
绑定自己的ip,port
open file
发送文件大小
while(1)
{
ret = read();
if (0 >= ret)
 break;
sendto
}

close file
close socket


client:
打开socket
绑定自己的ip,port
create and open file
接收文件大小

while(1)
{
ret = recvfrom();
if (0 >= ret)
 break;
write
fileSize -= ret;
if (0 >= fileSize)
 break;
}

close file
close socket


内存使用:小栈大堆
栈:先入后出;系统自己分配和释放;用的时候分配,用完自动释放。
栈里存放-》局部变量,参数... ...
例:数组-》栈 -》少于200个字节
堆:
ogm->视频->7M空间

一、IO模型
1.fcntl修改阻塞<->非阻塞
2.多路复用

tcp server

版本1

打开socket; 绑定自己的ip,port; 监听

三次握手
 通信
三次握手
 通信
三次握手
 通信
三次握手
 通信
 ... ...
 
关闭socket

版本2
打开socket; 绑定自己的ip,port; 监听

while(1)
{
 三次握手 accept
 通信     recv, (scanf)send
}
 
关闭socket

版本3
打开socket; 绑定自己的ip,port; 监听

while(1)
{
 三次握手 accept
 通信     recv, recv, recv ... (scanf)send
}
 
关闭socket

版本4
打开socket; 绑定自己的ip,port; 监听

while(1)
{
 三次握手 accept(socketID)
 while(1){
  通信     recv(newID) //(scanf)send
 }
}
 
关闭socket

总结:
当前的程序中有多个阻塞描述符,每一个描述符可能会对其它描述符的正常通信造成影响。

可以使用多路复用来解决上述问题,例如select

#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

int select(int n, fd_set *read_fds, fd_set *write_fds, fd_set *except_fds, struct timeval *timeout);
参数
n: 监听的最大文件描述符+1.
read_fds : 读文件描述符集。-- 通俗的讲是使用读相关的函数来操作描述符的。
 例: accept, read, recv, scanf ... ...
write_fds : 所有要的写文件文件描述符的集合
except_fds : 其他要向我们通知的文件描述符
timeout : 超时设置.
 NULL:一直阻塞,直到有文件描述符就绪或出错
 时间值为0:仅仅检测文件描述符集的状态,然后立即返回
 时间值不为0:在指定时间内,如果没有事件发生,则超时返回。

定义变量fd_set read_fds;
FD_SET     将fd加入到fd_set
FD_CLR     将fd从fd_set里面清除
FD_ZERO    从fd_set中清除所有的文件描述符
FD_ISSET   判断fd是否在fd_set集合中

void FD_CLR(int fd, fd_set *set);
int  FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);

//清空
FD_ZERO(&read_fds);
//把socketID加入到读描述符集里
FD_SET(socketID, &read_fds);

//设置描述符最大值加1
int maxFds = socketID + 1;

//调用select
select(maxFds, &read_fds, NULL, NULL, NULL);
 
多路复用版本1
打开socket; 绑定自己的ip,port; 监听

fd_set read_fds;
//清空
FD_ZERO(&read_fds);
//把socketID加入到读描述符集里
FD_SET(socketID, &read_fds);

//设置描述符最大值加1
int maxFds = socketID + 1;

while(1)
{
 select(maxFds, &read_fds, NULL, NULL, NULL);
 三次握手 accept(socketID)
 //while(1){
  通信     recv(newID) //(scanf)send
 //}
}
 
关闭socket  

总结版本1,问题是:当一个客户端连接服务器后,accept会返回newID.
这时,直接调用recv,这时,如果当前客户端没有发过来数据,那么recv阻塞。
此时,如果第二个客户端想要连接服务器,必须要等待。
 
多路复用版本2
打开socket; 绑定自己的ip,port; 监听

fd_set read_fds;
//清空
FD_ZERO(&read_fds);
//把socketID加入到读描述符集里
FD_SET(socketID, &read_fds);

//设置描述符最大值加1
int maxFds = socketID;

while(1)
{//1,3,4,5
 fd_set tmp = read_fds;
 select(maxFds +1, &tmp, NULL, NULL, NULL);
 //select正确返回时,判断每一个监听的描述符是否可以进行IO操作
 for (i = 0; i < maxFds + 1; i++)
 {
  if (FD_ISSET -> socketID && socketID == i)//tmp
  {
   三次握手 accept(socketID)--> newID=16
   FD_SET(newID, &read_fds);   //16
   if (maxFds <  newID)       16
   {
    maxFds = newID;
   }
  }
  if (FD_ISSET -> i)//tmp
   通信     recv(i) //(scanf)send
 }
}
 
关闭socket  

 
typedef struct
  {
    __fd_mask __fds_bits[__FD_SETSIZE / __NFDBITS];

  } fd_set;

__fd_mask类型 : long int
__fds_bits数组名
__FD_SETSIZE / __NFDBITS数组的大小: 1024 / (8 * sizeof(long int))

-->
typedef struct
  {
    long arr[128 / (sizeof(long))];
  } fd_set;

fd_set readFds; --> 大小为1024bit


 select(maxFds +1, &read_fds, NULL, NULL, NULL);
 三次握手 accept(socketID)--> newID=13
 
 FD_SET(newID, &read_fds);
 if (maxFds <  newID)13
 {
  maxFds = newID;
 }
  
 //2
 select(maxFds +1, &read_fds, NULL, NULL, NULL);
 if (FD_ISSET -> socketID)
 {
  三次握手 accept(socketID)--> newID=15
  FD_SET(newID, &read_fds);   //15
  if (maxFds <  newID)       15
  {
   maxFds = newID;
  }
 }
 if (FD_ISSET -> 13)
  通信     recv(13) //(scanf)send
 
 //3-> socketID, 13, 15
 select(maxFds +1, &read_fds, NULL, NULL, NULL);
 if (FD_ISSET -> socketID)
 {
  三次握手 accept(socketID)--> newID=16
  FD_SET(newID, &read_fds);   //16
  if (maxFds <  newID)       16
  {
   maxFds = newID;
  }
 }
 if (FD_ISSET -> 13)
  通信     recv(13) //(scanf)send
 if (FD_ISSET -> 15)
  通信     recv(15) //(scanf)send

 //4 socketID, 13, 15, 16
 select(maxFds +1, &read_fds, NULL, NULL, NULL);
 //select正确返回时,判断每一个监听的描述符是否可以进行IO操作
 if (FD_ISSET -> socketID)
 {
  三次握手 accept(socketID)--> newID=16
  FD_SET(newID, &read_fds);   //16
  if (maxFds <  newID)       16
  {
   maxFds = newID;
  }
 }
 if (FD_ISSET -> 13)
  通信     recv(13) //(scanf)send
 if (FD_ISSET -> 15)
  通信     recv(15) //(scanf)send
 if (FD_ISSET -> 16)
  通信     recv(16)
  
 思考:上面的在个if是重复性的动作,想要简化。 
 方法1:
  for (i = 0; i < array.count; i++)
  {
   if (FD_ISSET -> i)
    通信     recv(i) //(scanf)send
  }  
 总结:数组的大小无法确定,不好。
 方法2:
  for (i = 0; i < maxFds + 1; i++)
  {
   if (FD_ISSET -> i)
    通信     recv(i) //(scanf)send
  }
 
 ---------------------------------------------
 现在综合上面把//4修改一下
 select(maxFds +1, &read_fds, NULL, NULL, NULL);
 //select正确返回时,判断每一个监听的描述符是否可以进行IO操作
 for (i = 0; i < maxFds + 1; i++)
 {
  if (FD_ISSET -> socketID && socketID == i)
  {
   三次握手 accept(socketID)--> newID=16
   FD_SET(newID, &read_fds);   //16
   if (maxFds <  newID)       16
   {
    maxFds = newID;
   }
  }
  if (FD_ISSET -> i)
   通信     recv(i) //(scanf)send
 }
 
 
 


   

猜你喜欢

转载自blog.csdn.net/qq_37347705/article/details/54580016