1. 程式人生 > >一些關於網路知識的筆記,便於以後翻閱

一些關於網路知識的筆記,便於以後翻閱

棧存放:區域性變數,先入後出,系統自己分配和釋放。
  函式引數
  
堆存放:陣列
  少於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
 }