亚洲韩日午夜视频,欧美日韩在线精品一区二区三区,韩国超清无码一区二区三区,亚洲国产成人影院播放,久草新在线,在线看片AV色

您好,歡迎來到思海網絡,我們將竭誠為您提供優質的服務! 誠征網絡推廣 | 網站備案 | 幫助中心 | 軟件下載 | 購買流程 | 付款方式 | 聯系我們 [ 會員登錄/注冊 ]
促銷推廣
客服中心
業務咨詢
有事點擊這里…  531199185
有事點擊這里…  61352289
點擊這里給我發消息  81721488
有事點擊這里…  376585780
有事點擊這里…  872642803
有事點擊這里…  459248018
有事點擊這里…  61352288
有事點擊這里…  380791050
技術支持
有事點擊這里…  714236853
有事點擊這里…  719304487
有事點擊這里…  1208894568
有事點擊這里…  61352289
在線客服
有事點擊這里…  531199185
有事點擊這里…  61352288
有事點擊這里…  983054746
有事點擊這里…  893984210
當前位置:首頁 >> 技術文章 >> 文章瀏覽
技術文章

Linux系統下實現多線程客戶/服務器

添加時間:2011-4-13  添加: admin 
 

在傳統的Unix模型中,當一個進程需要由另一個實體執行某件事時,該進程派生(fork)一個子進程,讓子進程去進行處理。Unix下的大多數網絡服務器程序都是這么編寫的,即父進程接受連接,派生子進程,子進程處理與客戶的交互。

雖然這種模型很多年來使用得很好,但是fork時有一些問題:

1. fork是昂貴的。內存映像要從父進程拷貝到子進程,所有描述字要在子進程中復制等等。目前有的Unix實現使用一種叫做寫時拷貝(copy-on-write)的技術,可避免父進程數據空間向子進程的拷貝。盡管有這種優化技術,fork仍然是昂貴的。

2. fork子進程后,需要用進程間通信(IPC)在父子進程之間傳遞信息。Fork之前的信息容易傳遞,因為子進程從一開始就有父進程數據空間及所有描述字的拷貝。但是從子進程返回信息給父進程需要做更多的工作。

線程有助于解決這兩個問題。線程有時被稱為輕權進程(lightweight process),因為線程比進程“輕權”,一般來說,創建一個線程要比創建一個進程快10~100倍。

一個進程中的所有線程共享相同的全局內存,這使得線程很容易共享信息,但是這種簡易性也帶來了同步問題。

一個進程中的所有線程不僅共享全局變量,而且共享:進程指令、大多數數據、打開的文件(如描述字)、信號處理程序和信號處置、當前工作目錄、用戶ID和組ID。但是每個線程有自己的線程ID、寄存器集合(包括程序計數器和棧指針)、棧(用于存放局部變量和返回地址)、error、信號掩碼、優先級。在Linux中線程編程符合Posix.1標準,稱為Pthreads。所有的pthread函數都以pthread_開頭。以下先講述5個基本線程函數,在調用它們前均要包括pthread.h頭文件。然后再給出用它們編寫的一個TCP客戶/服務器程序例子。

第一個函數:

int pthread_create 
(pthread_t *tid,const pthread_attr_t *attr,void *
(*func)(void *),void *arg);

一個進程中的每個線程都由一個線程ID(thread ID)標識,其數據類型是pthread_t(常常是unsigned int)。如果新的線程創建成功,其ID將通過tid指針返回。

每個線程都有很多屬性:優先級、起始棧大小、是否應該是一個守護線程等等,當創建線程時,我們可通過初始化一個pthread_attr_t變量說明這些屬性以覆蓋缺省值。我們通常使用缺省值,在這種情況下,我們將attr參數說明為空指針。

最后,當創建一個線程時,我們要說明一個它將執行的函數。線程以調用該函數開始,然后或者顯式地終止(調用pthread_exit)或者隱式地終止(讓該函數返回)。函數的地址由func參數指定,該函數的調用參數是一個指針arg,如果我們需要多個調用參數,我們必須將它們打包成一個結構,然后將其地址當作唯一的參數傳遞給起始函數。

在func和arg的聲明中,func函數取一個通用指針(void *)參數,并返回一個通用指針(void *),這就使得我們可以傳遞一個指針(指向任何我們想要指向的東西)給線程,由線程返回一個指針(同樣指向任何我們想要指向的東西)。調用成功,返回0,出錯時返回正Exxx值。Pthread函數不設置errno。

第二個函數:

int pthread_join(pthread_t tid,void **status);

該函數等待一個線程終止。把線程和進程相比,pthread_creat類似于fork,而pthread_join類似于waitpid。我們必須要等待線程的tid,很可惜,我們沒有辦法等待任意一個線程結束。如果status指針非空,線程的返回值(一個指向某個對象的指針)將存放在status指向的位置。

第三個函數:

pthread_t pthread_self(void);

線程都有一個ID以在給定的進程內標識自己。線程ID由pthread_creat返回,我們可以pthread_self取得自己的線程ID。

第四個函數:

int pthread_detach(pthread_t tid);

 

線程或者是可匯合的(joinable)或者是脫離的(detached)。當可匯合的線程終止時,其線程ID和退出狀態將保留,直到另外一個線程調用pthread_join。脫離的線程則像守護進程:當它終止時,所有的資源都釋放,我們不能等待它終止。如果一個線程需要知道另一個線程什么時候終止,最好保留第二個線程的可匯合性。Pthread_detach函數將指定的線程變為脫離的。該函數通常被想脫離自己的線程調用,如:pthread_detach (pthread_self ( ));

第五個函數:

void pthread_exit(void *status);

該函數終止線程。如果線程未脫離,其線程ID和退出狀態將一直保留到調用進程中的某個其他線程調用pthread_join函數。指針status不能指向局部于調用線程的對象,因為線程終止時這些對象也消失。有兩種其他方法可使線程終止:

1. 啟動線程的函數(pthread_creat的第3個參數)返回。既然該函數必須說明為返回一個void指針,該返回值便是線程的終止狀態。

2. 如果進程的main函數返回或者任何線程調用了exit,進程將終止,線程將隨之終止。

以下給出一個使用線程的TCP回射客戶/服務器的例子,完成的功能是客戶端使用線程給服務器發從標準輸入得到的字符,并在主線程中將從服務器端返回的字符顯示到標準輸出,服務器端將客戶端發來的數據原樣返回給客戶端,每一個客戶在服務器上對應一個線程。利用該程序框架,通過擴展客戶端和服務器端的處理功能,可以完成多種基于多線程的客戶機/服務器程序。該程序在RedHat 6.0和TurboLinux4.02下調試通過。 共用頭文件如下:(head.h)

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define MAXLINE 1024 
#define SERV_PORT 8000 
#define LISTENQ 1024 
static int sockfd; 
static FILE *fp;

公用函數如下(common.c):

/* 從一個描述字讀文本行 */ 
ssize_t readline(int fd, void *vptr, size_t maxlen) 
{ 
ssize_t n, rc; 
char c, *ptr; 
for (n=1; n0) 
{ 
if ( (nwritten=write (fd, ptr, nleft ) )<=0 ) 
{ 
if (errno==EINTR ) 
nwritten=0; 
else 
return (-1); 
} 
nleft-=nwritten; 
ptr++=nwritten; 
}

客戶端主程序如下:(client.c)

#include “head.h"; 
#include “common.c"; 
/* 在str_cli中定義的要被線程執行的函數 */ 
void *copyto (void *arg) 
{ 
char sendline[MAXLINE]; 
while (fgets (sendline,MAXLINE,fp) !=NULL ) 
writen(sockfd,sendline,strlen(sendline)); 
shutdown(sockfd,SHUT_WR); 
return(NULL); 
} 

void str_cli(FILE *fp_arg, int sockfd_arg) 
{ 
char recvline[MAXLINE]; 
pthread_t tid; 
sockfd=sockfd_arg; 
fp=fp_arg; 
pthread_creat(&tid, NULL, copyto, NULL); 
while (readline (sockfd,recvline,MAXLINE) >0) 
---- fputs(recvline,stdout); 
} 

int main ( int argc, char **argv ) 
{ 
int sockfd; 
struct sockaddr_in servaddr; 
if (argc!=2 ) 
printf ( “ usage: tcpcli " ); 
exit(0); 
bzero(&servaddr, sizeof (servaddr)) ; 
servaddr.sin_family=AF_INET; 
servaddr.sinport=htons(SERV_PORT); 
inet_pton (AF_INET, argv[1], &servaddr.sin_addr ); 
connect (sockfd, (struct sockaddr *)&servaddr, 
siziof (servaddr ) ); 
str_cli (stdin, sockfd ); 
exit (0 ); 
}

服務器端主程序如下:

#include “head.h"; 
#include “common.c"; 
void str_echo (int sockfd ) 
{ 
ssize_t n; 
char line[MAXLINE]; 
for (; ; ) 
{ 
if ( (n=readline (sockfd, line, MAXLINE) )==0) 
return; 
writen (sockfd, line, n); 
} 
} 

static void *doit ( void *arg) 
{ 
pthread_detach(pthread_self ( ) ); 
str_echo ( (int ) arg ); 
close ( (int ) arg ); 
return ( NULL ) ; 
} 

int main ( int argc, char **argv ) 
{ 
int listenfd, connfd; 
socklen_t addrlen,len; 
struct sockaddr_in cliaddr, servaddr; 
pthread_t tid; 
listenfd=socket (AF_INET, SOCK_STREAM, 0 ); 
bzero (&servaddr, sizeof (servaddr ) ); 
servaddr.sin_family=AF_INET; 
servaddr.sin_addr.s_addr=htonl (INADDR_ANY ); 
servaddr.sin_port=SERV_PORT; 
bind (listenfd, ( struct sockaddr * )&servaddr, sizeof 
(servaddr ) ); 
listen (listenfd, LISTENQ ); 
addrlen=sizeof ( cliaddr ); 
cliaddr=malloc(addrlen ); 
for ( ; ; ) 
{ 
len=addrlen; 
connfd=accept(listenfd, (struct sockaddr * )&cliaddr, &len ); 
pthread_creat ( &tid, NULL, &doit, ( void * )connfd ); 
} 
}
關鍵字:服務器、多線程、系統
分享到:

頂部 】 【 關閉
版權所有:佛山思海電腦網絡有限公司 ©1998-2024 All Rights Reserved.
聯系電話:(0757)22630313、22633833
中華人民共和國增值電信業務經營許可證: 粵B1.B2-20030321 備案號:粵B2-20030321-1
網站公安備案編號:44060602000007 交互式欄目專項備案編號:200303DD003  
察察 工商 網安 舉報有獎  警警  手機打開網站