Linux網路程式設計之高階併發伺服器
在上一節,我們介紹了Linux簡單的併發伺服器,通過在伺服器端建立多個子程序,來接收客戶端的請求,實現併發處理,但這種方式明顯有缺陷,伺服器並不知道客戶端請求的數量,所以事先建立的程序數不好確定。所以,這裡介紹三種高階併發伺服器模式。第一種是伺服器端統一accept,接收客戶端的到來,然後為每個客戶端分配一個程序去處理. 第二種是統一accept接收請求,然後為每個客戶端分配一個執行緒去處理。第三種建立多個執行緒去處理客戶端請求,每個執行緒獨自監聽客戶端的請求。顯然,第一種方案解決了簡單伺服器的併發問題。第二種方案其實是對第一種方案的改進,因為執行緒切換的開銷明顯要小於程序切換的開銷。第三種方案就是原來用程序去處理每個請求,現在換成用執行緒去處理,個人認為改進不是很大.
2. 高階併發伺服器演算法流程
(1)統一accept,多程序
socket(...);
bind(...);
listen(...);
while(1){
accept(...);
fork(...);//子程序
}
close(...);//關閉伺服器套接字
子程序:
recv(...);
process(...);
send(...);
close(...);//關閉客戶端
(2)統一accept,多執行緒
socket(...);
bind(...);
listen(...);
while(1){
accept(...);
pthread_create(....);
}
close(...);//關閉伺服器
執行緒1:
recv(....);
process(....);
send(...);
close(...);//關閉客戶端
(3)accept放入每個執行緒
socket(...);
bind(...);
listen(...);
pthread_create(...);
pthread_join(...);//等待執行緒結束
close(...);//關閉伺服器
執行緒1:
Mutex_lock//互斥鎖
accept(...);
Mutex_unlock(...);
recv(...);
process(...);
send(...);
close(...);//客戶端
3. 相關例子
TCP伺服器:
(1)統一accept多程序
伺服器;
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <time.h>
/**
高階併發伺服器
TCP統一accept
當有客戶端到來時,為每個客戶端建立程序,然後每個程序處理客戶端的請求,動態的建立程序
**/
#define PORT 8888
#define BUFFERSIZE 1024
#define BACKLOG 2
static void handle(int sc){//處理客戶端的請求
char buffer[BUFFERSIZE];
time_t now;
int size;
memset(buffer,0,BUFFERSIZE);
size=recv(sc,buffer,BUFFERSIZE,0);
if(size>0&&!strncmp(buffer,"TIME",4)){//時間伺服器,當客戶端請求時間就把時間傳送給客戶端
memset(buffer,0,BUFFERSIZE);
now=time(NULL);
sprintf(buffer,"%24s\r\n",ctime(&now));
send(sc,buffer,strlen(buffer),0);
}
close(sc);
}
int main(int argc,char*argv[]){
int ret;
int s;
int sc;//用於伺服器與客戶端進行資料傳輸的套接字
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
int len;
len=sizeof(client_addr);
//建立流式套接字
s=socket(AF_INET,SOCK_STREAM,0);
if(s<0){
perror("socket error");
return -1;
}
//將地址結構繫結到套接字描述符上去
memset(&server_addr,0,sizeof(server_addr));
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(PORT);
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
ret=bind(s,(struct sockaddr*)&server_addr,sizeof(server_addr));
if(ret==-1){
perror("bind error");
return -1;
}
ret=listen(s,BACKLOG);
if(ret<0){
perror("listen error");
return -1;
}
while(1){
sc=accept(s,(struct sockaddr*)&client_addr,&len);
if(sc<0){
continue;
}
if(fork()==0){//子程序
handle(sc);
close(s);//子程序關閉用於監聽的套接字
}else{
close(sc);//父程序關閉客戶端套接字
}
}
}
客戶端:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#include <netinet/in.h>
#define PORT 8888
#define BUFFERSIZE 1024
int main(int argc,char*argv[]){
int s;
int ret;
int size;
struct sockaddr_in server_addr;
char buffer[BUFFERSIZE];
s=socket(AF_INET,SOCK_STREAM,0);
if(s<0){
perror("socket error");
return -1;
}
bzero(&server_addr,sizeof(server_addr));
//將地址結構繫結到套接字
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(PORT);
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
//連線伺服器
ret=connect(s,(struct sockaddr*)&server_addr,sizeof(server_addr));
if(ret==-1){
perror("connect error");
return -1;
}
memset(buffer,0,BUFFERSIZE);
strcpy(buffer,"TIME");
size=send(s,buffer,strlen(buffer),0);
if(size<0){
perror("send error");
return -1;
}
memset(buffer,0,BUFFERSIZE);
size=recv(s,buffer,BUFFERSIZE,0);
if(size<0){
perror("recv error");
return;
}
printf("%s",buffer);
close(s);
return 0;
}
(2)統一accept多執行緒
伺服器:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <time.h>
/**
TCP併發伺服器,採用多執行緒,每次客戶端傳送請求,主執行緒建立一個子執行緒,用於處理客戶端的請求
執行緒具有速度快,佔用資源少,資料可以共享等優點 統一accept
**/
#define PORT 8888
#define BUFFERSIZE 1024
#define BACKLOG 2
static void handle(void* sc1){
int sc;
time_t now;
char buffer[BUFFERSIZE];
int size;
sc=*((int*)sc1);//轉換成int指標,然後取值,sc1本身就是一個指標
memset(buffer,0,BUFFERSIZE);
size=recv(sc,buffer,BUFFERSIZE,0);
if(size>0&&!strncmp(buffer,"TIME",4)){//請求伺服器的時間
memset(buffer,0,BUFFERSIZE);//清0
now=time(NULL);
sprintf(buffer,"%24s\r\n",ctime(&now));
send(sc,buffer,strlen(buffer),0);//向客戶端傳送資料
}
close(sc);//關閉客戶端
}
int main(int argc,char*argv[]){
int ret;
int s;
int sc;
int len;
pthread_t thread1;//定義執行緒名
struct sockaddr_in server_addr,client_addr;
len=sizeof(client_addr);
//建立流式套接字
s=socket(AF_INET,SOCK_STREAM,0);
if(s<0){
perror("socket error");
return -1;
}
//將伺服器端的地址結構繫結到套接字描述符
server_addr.sin_family=AF_INET;
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
server_addr.sin_port=htons(PORT);
ret=bind(s,(struct sockaddr*)&server_addr,sizeof(struct sockaddr_in));
if(ret<0){
perror("bind error");
return -1;
}
//監聽
ret=listen(s,BACKLOG);
if(ret<0){
perror("listen error");
return -1;
}
//接收客戶端的請求
for(;;){
sc=accept(s,(struct sockaddr*)&client_addr,&len);
if(sc<0){
continue;
} else {
pthread_create(&thread1,NULL,handle,(void*)&sc);//建立執行緒,讓執行緒去處理,最後一個欄位是傳遞給執行緒處理函式handle的引數
}
}
close(s);
}
客戶端:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#include <netinet/in.h>
#define PORT 8888
#define BUFFERSIZE 1024
int main(int argc,char*argv[]){
int s;
int ret;
int size;
struct sockaddr_in server_addr;
char buffer[BUFFERSIZE];
s=socket(AF_INET,SOCK_STREAM,0);
if(s<0){
perror("socket error");
return -1;
}
bzero(&server_addr,sizeof(server_addr));
//將地址結構繫結到套接字
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(PORT);
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
//連線伺服器
ret=connect(s,(struct sockaddr*)&server_addr,sizeof(server_addr));
if(ret==-1){
perror("connect error");
return -1;
}
memset(buffer,0,BUFFERSIZE);
strcpy(buffer,"TIME");
size=send(s,buffer,strlen(buffer),0);
if(size<0){
perror("send error");
return -1;
}
memset(buffer,0,BUFFERSIZE);
size=recv(s,buffer,BUFFERSIZE,0);
if(size<0){
perror("recv error");
return;
}
printf("%s",buffer);
close(s);
return 0;
}
(3)單獨執行緒accept
伺服器:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <time.h>
#include <pthread.h>
/**
多執行緒TCP併發伺服器
主執行緒建立多個執行緒,然後每個執行緒獨立的accept和進行資料的傳送與接收
多執行緒,獨立accept
**/
#define PORT 8888
#define BUFFERSIZE 1024
#define BACKLOG 2
#define CLIENTNUM 3
static void *handle(void* s1){
int s;
int len;
int sc;
pthread_mutex_t alock=PTHREAD_MUTEX_INITIALIZER;
char buffer[BUFFERSIZE];
int size;
struct sockaddr_in client_addr;
s=*((int*)s1);//得到伺服器端的套接字描述符
//等待客戶端連線
len=sizeof(client_addr);
for(;;){//不停的迴圈等待客戶端的連線
time_t now;
//進入互斥區,每次一個執行緒處理客戶端
pthread_mutex_lock(&alock);
sc=accept(s,(struct sockaddr*)&client_addr,&len);
pthread_mutex_unlock(&alock);
memset(buffer,0,BUFFERSIZE);
size=recv(sc,buffer,BUFFERSIZE,0);
if(size>0&&!strncmp(buffer,"TIME",4)){
memset(buffer,0,BUFFERSIZE);
now=time(NULL);
sprintf(buffer,"%24s\r\n",ctime(&now));
send(sc,buffer,strlen(buffer),0);
}
close(sc);//關閉客戶端
}
}
int main(int argc,char*argv[]){
int ret;
int s;
int len;
int i;
pthread_t thread[CLIENTNUM];
struct sockaddr_in server_addr;
//建立流式套接字
s=socket(AF_INET,SOCK_STREAM,0);
if(s<0){
perror("socket error");
return -1;
}
//將地址結構繫結到套接字上
server_addr.sin_family=AF_INET;
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
server_addr.sin_port=htons(PORT);
ret=bind(s,(struct sockaddr*)&server_addr,sizeof(struct sockaddr_in));
if(ret==-1){
perror("bind error");
return -1;
}
//監聽
ret=listen(s,BACKLOG);
if(ret==-1){
perror("listen error");
return -1;
}
//建立3個執行緒,每個執行緒獨立的accept
for(i=0;i<CLIENTNUM;i++){
pthread_create(&thread[i],NULL,handle,(void*)&s);//執行緒的處理函式為handle,傳遞的引數為套接字描述符s
}
//while(1);
//等待執行緒結束
for(i=0;i<CLIENTNUM;i++){
pthread_join(thread[i],NULL);
}
//關閉套接字
close(s);
return 0;
}
客戶端:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#include <netinet/in.h>
#define PORT 8888
#define BUFFERSIZE 1024
int main(int argc,char*argv[]){
int s;
int ret;
int size;
struct sockaddr_in server_addr;
char buffer[BUFFERSIZE];
s=socket(AF_INET,SOCK_STREAM,0);
if(s<0){
perror("socket error");
return -1;
}
bzero(&server_addr,sizeof(server_addr));
//將地址結構繫結到套接字
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(PORT);
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
//連線伺服器
ret=connect(s,(struct sockaddr*)&server_addr,sizeof(server_addr));
if(ret==-1){
perror("connect error");
return -1;
}
memset(buffer,0,BUFFERSIZE);
strcpy(buffer,"TIME");
size=send(s,buffer,strlen(buffer),0);
if(size<0){
perror("send error");
return -1;
}
memset(buffer,0,BUFFERSIZE);
size=recv(s,buffer,BUFFERSIZE,0);
if(size<0){
perror("recv error");
return;
}
printf("%s",buffer);
close(s);
return 0;
}
總結:
統一accept,多程序伺服器是對簡單併發伺服器的改進,而由於程序的切換開銷比較大,所以又有了統一accept,多執行緒的併發伺服器。而單獨執行緒的accept是完全用執行緒來處理請求。這些都是TCP伺服器,由於UDP是突發的資料流,沒有三次握手,所以伺服器不能檢測到客戶端什麼時候傳送資料。以上三種高階併發伺服器仍然存在著效能問題,下一節介紹的I/O複用的迴圈伺服器是對這三種高階併發伺服器的改進。
相關推薦
Linux網路程式設計之高階併發伺服器(轉)
1. 介紹 在上一節,我們介紹了Linux簡單的併發伺服器,通過在伺服器端建立多個子程序,來接收客戶端的請求,實現併發處理,但這種方式明顯有缺陷,伺服器並不知道客戶端請求的數量,所以事先建立的程序數不好確定。所以,這裡介紹三種高階併發伺服器模式。第一種是伺服器端統一
Linux網路程式設計之高階併發伺服器
在上一節,我們介紹了Linux簡單的併發伺服器,通過在伺服器端建立多個子程序,來接收客戶端的請求,實現併發處理,但這種方式明顯有缺陷,伺服器並不知道客戶端請求的數量,所以事先建立的程序數不好確定。所以,這裡介紹三種高階併發伺服器模式。第一種是伺服器端統一accept,接收
《Linux網路程式設計》: 併發伺服器的三種實現模型
迴圈伺服器與併發伺服器模型 伺服器設計技術有很多,按使用的協議來分有 TCP 伺服器和 UDP 伺服器,按處理方式來分有迴圈伺服器和併發伺服器。 在網路程式裡面,一般來說都是許多客戶對應一個伺服器(多對一),為了處理客戶的請求,對服務端的程式就提出了特殊的要求。 目前最
linux網路程式設計之多程序併發伺服器
1)使用多程序併發伺服器考慮的因素: (1)父程序描述最大檔案描述符的個數(父程序需要關閉accept返回的新檔案描述符) (2)系統內可建立程序的個數(與記憶體大小相關) (3)程序建立過多是否降低整體服務效能 2)多程序建立併發
linux網路程式設計之伺服器
基於tcp協議的網路程式 1.所用函式: socket函式 socket()開啟一個網路埠,如果成功,就像open()一樣返回一個檔案描述符,應用程式可以像讀寫檔案一樣用read/write在網路上首發資料,如果調用出錯返回-1 bind函式:
Linux網路程式設計之I/O複用迴圈伺服器
原文:http://blog.csdn.net/chenjin_zhong/article/details/7270166 1.介紹 在前幾節,我們介紹了迴圈伺服器,併發伺服器. 簡單的迴圈伺服器每次只能處理一個請求,即處理的請求是序列的。而併發伺服器可以通過建立多
linux 網路程式設計之伺服器多執行緒限制
本文討論伺服器端多執行緒併發的操作和限制: 基於實驗結果和百度結果: 實驗基礎:伺服器和客戶端,伺服器為每個客戶端連線開闢執行緒,驗證伺服器多執行緒的最大支援數目 實驗環境:ubuntu 12.04 實驗結果: 1、一切系統預設設定的情況下,最多接收了381個連結,也即
Linux網路程式設計:TCP客戶/伺服器模型及基本socket函式
TCP客戶/伺服器模型 TCP連線的分組交換 在使用socket API的時候應該清楚應用程式和TCP協議棧是如何互動的: 呼叫connect()會發出SYN段(SYN是TCP報文段頭部的一個標誌位,置為1) 阻塞的read()函式返回0就表明收到了FIN段 客戶端呼叫c
linux 網路程式設計之廣播
linux 網路程式設計之廣播 轉載:https://blog.csdn.net/qdlovecsj/article/details/8805483 廣播方式主要是指使用UDP套介面傳送資料,傳送資料的目標地址不是普通的地址,而是所指定網路的廣播地址。 什麼是廣播地址?是指IP地
Linux網路程式設計之TCP程式設計
直接上程式碼如下所示: 1、標頭檔案定義 #ifndef __HEAD_NET_H__ #define __HEAD_NET_H__ #include <stdio.h> #include <string.h> #include <stdlib.h>
Linux網路程式設計之IO模型
本文基於IO訪問中存在的兩個階段詳細介紹了Linux產生的五種IO模型。 上篇文章回顧: 小米開源監控Open-Falcon收錄汽車之家貢獻的Win版Agent 同步與非同步 同步是指一個任務的完成需要依賴另外一個任務時,只有等待被依賴的任務完成後
linux網路程式設計之TCP狀態轉換及埠複用
(1)TCP狀態轉換圖 其中圖中分為三種狀態:實線代表的主動發起連線,虛線代表的被動發起連線,細實線代表的可以雙向發起連線的狀態。 主動發起連線方狀態變化:1)主動發起連線的一方傳送SYN標誌位,進入SYN_SENT狀態,等待接收被髮起連線方
嵌入式Linux網路程式設計,UDP迴圈伺服器,sendto(),recvfrom()
文章目錄 1,UDP迴圈伺服器模型: 2,UDP的使用場景 3,UDP資料傳送和接受sendto()、recvfrom() 4,UDP迴圈伺服器示例(可同時連線多個客戶端) 4.1,標頭檔案 net.h 4.2,客戶端程式碼cl
linux網路程式設計之TCP介面詳解
socket int socket(int domain, int type, intprotocol); 監聽套接字描述符由socket建立,隨後用作bind和listen的第一個引數。一個伺服器通常僅建立一個監聽套接字,他在該伺服器的生命週期內一直存在。 c
linux網路程式設計之用socket實現簡單客戶端和服務端的通訊(基於TCP)
一、介紹基於TCP協議通過socket實現網路程式設計常用API 1、讀者如果不是很熟悉,可以先看我之前寫的幾篇部落格,有socket,地址結構的理解,更加方便讀者理解 地址分別是: 2、socket(TCP)程式設計API簡介 1)、socket int s
linux網路程式設計之RTP協議
以下內容取自: 本機通訊:https://www.cnblogs.com/lidabo/p/4160138.html(RTP協議傳輸)https://www.cnblogs.com/lidabo/p/4160145.html(RTP協議傳輸) 非本機:http://velep.com/arc
linux網路程式設計之用socket實現簡單客戶端和服務端的通訊(基於UDP)
1、sendto和recvfrom函式介紹 sendto(經socket傳送資料) 相關函式 send , sendmsg,recv , recvfrom , socket 表頭檔案 #include < sys/types.h >#includ
Linux網路程式設計 之 大小端初探
首先解釋一下大小端的概念。 大端(Big Endian),同時也是網路序,是資料在網路上傳輸的一種資料組織格式,其儲存的方式比較符合人們讀寫的習慣。 小端(Little Endian
網路程式設計之:TCP伺服器的簡單實現
說到TCP伺服器,就不得不提socket程式設計,我們知道,在TCP/IP協議中,“IP地址+TCP或UDP埠號”唯一標識⽹絡通訊中的唯一一個程序,“IP地址+埠號”就稱為socket。 在TCP協
網路程式設計中設計併發伺服器,使用多程序與多執行緒有什麼區別?
網路程式設計中設計併發伺服器,使用多程序與多執行緒,請問有什麼區別? 答案一: 1,程序:子程序是父程序的複製品。子程序獲得父程序資料空間、堆和棧的複製品。 2,執行緒:相對與程序而言,執行緒是一個更加接近與執行體的概念,它可以與同進程的其他執行緒共享資料,但擁有自己的棧空