您的位置:首页 > 理论基础 > 计算机网络

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

2017-01-17 09:13 375 查看
栈存放:局部变量,先入后出,系统自己分配和释放。

  函数参数

  

堆存放:数组

  少于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

 }

 

 

 

   
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: