TCP網絡編程、OSI、TCP\IP模型,socket、循環服務器與併發服務器

TCP編程

基於大腦遺忘特性,還有就是週五有家公司電話面試我,我一開始給回答錯了的原因,在這兒呢,我再強調和複習一下TCP/IP模型。
TCP/IP模型共計 4 層,與OSI模型的七層不一樣,TCP/IP更簡單和高效。
那我們說OIS模型複雜一下,那麼我們先說說OSI模型有哪些吧?

1. OSI模型:

  1. 物理層 ————————幹嘛的?用網線什麼的將兩個臺電腦連起來,然後通過高低電頻傳遞0/1電信號
  2. 數據鏈路層————————幹嘛的?無規則的1000011看不懂,甚至可能還有錯誤,怎麼搞?鏈路層的作用來了。
    數據鏈路層將無規則的'011000011'進行協議檢測,數據對,則通過且打包成幀,不對,重新發。
    還需要控制發送方的發送速率,保證通過鏈路層的數據無差錯地傳遞
  3. 網絡層 ————————幹嘛的?爲網絡上的不同主機提供通信。具體地說,數據鏈路層的數據在這一層被轉換爲數據包,
    然後通過路徑選擇、分段組合、流量控制、擁塞控制等將信息從一臺網絡設備傳送到另一臺網絡設備。
  4. 傳輸層 ————————幹嘛的?前三層是數據通信,後三層是數據處理。運輸層是第四層,承上啓下。網絡層只是傳到了各個主機(網絡設備)
    運輸層還要控制到具體的程序,所以:第一,提供可靠的端到端(進程到進程)的通信;第二,向會話層提供獨立於網絡的運輸服務。
  5. 會話層 ————————幹嘛的?管理主機之間的會話進程,即負責建立、管理、終止進程之間的會話。
    會話層還利用在數據中插入校驗點來實現數據的同步。
  6. 表示層 ————————幹嘛的?表示層對上層數據或信息進行變換以保證一個主機應用層信息可以被另一個主機的應用程序理解。
    表示層的數據轉換包括數據的加密、壓縮、格式轉換等。
  7. 應用層 ————————幹嘛的?應用層爲操作系統或網絡應用程序提供訪問網絡服務的接口。

OSI模型我們已經講完了,雖然包括我在內的很多人一時之間也記不準,甚至還容易弄混亂,但多複習,總有一天會好的。
接下來,我們講講 TCP/IP模型:

2.TCP/IP模型:

  1. 網絡接口層————————幹嘛的?負責將二進制流轉換爲數據幀,並進行數據幀的發送和接收.
  2. 網絡層 ————————幹嘛的?負責在主機之間的通信中選擇數據包的傳輸路徑,即路由。網絡層還要負責處理傳入的數據包、
    檢驗其有效性,最後還需要根據需要發出還是接收ICMP差錯和控制報文
  3. 傳輸層 ————————幹嘛的?負責實現應用程序之間的通信服務,又叫端到端通信。傳輸層要系統的管理信息的流動、還要提供
    可靠的傳輸協議,確保數據到達無差錯、無序亂。
  4. 應用層 ————————幹嘛的?把封裝好的數據提交給傳輸層或者是從傳輸層接受數據並處理。

小結一下:我們看TCP/IP模型可以看出,TCP/IP的網絡接口層對應OSI模型的物理層和數據鏈路層,網絡層對應OSI的網絡層, 傳輸層對應傳輸層,應用層對應OSI的會話層、表示層、應用層

 思考:	在前面的OSI模型中的數據鏈路層中,我們講到鏈路層的作用,
 在網絡層中我們提到路由的概念,那我們常聽到的交換機和路由器到底在模型中的哪一層呢?

(我之前聽過多次,但沒有接觸過交換機,家裏也在用路由器,但真沒了解過,面試問到都是自己推理出來的,現在我們去了解一下,當然,也只是粗略的說說原理,具體的還是後面再去深究,感覺並不簡單。)

  1. 首先,我們需要弄清楚交換機是什麼?路由器是什麼?
    1. 交換機: https://baike.baidu.com/item/交換機/103532
    2. 路由器: https://baike.baidu.com/item/路由器
    然後根據《計算機網絡自頂向下方法》中擴述,我進行理解爲交換機主要在於數據交換,即使現在很多多層交換機,
    但交換機的主要作用還是在於特定網絡內的數據交換,路由器主要在於尋址路由,實現不同網絡之間的數據轉發。

  2. 路由器和交換機位於什麼位置?
    路由器位於網絡層,畢竟主要是實現最佳路由的數據轉發。
    交換機得分情況,據《計算機網絡自頂向下方法》P15介紹交換機分:
    1.路由器;
    2.鏈路層交換機。
    路由器不再講,鏈路層交換機當然就是在數據鏈路層了,位於TCP/IP模型的網絡接口層。

  3. 路由和交換機的原理和具體過程,本次就暫不介紹了,我自己也還不清楚,有時間了我再好好學習,整理整理髮出來。

3. socket編程的基本函數

  1. socket() :創建套接字,同時制定協議和類型

  2. bind() :綁定本機 ( IP + port) + 套接字,主要用於服務器,客戶端不需要綁定。

  3. listen() :設置監聽,將套接字設置爲監聽模式,準備接受客戶端的連接請求

  4. accept() :接收 TCP,服務器調用 accept() 等待接收客戶端的連接,建立好後返回一個新的連接套接字

  5. connect() :建立連接,客戶端通過該函數向服務器的監聽套接字發送連接請求

  6. send() :TCP 發送數據,也可以用在 UDP

  7. recv() :TCP接收數據,也可以用在UDP

  8. sendto() :UDP發送數據,當用在TCP時候,地址參數失效,等同於send()

  9. recvfrom() :UDP接收數據,當用在TCP時候,地址參數失效,等同於recv()

  10. close() :關閉套接字

    備註:通常來講,服務器是固定的,方便客戶端的連接。

4. 實現思路

想一想,假設是 TCP通信,客戶端和服務器從創建socket()close(),這中間由什麼異同嗎?
1. 服務器一般是長時間開啓,那麼基本是固定的。只需要等待客戶端的連接,連接成功後處理事務。
2. 客戶端是多樣的,那麼客戶端就需要去主動連接服務器,在這個中間,就需要考慮怎麼建立連接問題。
3. 既然客戶端是移動多變的IP, 服務器是固定的IP和port,那麼到底誰綁定誰?有沒有誰可以不綁定呢?
4. 首先,服務器和客戶端肯定都需要創建socket的。
5. 創建好了後,服務器需要綁定本機,就是服務器自己的信息,爲什麼必須綁定呢?那客戶端需不需要去綁定自己呢?
1. 服務器不綁定自己 IP和端口,客戶端怎麼能夠找到服務器呢?所以服務器必須綁定
2. 客戶端可以不需要綁定,系統會自動分配端口,連接服務器是客戶端的主動過程,在這個過程中客戶段系統自動
分配一個端口,隨着連接信息發送給服務器,服務器收到信息後拆解其中的IP和端口就知道服務器了,就可以正常
通信了,如果綁定,端口號反而容易出錯,所以一般主觀上客戶端不綁定,讓系統自動分配。
6. 客戶端是一個主動的過程,服務器是一個被動的過程,那麼,服務器就需要提前開啓自己,讓自己保持在
隨時可以連接的狀態,即 listen狀態,那麼服務器就需要創建 listen(),然而客戶端不需要。
7. 前面總共幾個步驟了?
服務器 ------------------------------------ 客戶端
socket() ---------------------------------- socket()
bind()
listen()
8. 經過前面的步驟,服務器就處於一個等待連接的狀態了,客戶端也處於已經創建好的狀態,隨時可以連接。
那麼,在這兒,客戶端進行主動行連接,即 connect(),服務器是被連接的,需要接受信息,所以 accept()
9. 現在客戶端和服務器就連接好了,只需要發送信息和接收信息了。所以雙方都是send()、recv()
10. 信息發完了,服務器和客戶端各自關閉close()。這兒,需要考慮誰先關誰後關的問題。
一般來講服務器是長時間開啓且固定,那麼正常情況下就是客戶端先關閉,開啓四次揮手過程。
如果服務器先關閉了,那麼服務器並不處於listen狀態,客戶端的報文將無法被服務器接收,
會返回一個錯誤值給客戶端,客戶端在收到錯誤值的時候,也會自動關閉TCP。

整理一下:

	服務器 ----------------------------------客戶端
	socket()  ----------------------------- socket()
	bind()
	listen()              
	accept() -------------------------------connect()
	send()/recv()------------------------recv()/send()  
	recv()/send() -----------------------send()/recv()
	close()  -------------------------------close()
		
    7 步驟  --------------------------------- 5 步驟

當連接建立好後,服務器和客戶端都可以主動發送信息,不一定非要客戶端先發送。

5. 函數重點講解:

  1. socket():
    int socket( int family, int type, int protocol)
    family:協議家族 type:套接字類型 protocol:0(原始套接字除外:ping命令等)

    1. family協議家族:1. AF_INET -----IPV4協議
      2. AF_INET6 -----IPV6協議
      3. AF_LOCAL -----UNIX或協議(本地協議)
      4. AF_ROUTE -----路由套接字
      5. AF_KEY -----密鑰套接字

    2. type套接字類型:1. SOCK_STREAM ------流式套接字( TCP)
      2. SOCK_DGRAM ------數據報套接字( UDP)
      3. SOCK_RAW ------原始套接字( ping命令)

      返回值: 1. 成功: 非負套接字描述符(文件描述符) 2.出錯:-1

      幫助手冊" man socket"看一下,AF_INIT(IPV4)在第七頁," man 7 ip"試試。
      進入後,選擇 tcp_socket = socket( AF_INET, SOCK_STREAM, 0);
      ipv4 的TCP連接創建套接字

  2. bind():
    int bind( int sockfd, struct sockarrd * my_addr, int addrlen)

    1. sockfd:套接字描述符(socket()返回值)

    2. my_addr:綁定的地址

    3. addrlen:地址的長度

    4. struct sockaddr * my_addr爲通用型,需要將實際型轉換爲通用型。怎麼轉呢?不要急。
      當爲服務器時候, struct sockaddr *serveraddr(my_addr就是一個名字而已,自己取)

      幫助手冊 " man bind"看看, int bind( int sockfd, struct sockarrd * my_addr, int addrlen)
      在 " DESCRIPTION"去看:

      原文:
      When a socket is created with socket(2), it exists in a name space (address family) but has no
      address assigned to it. bind() assigns the address specified to by addr to the socket referred
      to by the file descriptor sockfd.
      addrlen specifies the size, in bytes, of the address structurepointed to by addr.
      Traditionally, this operation is called “assigning a name to a socket”.

      It is normally necessary to assign a local address using bind() before a SOCK_STREAM socket may
      receive connections (see accept(2)).

      The rules used in name binding vary between address families. Consult the manual entries in Sec‐
      tion 7 for detailed information. For AF_INET see ip(7), for AF_INET6 see ipv6(7), …
      翻譯:
      使用socket(2)創建套接字時,該套接字存在於名稱空間(地址族)中,但未分配地址。
      bind()將addr指定的地址分配給文件描述符sockfd所引用的套接字。
      addrlen指定addr指向的地址結構的大小(以字節爲單位)。
      傳統上,此操作稱爲“爲套接字分配名稱”。

      在SOCK_STREAM套接字接收連接之前,通常必須使用bind()分配本地地址(請參見accept(2))。

      名稱綁定中使用的規則在地址族之間有所不同。有關詳細信息,請查閱第7節中的手冊條目。
      對於AF_INET,請參見ip(7);對於AF_INET6,請參見ipv6(7)…

      注意: 看幫助手冊中“ 對於AF_INET,請參見ip(7)”,所以還是回到了幫助" man 7 ip".
      在 " DESCRIPTION"去看:

      原文:
      When a process wants to receive new incoming packets or connections,
      it should bind a socket to a local interface address using bind(2).
      Only one IP socket may be bound to any given local (address, port) pair.
      When INADDR_ANY is specified in the bind call, the socket will be bound to all local interfaces.
      When listen(2) or connect(2) are called on an unbound socket,
      it is auto‐ matically bound to a random free port with the local address set to INADDR_ANY.

      翻譯:
      當進程想要接收新的傳入數據包或連接時,應使用bind(2)將套接字綁定到本地接口地址。
      任何給定的本地(地址,端口)對都只能綁定一個IP套接字。
      在bind調用中指定INADDR_ANY時,套接字將綁定到所有本地接口。
      在未綁定的套接字上調用listen(2)或connect(2)時,
      它將自動綁定到本地地址設置爲INADDR_ANY的隨機空閒端口。

      接下來。我們繼續看 Address Format( 地址格式)
      原文:
      An IP socket address is defined as a combination of an IP interface address and a 16-bit port number.
      The basic IP protocol does not supply port numbers,
      they are implemented by higher level protocols like udp(7) and tcp(7).
      On raw sockets sin_port is set to the IP protocol.

      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 */
      };

      sin_family is always set to AF_INET.
      This is required; in Linux 2.2 most networking functions return EINVAL when this setting is missing.
      sin_port contains the port in network byte order The port numbers below 1024 are called privileged ports (or sometimes: reserved ports).
      Only privileged processes (i.e., those having the CAP_NET_BIND_SERVICE capability) may bind(2) to these sockets.
      Note that the raw IPv4 protocol as such has no concept of a port,
      they are only implemented by higher protocols like tcp(7) and udp(7).

      sin_addr is the IP host address.
      The s_addr member of struct in_addr contains the host interface address in network byte order.
      in_addr should be assigned one of the INADDR_* values (e.g INADDR_ANY) or set using the inet_aton(3),
      inet_addr(3), inet_makeaddr(3) library functions or directly with the name resolver (see gethostbyname(3)).

      翻譯:
      地址和16位端口號的組合。基本IP協議不提供端口號,而是由更高級別的協議(如udp(7)和tcp(7))實現的。在原始套接字上,將sin_port設置爲IP協議。

      struct sockaddr_in {
      sa_family_t sin_family; / * 地址族:AF_INET * /
      in_port_t sin_port; / * 網絡字節順序的端口 * /
      struct in_addr sin_addr; / * 互聯網地址 * /
      };

      / *互聯網地址。 * /
      struct in_addr {
      uint32_t s_addr; / * 地址以網絡字節順序 * /
      };

      sin_family始終設置爲AF_INET。
      這是必需的;在Linux 2.2中,缺少此設置時,大多數聯網功能都會返回EINVAL。
      sin_port包含網絡字節順序的端口。低於1024的端口號稱爲特權端口(有時稱爲保留端口)。
      只有特權進程(即具有CAP_NET_BIND_SERVICE能力的進程)可以將(2)綁定到這些套接字。
      請注意,這樣的原始IPv4協議沒有端口的概念,它們僅由更高的協議(如tcp(7)和udp(7))實現。

      sin_addr是IP主機地址。 struct in_addr的s_addr成員包含網絡字節順序的主機接口地址。
      應該爲in_addr分配INADDR_ *值之一(例如INADDR_ANY),或者使用inet_aton(3),
      inet_addr(3),inet_makeaddr(3)庫函數或直接使用名稱解析器進行設置(請參閱gethostbyname(3))。

      注意:
      我們這兒搞這麼多,其實就是爲了my_addr,這個地址怎麼去表示,填寫。在 " man 7 IP"中Address Format( 地址格式)
      中可以看到 my_addr的表示是一個結構體。

      my_addr 結構體含有三個成員: 1. " sin_family"(協議族,IPV4(AF_INET)還是IPV6(AF_INET6)之類的)
      2. " sin_port"(網絡字節序端口)
      3. " sin_addr"(互聯網地址)
      這兒需要注意: 1. sin_family = AF_INET(即可表示採用IPV4協議)
      2. sin_port是網絡字節序端口,需要進行轉化爲大端存儲,16位htons(端口號:8888)
      這兒需要特別注意,初始化填寫" 8888"爲整形數字,
      如果是初始化時候沒填寫,後期手動寫的"8888"爲字符串,需要進行轉化爲整形,atoi()
      3. " sin_addr"對應的是一個結構體,需要去具體的看結構體

      in_addr 中只有s_addr一個成員,表示:地址以網絡字節序順序存儲。
      假設服務器 IP:192.168.2.219,則表達方式 sin_addr.s_addr = inet_addr(“192.168.2.219”)
      在網絡通信中,統一規定:數據以高位字節優先順序在網絡上傳輸————————大端存儲。

      綜合:
      申明變量、創建套接字、綁定IP 與 端口
      struct sockaddr_in serveraddr;
      int sockfd = socket( AF_INET, SOCK_STREAM, 0);

      原型:
      int bind( int sockfd, struct sockarrd * my_addr, int addrlen)
      實際:
      bzero( &serveraddr, sizeof(serveraddr)); // 將空間元素值置 0;
      serveraddr.sin = AF_INET; // 設置IP協議爲 IPV4;
      serveraddr.sin_port = htons(8888); // 綁定本地端口(8888);
      serveraddr.sin_addr.s_addr = inet_addr(" 192.168.2.219") // 綁定本地IP (192.168.2.219)
      bind( sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)); // 實際綁定

  3. listen():
    int listen( int sockfd, int backlog)
    sockfd : 套接字描述符
    backlog: 請求隊列中方允許的最大數,大多數默認5. // 監聽連接的套接字

    這個函數很好理解,不多講。
    listen( sockfd, 5);
    注意:
    listen()函數不會阻塞,它主要做的事情爲,
    將該套接字和套接字對應的連接隊列長度告訴 Linux 內核,然後,listen()函數就結束。

    這樣的話,當有一個客戶端主動連接(connect()),Linux 內核就自動完成TCP 三次握手,
    將建立好的鏈接自動存儲到隊列中,如此重複。
    所以,只要 TCP 服務器調用了 listen(),
    客戶端就可以通過 connect() 和服務器建立連接,而這個連接的過程是由內核完成。
    https://i.csdn.net/#/uc/collection-list

  4. accept():
    int accept( int sockfd, struct sockaddr * addr, socklen_t * addrlen)
    sockfd : 套接字描述符(服務器的socket描述符sockfd)
    addr : 用於保存用戶客戶端地址
    addrlen: 地址長度
    返回值 : 1. 成功:建立好連接的套接字描述符 (新的套接字) 2. 出錯:-1

    注意:
    1.返回值的新描述符是已經連接的描述符,表示和客戶端已經連接好了。

    2.addr:指針,指向一緩衝區,其中接收爲通訊層所知的連接實體的地址
    實際參數格式由套接字創建時所產生的地址族決定
    可選,即可置爲 NULL;

    3.addrlen: 指針,輸入參數,配合addr一起使用,指向存有addr地址長度的整形數
    可選,即可置爲 NULL;

    理解: 有人通過你正在監聽listen()的端口連接connect()到你,他的連接將加入等待接受accept()
    的隊列中去,你用accept()告訴他你有資源可以連接,他講返回一個新的描述符。這樣就存在兩個描述符了。
    原來的繼續監聽listen()原來的端口,新的描述符用來接收和發送信息。

    這個新生成的套接字在《計算機網絡自頂向下方法》中P110被稱爲“連接套接字”,原始套接字叫“歡迎套接字”。

  5. send():
    int send( int sockfd, const void * buf, int len, int flags)
    sockfd: 套接字描述符(),這個描述符是accept()的返回值描述符,不是socket()描述符
    buf :發送緩衝區的地址, 其實就是弄個地方緩存一下
    len : 發送數據的長度,不是緩衝區的長度,是實際要發送的長度
    flags : 一般爲 0; <具體爲什麼是0,我目前也還沒弄清楚,弄清楚了再補上>
    目前所知在《TCP IP網絡編程》中描述爲"傳輸數據時用到的多種選項信息",“0代表着不需要設置任何選項”,
    返回值: 1.成功: 實際發送的字節數 2.失敗:-1

  6. recv():
    int recv( int sockfd, void * buf, int len, unsigned int flags)
    sockfd: 套接字描述符,連接套接字
    buf : 存放接收數據的緩衝區
    len : 接收數據的長度
    flags : 一般爲0
    返回值: 1.成功:實際接收的字節數 2.失敗:-1

  7. sendto():
    int snedto( int sockfd, const void * buf, int len, unsigned int flags, const struct sockaddr * to, int tolen)
    sockfd: 套接字描述符
    buf : 發送緩衝區首地址
    len : 發送數據的長度
    flags : 一般爲0,調用方式標誌位,改變數值則改變sendto()方式
    to : 接收方套接字的 IP和端口號
    tolen : 地址長度
    返回值: 1. 實際發送的字節數 2.失敗:-1

  8. recvfrom():
    int recvfrom( int sockfd, void * buf, int len, unsigned int flags, struct sockaddr * from, int * fromlen)
    sockfd: 套接字描述符,accept()返回的新的套接字
    buf : 存放接收數據的緩衝區
    len : 數據長度
    flags : 一般爲 0
    from :發送發的IP地址和端口號信息,如果不需要知道,可設置爲NULL
    fromlen: 地址長度,可設置爲NULL
    返回值: 1. 成功: 實際接收到的字節數 2. 出錯:-1
    recvfrom()的返回值一般不爲0,因爲沒有連接,即使斷開也不會進行提醒。

6. 服務器模型

1、循環服務器
循環服務器,循環的是什麼?

循環服務器模型指服務器依次處理每個客戶端、直到當前客戶端的所有請求處理完畢,再處理下一個客戶端。

所以循環接收、處理、發送,不需要也不是循環綁定、監聽

比如三個人問老師問題,老師是服務器,採取的措施是解決完一號所有問題解決二號的,以此類推

注意:循環中的循環,循環嵌套。外層循環依次提取每個客戶端的連接請求,建立TCP連接。
內層循環接收並處理當前客戶端的所有數據,直到客戶端關閉連接。
如果當前客戶端沒有處理結束,其他客戶端必須一直等待。

循環服務器:

 {
		socket(...); 
		bind(...); 
		listen(...); 
		while(1) 
		{ 
			accept(...);     
			while(1) 
			{          
				recv(...);          
				process(...);          
				send(...);     
			}    
			close(...); 
		}
	}

2、併發服務器
什麼是併發?簡單點說就是在這個時間內有多個程序在這個處理機上運行。

併發服務器的基本思想就是採取多任務機制(多進程/線程),分別爲每個客戶端創建一個任務來處理。

比如三個人同時問老師問題,老師是服務器,需要同時處理三個學生的問題,回答A一個問題回答B一個
並不是 A 所有問題解答結束再解答 B 的問題

注意:這兒需要創建新的進程,在什麼時候進行創建呢?誰去處理事務呢?創建完了什麼時候關閉呢?

服務器端父進程一旦接受客戶端的連接請求,便建立好連接並創建新的子進程。子進程處理客戶端事務。
服務器的多個子進程同時運行,處理多個客戶端。
服務器的父進程並不處理每個具體的數據請求。

併發服務器:

{
		listenfd = socket(...);  
		bind(...);  
		listen(...);
		signal(SIGCHLD, handler);
		while(1)
		{ 
			connfd = accept(...); 
			if ( fork() = = 0) 
			{ 
				close(listenfd);
				while(1) 
				{ 
					recv(...); 
					process(...); 
					send(...); 
				}			  
				close(connd); 
				exit(...); 
			} 
			close(connfd); 
		}
		void handler(int signo){ 
		while (waitpid(-1, NULL, WNOHANG) > 0);  //循環收屍
	}

思考: 一個併發服務器,用多進程和多線程來實現,它們存在主要區別是什麼?

首先你得回憶一下什麼叫進程、線程,有什麼區別。
進程:進程是指一個具有獨立功能的程序在某個數據集合上的一次動態執行過程,是操作系統及進行資源分配和調度的基本單位。
線程:線程是進程內獨立的一條運行路線,也是內核調度的最小單元,也被稱爲輕量級進程
一個進程可包括多個線程

多線程:同一時刻執行多個線程。用瀏覽器一邊下載,一邊聽歌,一邊看網頁。。。
多進程:同時執行多個程序。如,電腦同時運行微信,QQ,各種瀏覽器等任務。

多進程優點:

1、每個進程互相獨立,不影響主程序的穩定性,子進程崩潰沒關係;

2、通過增加CPU,就可以容易擴充性能;

3、可以儘量減少線程加鎖/解鎖的影響,極大提高性能,就算是線程運行的模塊算法效率低也沒關係;

4、每個子進程都有2GB地址空間和相關資源,總體能夠達到的性能上限非常大。

多進程缺點:

1、邏輯控制複雜,需要和主程序交互;

2、需要跨進程邊界,如果有大數據量傳送,就不太好,適合小數據量傳送、密集運算 多進程調度開銷比較大;

3、最好是多進程和多線程結合,即根據實際的需要,每個CPU開啓一個子進程,
這個子進程開啓多線程可以爲若干同類型的數據進行處理。當然你也可以利用多線程+多CPU+輪詢方式來解決問題……

4、方法和手段是多樣的,關鍵是自己看起來實現方便有能夠滿足要求,代價也合適。

多線程:

多線程的優點:

1、無需跨進程邊界;

2、程序邏輯和控制方式簡單;

3、所有線程可以直接共享內存和變量等;

4、線程方式消耗的總資源比進程方式好。

多線程缺點:

1、每個線程與主程序共用地址空間,受限於2GB地址空間;

2、線程之間的同步和加鎖控制比較麻煩;

3、一個線程的崩潰可能影響到整個程序的穩定性;

4、到達一定的線程數程度後,即使再增加CPU也無法提高性能。

5、線程能夠提高的總性能有限,而且線程多了之後,線程本身的調度也是一個麻煩事兒,需要消耗較多的CPU。

7. 常見協議

TCP(Transport Control Protocol)傳輸控制協議
IP(Internetworking Protocol)網間協議
UDP(User Datagram Protocol)用戶數據報協議
ICMP(Internet Control Message Protocol)互聯網控制信息協議
SMTP(Simple Mail Transfer Protocol)簡單郵件傳輸協議
SNMP(Simple Network manage Protocol)簡單網絡管理協議
HTTP(Hypertext Transfer Protocol) 超文本傳輸協議
FTP(File Transfer Protocol)文件傳輸協議
ARP(Address Resolution Protocol)地址解析協議

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章