1. 程式人生 > >Linux下TCP網路伺服器實現原始碼

Linux下TCP網路伺服器實現原始碼

大家都知道各類網路伺服器程式的編寫步驟,並且都知道網路伺服器就兩大類:迴圈服務和併發服務。這裡附上原始碼來個小結吧。

首先,迴圈網路伺服器程式設計實現的步驟是這樣的:

這種伺服器模型是典型迴圈服務,如果不加上多程序/執行緒技術,此種服務吞吐量有限,大家都可以看到,如果前一個連線服務資料沒有收發完畢後面的連線沒辦法處理。所以一般有多程序技術,對一個新連線啟用一個新程序去處理,而監聽socket繼續監聽。

/************關於本文件********************************************
*filename:Linux下各類TCP網路伺服器的實現原始碼
*purpose:記錄Linux下各類tcp服務程式原始碼
*wroteby:zhoulifa(

[email protected])周立發(http://zhoulifa.bokee.com)
Linux愛好者Linux知識傳播者SOHO族開發者最擅長C語言
*datetime:2006-07-0422:00:00
*Note:任何人可以任意複製程式碼並運用這些文件,當然包括你的商業用途
*但請遵循GPL
*Hope:希望越來越多的人貢獻自己的力量,為科學技術發展出力
*********************************************************************/

一個迴圈TCP服務原始碼(因為用fork進行多程序服務了,所以這種服務現實中也有用)如下:

/*----------------------原始碼開始--------------------------------------------*/
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<sys/wait.h>
/*********************************************************************
*filename:cycletcpserver.c
*purpose:迴圈tcp服務端程式
*tidiedby:zhoulifa(
[email protected]
)周立發(http://zhoulifa.bokee.com)
Linux愛好者Linux知識傳播者SOHO族開發者最擅長C語言
*datetime:2006-07-0422:00:00
*Note:任何人可以任意複製程式碼並運用這些文件,當然包括你的商業用途
*但請遵循GPL
*Thanksto:Google.com
*********************************************************************/
intmain(intargc,char**argv)
{
intsockfd,new_fd;/*監聽socket:sock_fd,資料傳輸socket:new_fd*/
structsockaddr_inmy_addr;/*本機地址資訊*/
structsockaddr_intheir_addr;/*客戶地址資訊*/
unsignedintsin_size,myport,lisnum;

if(argv[1])myport=atoi(argv[1]);
elsemyport=7838;

if(argv[2])lisnum=atoi(argv[2]);
elselisnum=2;

if((sockfd=socket(PF_INET,SOCK_STREAM,0))==-1){
perror("socket");
exit(1);
}
my_addr.sin_family=PF_INET;
my_addr.sin_port=htons(myport);
my_addr.sin_addr.s_addr=INADDR_ANY;
bzero(&(my_addr.sin_zero),0);
if(bind(sockfd,(structsockaddr*)&my_addr,sizeof(structsockaddr))==-1){
perror("bind");
exit(1);
}

if(listen(sockfd,lisnum)==-1){
perror("listen");
exit(1);
}
while(1){
sin_size=sizeof(structsockaddr_in);
if((new_fd=accept(sockfd,(structsockaddr*)&their_addr,&sin_size))==-1){
perror("accept");
continue;
}
printf("server:gotconnectionfrom%s\n",inet_ntoa(their_addr.sin_addr));
if(!fork()){/*子程序程式碼段*/
if(send(new_fd,"Hello,world!\n",14,0)==-1){
perror("send");
close(new_fd);
exit(0);
}
}
close(new_fd);/*父程序不再需要該socket*/
waitpid(-1,NULL,WNOHANG);/*等待子程序結束,清除子程序所佔用資源*/
}
}
/*----------------------原始碼結束--------------------------------------------*/

一個測試客戶端程式碼如下:

/*----------------------原始碼開始--------------------------------------------*/
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<netdb.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<sys/socket.h>
#defineMAXDATASIZE100/*每次最大資料傳輸量*/
/*********************************************************************
*filename:cycletcpclient.c
*purpose:迴圈tcp客戶端程式
*tidiedby:zhoulifa(
[email protected]
)周立發(http://zhoulifa.bokee.com)
Linux愛好者Linux知識傳播者SOHO族開發者最擅長C語言
*datetime:2006-07-0422:20:00
*Note:任何人可以任意複製程式碼並運用這些文件,當然包括你的商業用途
*但請遵循GPL
*Thanksto:Google.com
*Hope:希望越來越多的人貢獻自己的力量,為科學技術發展出力
*********************************************************************/

intmain(intargc,char*argv[])
{
intsockfd,numbytes;
charbuf[MAXDATASIZE];
structhostent*he;
structsockaddr_intheir_addr;
unsignedintmyport;

if(argv[2])myport=atoi(argv[2]);
elsemyport=7838;

if(argc!=3){
fprintf(stderr,"usage:%shostnameport\n",argv[0]);
exit(1);
}
if((he=gethostbyname(argv[1]))==NULL){
herror("gethostbyname");
exit(1);
}
if((sockfd=socket(PF_INET,SOCK_STREAM,0))==-1){
perror("socket");
exit(1);
}
their_addr.sin_family=PF_INET;
their_addr.sin_port=htons(myport);
their_addr.sin_addr=*((structin_addr*)he->h_addr);
bzero(&(their_addr.sin_zero),0);
if(connect(sockfd,(structsockaddr*)&their_addr,sizeof(structsockaddr))==-1){
perror("connect");
exit(1);
}
if((numbytes=recv(sockfd,buf,MAXDATASIZE,0))==-1){
perror("recv");
exit(1);
}
buf[numbytes]=0;
printf("Received:%s\n",buf);
close(sockfd);
return0;
}
/*----------------------原始碼結束--------------------------------------------*/

用gcccycletcpserver.c-otcpserver和gcccycletcpclient.c-otcpclient分別編譯上述程式碼後執行情況如下:
服務端執行顯示:

[email protected]:/data/example/c$./tcpserver
server:gotconnectionfrom127.0.0.1
server:gotconnectionfrom127.0.0.1
server:gotconnectionfrom127.0.0.1

客戶端執行顯示:

[email protected]:/data/example/c$./tcpclient127.0.0.17838
Received:Hello,world!

[email protected]:/data/example/c$./tcpclient127.0.0.17838
Received:Hello,world!

[email protected]:/data/example/c$./tcpclient127.0.0.17838
Received:Hello,world!

#p#分頁標題#e#


不得不說的一個概念性問題:阻塞與非阻塞
在阻塞服務中,當伺服器執行到accept語句而沒有客戶連線服務請求到來,那麼會發生什麼情況?這時伺服器就會停止在accept語句上等待連線服務請求的到來;同樣,當程式執行到接收資料語句recv時,如果沒有資料可以讀取,則程式同樣會停止在接收語句上。這種情況稱為阻塞(blocking)。
但如果你希望伺服器僅僅注意檢查是否有客戶在等待連線,有就接受連線;否則就繼續做其他事情,則可以通過將socket設定為非阻塞方式來實現:非阻塞socket在沒有客戶在等待時就使accept呼叫立即返回。
通過設定socket為非阻塞方式,可以實現“輪詢”若干socket。當企圖從一個沒有資料等待處理的非阻塞socket讀入資料時,函式將立即返回,並且返回值置為-1,並且errno置為EWOULDBLOCK。但是這種“輪詢”會使CPU處於忙等待方式,從而降低效能。考慮到這種情況,假設你希望伺服器監聽連線服務請求的同時從已經建立的連線讀取資料,你也許會想到用一個accept語句和多個recv()語句,但是由於accept及recv都是會阻塞的,所以這個想法顯然不會成功。
呼叫非阻塞的socket會大大地浪費系統資源。而呼叫select()會有效地解決這個問題,它允許你把程序本身掛起來,而同時使系統核心監聽所要求的一組檔案描述符的任何活動,只要確認在任何被監控的檔案描述符上出現活動,select()呼叫將返回指示該檔案描述符已準備好的資訊,從而實現了為程序選出隨機的變化,而不必由程序本身對輸入進行測試而浪費CPU開銷。

其次,併發伺服器,在上述cycletcpserver.c中,由於使用了fork技術也可以稱之為併發伺服器,但這種伺服器並不是真正意義上的IO多路複用的併發伺服器,並且由於沒有處理阻塞問題,實際應用有各種各樣的問題。

一個典型IO多路複用的單程序併發伺服器流程如下:
/*IO多路複用併發服務流程圖*/

下面是一個演示IO多路複用的源程式,是一個埠轉發程式,但它的用處相當大,實際應用中的各類代理軟體或埠對映軟體都是基於這樣的程式碼的,比如Windows下的WinGate、WinProxy等都是在此基礎上實現的。原始碼如下:

/*----------------------原始碼開始--------------------------------------------*/
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<sys/time.h>
#include<sys/types.h>
#include<string.h>
#include<signal.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<errno.h>

staticintforward_port;

#undefmax
#definemax(x,y)((x)>(y)?(x):(y))

/*************************關於本文件************************************
*filename:tcpforwardport.c
*purpose:演示了select的用法,這是一個極好的代理軟體核心,專門作埠對映用
*tidiedby:zhoulifa([email protected])周立發(http://zhoulifa.bokee.com)
Linux愛好者Linux知識傳播者SOHO族開發者最擅長C語言
*datetime:2006-07-0519:00:00
*Note:任何人可以任意複製程式碼並運用這些文件,當然包括你的商業用途
*但請遵循GPL
*Thanksto:PaulSheer感謝PaulSheer在select_tut的man手冊裡提供了這份原始碼
*Hope:希望越來越多的人貢獻自己的力量,為科學技術發展出力
*********************************************************************/

staticintlisten_socket(intlisten_port){
structsockaddr_ina;
ints;
intyes;
if((s=socket(AF_INET,SOCK_STREAM,0))<0){
perror("socket");
return-1;
}
yes=1;
if(setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char*)&yes,sizeof(yes))<
0){
perror("setsockopt");
close(s);
return-1;
}
memset(&a,0,sizeof(a));
a.sin_port=htons(listen_port);
a.sin_family=AF_INET;
if(bind(s,(structsockaddr*)&a,sizeof(a))<0){
perror("bind");
close(s);
return-1;
}
printf("acceptingconnectionsonport%d\n",(int)listen_port);
listen(s,10);
returns;
}

staticintconnect_socket(intconnect_port,char*address){
structsockaddr_ina;
ints;
if((s=socket(AF_INET,SOCK_STREAM,0))<0){
perror("socket");
close(s);
return-1;
}

memset(&a,0,sizeof(a));
a.sin_port=htons(connect_port);
a.sin_family=AF_INET;

if(!inet_aton(address,(structin_addr*)&a.sin_addr.s_addr)){
perror("badIPaddressformat");
close(s);
return-1;
}

if(connect(s,(structsockaddr*)&a,sizeof(a))<0){
perror("connect()");
shutdown(s,SHUT_RDWR);
close(s);
return-1;
}
returns;
}

#defineSHUT_FD1{\
if(fd1>=0){\
shutdown(fd1,SHUT_RDWR);\
close(fd1);\
fd1=-1;\
}\
}

#defineSHUT_FD2{\
if(fd2>=0){\
shutdown(fd2,SHUT_RDWR);\
close(fd2);\
fd2=-1;\
}\
}

#defineBUF_SIZE1024

intmain(intargc,char**argv){
inth;
intfd1=-1,fd2=-1;
charbuf1[BUF_SIZE],buf2[BUF_SIZE];
intbuf1_avail,buf1_written;
intbuf2_avail,buf2_written;

if(argc!=4){
fprintf(stderr,"Usage\n\tfwd\n");
exit(1);
}

signal(SIGPIPE,SIG_IGN);

forward_port=atoi(argv[2]);

/*建立監聽socket*/
h=listen_socket(atoi(argv[1]));
if(h<0)exit(1);

for(;;){
intr,nfds=0;
fd_setrd,wr,er;
FD_ZERO(&rd);
FD_ZERO(&wr);
FD_ZERO(&er);
FD_SET(h,&rd);

/*把監聽socket和可讀socket三個一起放入select的可讀控制代碼列表裡*/
nfds=max(nfds,h);
if(fd1>0&&buf1_avail<BUF_SIZE){
FD_SET(fd1,&rd);
nfds=max(nfds,fd1);
}
if(fd2>0&&buf2_avail<BUF_SIZE){
FD_SET(fd2,&rd);
nfds=max(nfds,fd2);
}

/*把可寫socket兩個一起放入select的可寫控制代碼列表裡*/
if(fd1>0&&buf2_avail-buf2_written>0){
FD_SET(fd1,&wr);
nfds=max(nfds,fd1);
}
if(fd2>0&&buf1_avail-buf1_written>0){
FD_SET(fd2,&wr);
nfds=max(nfds,fd2);
}

/*把有異常資料的socket兩個一起放入select的異常控制代碼列表裡*/
if(fd1>0){
FD_SET(fd1,&er);
nfds=max(nfds,fd1);
}
if(fd2>0){
FD_SET(fd2,&er);
nfds=max(nfds,fd2);
}

/*開始select*/
r=select(nfds+1,&rd,&wr,&er,NULL);

if(r==-1&&errno==EINTR)continue;
if(r<0){
perror("select()");
exit(1);
}

/*處理新連線*/
if(FD_ISSET(h,&rd)){
unsignedintl;
structsockaddr_inclient_address;
memset(&client_address,0,l=sizeof(client_address));
r=accept(h,(structsockaddr*)&client_address,&l);
if(r<0){
perror("accept()");
}else{
/*關閉原有連線,把新連線作為fd1,同時連線新的目標fd2*/
SHUT_FD1;
SHUT_FD2;
buf1_avail=buf1_written=0;
buf2_avail=buf2_written=0;
fd1=r;
fd2=connect_socket(forward_port,argv[3]);
if(fd2<0){
SHUT_FD1;
}else
printf("connectfrom%s\n",inet_ntoa(client_address.sin_addr));
}
}

/*NB:readoobdatabeforenormalreads*/
if(fd1>0)
if(FD_ISSET(fd1,&er)){
charc;
errno=0;
r=recv(fd1,&c,1,MSG_OOB);
if(r<1){
SHUT_FD1;
}else
send(fd2,&c,1,MSG_OOB);
}

if(fd2>0)
if(FD_ISSET(fd2,&er)){
charc;
errno=0;
r=recv(fd2,&c,1,MSG_OOB);
if(r<1){
SHUT_FD1;
}else
send(fd1,&c,1,MSG_OOB);
}

/*NB:readdatafromfd1*/
if(fd1>0)
if(FD_ISSET(fd1,&rd)){
r=read(fd1,buf1+buf1_avail,BUF_SIZE-buf1_avail);
if(r<1){
SHUT_FD1;
}else
buf1_avail+=r;
}

/*NB:readdatafromfd2*/
if(fd2>0)
if(FD_ISSET(fd2,&rd)){
r=read(fd2,buf2+buf2_avail,BUF_SIZE-buf2_avail);
if(r<1){
SHUT_FD2;
}else
buf2_avail+=r;
}

/*NB:writedatatofd1*/
if(fd1>0)
if(FD_ISSET(fd1,&wr)){
r=write(fd1,buf2+buf2_written,buf2_avail-buf2_written);
if(r<1){
SHUT_FD1;
}else
buf2_written+=r;
}

/*NB:writedatatofd1*/
if(fd2>0)
if(FD_ISSET(fd2,&wr)){
r=write(fd2,buf1+buf1_written,buf1_avail-buf1_written);
if(r<1){
SHUT_FD2;
}else
buf1_written+=r;
}

/*checkifwritedatahascaughtreaddata*/
if(buf1_written==buf1_avail)buf1_written=buf1_avail=0;
if(buf2_written==buf2_avail)buf2_written=buf2_avail=0;

/*onesidehasclosedtheconnection,keepwritingtotheothersideuntilempty*/
if(fd1<0&&buf1_avail-buf1_written==0){
SHUT_FD2;
}
if(fd2<0&&buf2_avail-buf2_written==0){
SHUT_FD1;
}
}
return0;
}
/*----------------------原始碼結束--------------------------------------------*/

#p#分頁標題#e#


用gcctcpforwardport.c-oMyProxy編譯此程式後執行效果如下:

當有使用者訪問本機的8000埠時,MyProxy程式將把此請求轉發到172.16.100.218主機的80埠,即實現了一個http代理。

關於select函式:
其函式原型為:
intselect(intn,fd_set*readfds,fd_set*writefds,fd_set*exceptfds,structtimeval*timeout);
此函式的功能是由核心檢測在timeout時間內,是否有readfds,writefds,exceptfds三個控制代碼集(filedescriptors)裡的某個控制代碼(filedescriptor)的狀態符合尋求,即readfds控制代碼集裡有控制代碼可讀或writefds控制代碼集裡有可寫或exceptfds控制代碼集裡有例外發生,任何一個有變化函式就立即返回,返回值為timeout發生狀態變化的控制代碼個數。
n是所有readfds,writefds,exceptfds三個控制代碼集(filedescriptors)裡編號最大值加1。比如:要檢測兩個socket控制代碼fd1和fd2在timeout時間內是否分別可讀和可寫就可以這樣:
先把兩個控制代碼集(filedescriptors)清零:
FD_ZERO(&readfds);
FD_ZERO(&writefds);
然後把fd1加入讀檢測集:
FD_SET(fd1,&readfds);
然後把fd2加入寫檢測集:
FD_SET(fd2,&writefds);
再給timeout設定值,timeout是這樣的一個結構:
structtimeval{
longtv_sec;/*seconds*/
longtv_usec;/*microseconds*/
};
你可以這樣賦值:
timeout.tv_sec=1;
timeout.tv_uec=0;
表示檢測在1秒鐘內是否有控制代碼狀態發生變化。
如果有控制代碼發生變化,就可以用FD_ISSET檢測各個控制代碼,比如:
FD_ISSET(fd1,&readfds);//檢測是否fd1變成可讀的了
FD_ISSET(fd2,&writefds);//檢測是否fd2變成可寫的了
示意程式程式碼如下:
/*----------------------示意程式碼開始--------------------------------------------*/
fd1=socket();//建立一個socket
fd2=socket();//建立一個socket
while(1){
FD_ZERO(&readfds);
FD_ZERO(&writefds);
FD_SET(fd1,&readfds);
FD_SET(fd2,&writefds);
timeout.tv_sec=1;
timeout.tv_uec=0;
ret=select(fd1>fd2?(fd1+1):(fd2+1),&readfds,&writefds,NULL,&timeout);
if(ret<0){printf("系統錯誤,select出錯,錯誤程式碼:%d,錯誤資訊:%s",errno,strerror(errno));}
elseif(ret==0){printf("select超時返回,沒有任何控制代碼狀態發生變化!");}
//有控制代碼狀態發生了變化
if(FD_ISSET(fd1,&readfds)){
fd1有資料可讀;
fd1裡的資料被讀出來;
}
if(FD_ISSET(fd2,&writefds)){
fd2可寫;
fd2裡傳送資料給對方;
}
}
/*----------------------示意程式碼結束--------------------------------------------*/

經常用到的幾個自定義函式:
1、開啟監聽的函式

/*----------------------原始碼程式碼開始--------------------------------------------*/
int
OpenSCPServer(intport,inttotal,intsendbuflen,intrecvbuflen,intblockORnot,intreuseORnot){
/*************************關於本函式************************************
*function_name:OpenSCPServer
*引數說明:port整數型監聽埠號,total整數型監聽個數,sendbuflen整數型傳送緩衝區大小
*recvbuflen整數型接收緩衝區大小,blockORnot整數型是否阻塞,reuseORnot整數型是否埠重用
*purpose:用來建立一個tcp服務端socket
*tidiedby:zhoulifa([email protected])周立發(http://zhoulifa.bokee.com)
Linux愛好者Linux知識傳播者SOHO族開發者最擅長C語言
*datetime:2006-07-0520:00:00
*Note:任何人可以任意複製程式碼並運用這些文件,當然包括你的商業用途
*但請遵循GPL
*Thanksto:PaulSheer感謝PaulSheer在select_tut的man手冊裡提供了這份原始碼
*Hope:希望越來越多的人貢獻自己的力量,為科學技術發展出力
*Note:要使用此函式需要自定義一個全域性變數charerrorMessage[1024];幷包含GetCurrentTime.h標頭檔案
*********************************************************************/
intsockfd=0,ret=0,opt=0,flags=1;
structsockaddr_inladdr;

ret=sockfd=socket(PF_INET,SOCK_STREAM,0);
if(ret<0){
sprintf(errorMessage,"OpenTCPServersocket()error!return:%d,errno=%d,errortext:'%s'%s",ret,errno,strerror(errno),GetCurrentTime(0,0));
return-1;
}

ret=setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&reuseORnot,sizeof(int));
if(ret<0){
sprintf(errorMessage,"OpenTCPServersetsockopt()reuseerror!return:%d,errno=%d,errortext:'%s'%s",ret,errno,strerror(errno),GetCurrentTime(0,0));
return-2;
}

ret=setsockopt(sockfd,SOL_SOCKET,SO_RCVBUF,&recvbuflen,sizeof(int));
if(ret<0){
sprintf(errorMessage,"OpenTCPServersetsockopt()recvbuferror!return:%d,errno=%d,errortext:'%s'%s",ret,errno,strerror(errno),GetCurrentTime(0,0));
return-3;
}

ret=setsockopt(sockfd,SOL_SOCKET,SO_SNDBUF,&sendbuflen,sizeof(int));
if(ret<0){
sprintf(errorMessage,"OpenTCPServersetsockopt()sendbuferror!return:%d,errno=%d,errortext:'%s'%s",ret,errno,strerror(errno),GetCurrentTime(0,0));
return-4;
}

ioctl(sockfd,FIONBIO,&blockORnot);/*blockornot*/

laddr.sin_family=PF_INET;
laddr.sin_port=htons(port);
laddr.sin_addr.s_addr=INADDR_ANY;
bzero(&(laddr.sin_zero),8);

ret=bind(sockfd,(structsockaddr*)&laddr,sizeof(structsockaddr));
if(ret<0){
sprintf(errorMessage,"OpenTCPServerbind()error!return:%d,errno=%d,errortext:'%s'%s",ret,errno,strerror(errno),GetCurrentTime(0,0));
close(sockfd);
return-5;
}
ret=listen(sockfd,total);
if(ret<0){
sprintf(errorMessage,"OpenTCPServerlisten()error!return:%d,errno=%d,errortext:'%s'%s",ret,errno,strerror(errno),GetCurrentTime(0,0));
close(sockfd);
return-6;
}
sprintf(errorMessage,"OpenTCPServeropenedonport.%d(%d)OK,socket(%d),buf(%d:%d)!%s",port,total,sockfd,sendbuflen,recvbuflen,GetCurrentTime(0,0));
returnsockfd;
}
/*----------------------原始碼程式碼結束--------------------------------------------*/

2、連線伺服器的函式

/*----------------------原始碼程式碼開始--------------------------------------------*/
int
ConnectSCPServer(char*serverip,intserverport,intblockORnot){
/*************************關於本函式************************************
*function_name:ConnectSCPServer
*引數說明:serverip伺服器IP地址或主機名,serverport伺服器埠,blockORnot整數型是否阻塞
*purpose:用來建立一個tcp客戶端socket
*tidiedby:zhoulifa([email protected])周立發(http://zhoulifa.bokee.com)
Linux愛好者Linux知識傳播者SOHO族開發者最擅長C語言
*datetime:2006-07-0520:40:00
*Note:任何人可以任意複製程式碼並運用這些文件,當然包括你的商業用途
*但請遵循GPL
*Thanksto:PaulSheer感謝PaulSheer在select_tut的man手冊裡提供了這份原始碼
*Hope:希望越來越多的人貢獻自己的力量,為科學技術發展出力
*Note:要使用此函式需要自定義一個全域性變數charerrorMessage[1024];幷包含自己編寫的GetCurrentTime.h標頭檔案
*********************************************************************/
intserversock=0,ret=0;
unsignedlongaddr;
structsockaddr_insin;
structhostent*he;

if((he=gethostbyname(serverip))==0){
sprintf(errorMessage,"ConnectSCPServerIPaddress'%s'error!return:-1%s",serverip,GetCurrentTime(0,0));
return-1;
}

serversock=socket(PF_INET,SOCK_STREAM,0);
if(serversock==-1){
sprintf(errorMessage,"ConnectSCPServersocket()error!return:-2,errno=%d,errortext:'%s'%s",errno,strerror(errno),GetCurrentTime(0,0));
return-2;
}

ioctl(serversock,FIONBIO,&blockORnot);//blockornot

memset((char*)&sin,0,sizeof(structsockaddr_in));
sin.sin_family=PF_INET;
sin.sin_port=htons(serverport);
sin.sin_addr=*((structin_addr*)he->h_addr);

ret=connect(serversock,(structsockaddr*)&sin,sizeof(sin));

if(ret==-1){
sprintf(errorMessage,"ConnectSCPServerconnect()error!return:-3,errno=%d,errortext:'%s'%s",errno,strerror(errno),GetCurrentTime(0,0));
close(serversock);
return-3;
}

returnserversock;
}
/*----------------------原始碼程式碼結束--------------------------------------------*/

3、傳送資料函式Send
/*----------------------原始碼程式碼開始--------------------------------------------*/
int
Send(intsock,char*buf,size_tsize,intflag,inttimeout){
/*************************關於本函式************************************
*function_name:Send
*引數說明:sock整數型socket,buf待發送的內容,size要傳送的大小,flag傳送選項,timeout超時時間值
*purpose:用來通過一個socket在指定時間內傳送資料
*tidiedby:zhoulifa([email protected])周立發(http://zhoulifa.bokee.com)
Linux愛好者Linux知識傳播者SOHO族開發者最擅長C語言
*datetime:2006-07-0520:58:00
*Note:任何人可以任意複製程式碼並運用這些文件,當然包括你的商業用途
*但請遵循GPL
*Thanksto:PaulSheer感謝PaulSheer在select_tut的man手冊裡提供了這份原始碼
*Hope:希望越來越多的人貢獻自己的力量,為科學技術發展出力
*Note:要使用此函式需要自定義一個全域性變數charerrorMessage[1024];幷包含自己編寫的GetCurrentTime.h標頭檔案
*********************************************************************/
inti=0,ret=0,intretry=0;

structtimevaltival;
fd_setwritefds;
intmaxfds=0;

tival.tv_sec=timeout;
tival.tv_usec=0;

FD_ZERO(&writefds);

if(sock>0){
FD_SET(sock,&writefds);
maxfds=((sock>maxfds)?sock:maxfds);
}
else{
sprintf(errorMessage,"Sendsocket:%derror!return:-2%s",sock,GetCurrentTime(0,0));
return-2;
}

ret=select(maxfds+1,NULL,&writefds,NULL,&tival);
if(ret<=0){
if(ret<0)sprintf(errorMessage,"Sendsocket:%dselect()error!return:%d,errno=%d,errortext:'%s'%s",sock,ret,errno,strerror(errno),GetCurrentTime(0,0));
elsesprintf(errorMessage,"Sendsocket:%dselecttimeout(%d)!%s",sock,timeout,GetCurrentTime(0,0));
close(sock);
return-3;
}
if(!(FD_ISSET(sock,&writefds))){
sprintf(errorMessage,"Sendsocket:%dnotinwritefds!%s",sock,GetCurrentTime(0,0));
close(sock);
return-4;
}

while(i<size){
ret=send(sock,buf+i,size-i,flag);
if(ret<=0){
sprintf(errorMessage,"Sendsocket:%dsend()error!return:%d,errno=%d,errortext:'%s'%s",sock,ret,errno,strerror(errno),GetCurrentTime(0,0));

if(EINTR==errno)
if(intretry<10){intretry++;continue;}
elsesprintf(errorMessage,"Sendsocket:%dsend()error!EINTR10times!%s",sock,GetCurrentTime(0,0));

close(sock);
return-1;
}
elsei+=ret;
}
sprintf(errorMessage,"Sendsocket:%dsend()OK!%d/%dbytessent!%s",sock,i,size,GetCurrentTime(0,0));
returni;
}
/*----------------------原始碼程式碼結束--------------------------------------------*/

4、接收資料函式Recv

/*----------------------原始碼程式碼開始--------------------------------------------*/
int
Recv(intsock,char*buf,size_tsize,intflag,inttimeout){
/*************************關於本函式************************************
*function_name:Recv
*引數說明:sock整數型socket,buf接收資料的緩衝區,size要接收資料的大小,flag接收選項,timeout超時時間值
*purpose:用來從一個socket在指定時間內讀取資料
*tidiedby:zhoulifa([email protected])周立發(http://zhoulifa.bokee.com)
Linux愛好者Linux知識傳播者SOHO族開發者最擅長C語言
*datetime:2006-07-0521:10:00
*Note:任何人可以任意複製程式碼並運用這些文件,當然包括你的商業用途
*但請遵循GPL
*Thanksto:PaulSheer感謝PaulSheer在select_tut的man手冊裡提供了這份原始碼
*Hope:希望越來越多的人貢獻自己的力量,為科學技術發展出力
*Note:要使用此函式需要自定義一個全域性變數charerrorMessage[1024];幷包含自己編寫的GetCurrentTime.h標頭檔案
*********************************************************************/
inti=0,ret=0,intretry=0;

structtimevaltival;
fd_setreadfds;
intmaxfds=0;

tival.tv_sec=timeout;
tival.tv_usec=0;

FD_ZERO(&readfds);

if(sock>0){
FD_SET(sock,&readfds);
maxfds=((sock>maxfds)?sock:maxfds);
}
else{
sprintf(errorMessage,"Recvsocket:%derror!return:-2%s",sock,GetCurrentTime(0,0));
return-2;
}

ret=select(maxfds+1,&readfds,NULL,NULL,&tival);
if(ret<=0){
if(ret<0)sprintf(errorMessage,"Recvsocket:%dselect()error!return:%d,errno=%d,errortext:'%s'%s",sock,ret,errno,strerror(errno),GetCurrentTime(0,0));
elsesprintf(errorMessage,"Recvsocket:%dselecttimeout(%d)!%s",sock,timeout,GetCurrentTime(0,0));
close(sock);
return-3;
}
if(!(FD_ISSET(sock,&readfds))){
sprintf(errorMessage,"Recvsocket:%dnotinreadfds!%s",sock,GetCurrentTime(0,0));
close(sock);
return-4;
}
while(i<size){
ret=recv(sock,buf+i,size-i,flag);
if(ret<=0){
sprintf(errorMessage,"Recvsocket:%drecv()error!return:%d,errno=%d,errortext:'%s'%s",sock,ret,errno,strerror(errno),GetCurrentTime(0,0));
if(errno==EINTR)
if(intretry<10){intretry++;continue;}
elsesprintf(errorMessage,"Recvsocket:%drecv()error!EINTR10times!%s",sock,GetCurrentTime(0,0));
close(sock);
return-1;
}
elsei+=ret;
}
sprintf(errorMessage,"Recvsocket:%drecv()OK!%d/%dbytesreceived!%s",sock,i,size,GetCurrentTime(0,0));
returni;
}

最後需要說明的是:我這裡講到的源程式並不能實際地作為一個產品程式來用,實際情況下可能會有其它許多工作要做,比如可能要建立共享佇列來存放socket裡讀到的訊息,也可能把傳送訊息先進行排隊然後再呼叫Send函式。還有,如果不是全數字,在傳送前一定要htonl轉換為網路位元組序,同理接收到後一定要先ntohl由網路位元組序轉換為主機位元組序,否則對方傳送過來的0x00000001在你這裡可能是0x00010000,因為高低位順序不同。

#p#分頁標題#e#


進入2.6核心時代,select應該進垃圾堆了
高併發伺服器用select效率極低,特別是使用非阻塞IO時更是慢得一蹋糊塗
改用epoll會大大改善
我一個程式監聽從8000到18000共計1萬個埠,啟動1萬個LISTEN
用epoll來阻塞,系統非常輕鬆,完全沒有驚群現象

epoll用法比select簡單

初始化:建立epoll描述字;向epoll描述字新增需要響應的套接字,初始化過程只要一次即可

使用:等待epoll事件發生,提取事件的套接字進行相應的讀寫操作


staticints_epfd;//epoll描述字

{//初始化epoll
structepoll_eventev;

//設定epoll
s_epfd=epoll_create(65535);

{//這個過程可以迴圈以便加入多個LISTEN套接字進入epoll事件集合
//伺服器監聽建立
rc=listen();//listen引數這裡省略

//加入epoll事件集合
ev.events=EPOLLIN;
ev.data.fd=rc;
if(epoll_ctl(s_epfd,EPOLL_CTL_ADD,rc,&ev)<0){
fprintf(stderr,"epollsetinsertionerror:fd=%d",rc);
return(-1);
}
}
}

{//epoll事件處理
inti,nfds,sock_new;
structepoll_eventevents[16384];
for(;;){
//等待epoll事件
nfds=epoll_wait(s_epfd,events,16384,-1);
//處理epoll事件
for(i=0;i<nfds;i++){
//events.data.fd是epoll事件中彈出的套接字
//接收連線
sock_new=accept(events.data.fd);//accept其它引數這裡省略了
if(0>sock_new){
fprintf(stderr,"接收客戶端連線失敗\n");
continue;
}
}
}
}

1、為什麼select是落後的?

首先,在Linux核心中,select所用到的FD_SET是有限的,即核心中有個引數__FD_SETSIZE定義了每個FD_SET的控制代碼個數,在我用的2.6.15-25-386核心中,該值是1024,搜尋核心原始碼得到:

include/linux/posix_types.h:#define__FD_SETSIZE1024

也就是說,如果想要同時檢測1025個控制代碼的可讀狀態是不可能用select實現的。或者同時檢測1025個控制代碼的可寫狀態也是不可能的。

其次,核心中實現select是用輪詢方法,即每次檢測都會遍歷所有FD_SET中的控制代碼,顯然,select函式執行時間與FD_SET中的控制代碼個數有一個比例關係,即select要檢測的控制代碼數越多就會越費時。

當然,在前文中我並沒有提及poll方法,事實上用select的朋友一定也試過poll,我個人覺得select和poll大同小異,個人偏好於用select而已。



/************關於本文件********************************************

*filename:Linux2.6核心中提高網路I/O效能的新方法epoll

*purpose:補充“Linux下各類TCP網路伺服器的實現原始碼”一文的不足之處

*wroteby:zhoulifa([email protected])周立發(http://zhoulifa.bokee.com)

Linux愛好者Linux知識傳播者SOHO族開發者最擅長C語言

*datetime:2006-07-0622:30:00

*Note:任何人可以任意複製程式碼並運用這些文件,當然包括你的商業用途

*但請遵循GPL

*Hope:希望越來越多的人貢獻自己的力量,為科學技術發展出力

*********************************************************************/



2、2.6核心中提高I/O效能的新方法epoll



epoll是什麼?按照man手冊的說法:是為處理大批量控制代碼而作了改進的poll。要使用epoll只需要這三個系統呼叫:epoll_create(2),epoll_ctl(2),epoll_wait(2)。

當然,這不是2.6核心才有的,它是在2.5.44核心中被引進的(epoll(4)isanewAPIintroducedinLinuxkernel2.5.44)



以下文章轉自滕昱的WebLoghttp://mechgouki.spaces.msn.com/blog/PersonalSpace.aspx
[QUOTE]

/*********************************引用開始******************************/


[版權宣告]:此文件遵循GNU自由文件許可證(GNUFreeDocumentationLicense).任何人可以自由複製,分發,修改,不過如果方便,請註明出處和作者:)



(1)導言:



首先,我強烈建議大家閱讀RichardStevens著作《TCP/IPIllustractedVolume1,2,3》和《UNIXNetworkProgrammingVolume1,2》。雖然他離開我們大家已經5年多了,但是他的書依然是進入網路程式設計的最直接的道路。其中的3卷的《TCP/IPIllustracted》卷1是必讀-如果你不瞭解tcp協議各個選項的詳細定義,你就失去了優化程式重要的一個手段。卷2,3可以選讀一下。比如卷2講解的是4.4BSD核心TCP/IP協議棧實現----這個版本的協議棧幾乎影響了現在所有的主流os,但是因為年代久遠,內容不一定那麼vogue.在這裡我多推薦一本《TheLinuxNetworkingArchitecture--DesignandImplementationofNetworkProtocolsintheLinuxKernel》,以2.4核心講解LinuxTCP/IP實現,相當不錯.作為一個現實世界中的實現,很多時候你必須作很多權衡,這時候參考一個久經考驗的系統更有實際意義。舉個例子,linux核心中sk_buff結構為了追求速度和安全,犧牲了部分記憶體,所以在傳送TCP包的時候,無論應用層資料多大,sk_buff最小也有272的位元組.



其實對於socket應用層程式來說,《UNIXNetworkProgrammingVolume1》意義更大一點.2003年的時候,這本書出了最新的第3版本,不過主要還是修訂第2版本。其中第6章《I/OMultiplexing》是最重要的。Stevens給出了網路IO的基本模型。在這裡最重要的莫過於select模型和AsynchronousI/O模型.從理論上說,AIO似乎是最高效的,你的IO操作可以立即返回,然後等待os告訴你IO操作完成。但是一直以來,如何實現就沒有一個完美的方案。最著名的windows完成埠實現的AIO,實際上也是內部用執行緒池實現的罷了,最後的結果是IO有個執行緒池,你應用也需要一個執行緒池......很多文件其實已經指出了這帶來的執行緒context-switch帶來的代價。



在linux平臺上,關於網路AIO一直是改動最多的地方,2.4的年代就有很多AIO核心patch,最著名的應該算是SGI那個。但是一直到2.6核心釋出,網路模組的AIO一直沒有進入穩定核心版本(大部分都是使用使用者執行緒模擬方法,在使用了NPTL的linux上面其實和windows的完成埠基本上差不多了)。2.6核心所支援的AIO特指磁碟的AIO---支援io_submit(),io_getevents()以及對DirectIO的支援(就是繞過VFS系統buffer直接寫硬碟,對於流伺服器在記憶體平穩性上有相當幫助)。



所以,剩下的select模型基本上就是我們在linux上面的唯一選擇,其實,如果加上no-blocksocket的配置,可以完成一個"偽"AIO的實現,只不過推動力在於你而不是os而已。不過傳統的select/poll函式有著一些無法忍受的缺點,所以改進一直是2.4-2.5開發版本核心的任務,包括/dev/poll,realtimesignal等等。最終,DavideLibenzi開發的epoll進入2.6核心成為正式的解決方案

#p#分頁標題#e#



(2)epoll的優點



<1>支援一個程序開啟大數目的socket描述符(FD)



select最不能忍受的是一個程序所開啟的FD是有一定限制的,由FD_SETSIZE設定,預設值是2048。對於那些需要支援的上萬連線數目的IM伺服器來說顯然太少了。這時候你一是可以選擇修改這個巨集然後重新編譯核心,不過資料也同時指出這樣會帶來網路效率的下降,二是可以選擇多程序的解決方案(傳統的Apache方案),不過雖然linux上面建立程序的代價比較小,但仍舊是不可忽視的,加上程序間資料同步遠比不上執行緒間同步的高效,所以也不是一種完美的方案。不過epoll則沒有這個限制,它所支援的FD上限是最大可以開啟檔案的數目,這個數字一般遠大於2048,舉個例子,在1GB記憶體的機器上大約是10萬左右,具體數目可以cat/proc/sys/fs/file-max察看,一般來說這個數目和系統記憶體關係很大。



<2>IO效率不隨FD數目增加而線性下降



傳統的select/poll另一個致命弱點就是當你擁有一個很大的socket集合,不過由於網路延時,任一時間只有部分的socket是"活躍"的,但是select/poll每次呼叫都會線性掃描全部的集合,導致效率呈現線性下降。但是epoll不存在這個問題,它只會對"活躍"的socket進行操作---這是因為在核心實現中epoll是根據每個fd上面的callback函式實現的。那麼,只有"活躍"的socket才會主動的去呼叫callback函式,其他idle狀態socket則不會,在這點上,epoll實現了一個"偽"AIO,因為這時候推動力在os核心。在一些benchmark中,如果所有的socket基本上都是活躍的---比如一個高速LAN環境,epoll並不比select/poll有什麼效率,相反,如果過多使用epoll_ctl,效率相比還有稍微的下降。但是一旦使用idleconnections模擬WAN環境,epoll的效率就遠在select/poll之上了。



<3>使用mmap加速核心與使用者空間的訊息傳遞。



這點實際上涉及到epoll的具體實現了。無論是select,poll還是epoll都需要核心把FD訊息通知給使用者空間,如何避免不必要的記憶體拷貝就很重要,在這點上,epoll是通過核心於使用者空間mmap同一塊記憶體實現的。而如果你想我一樣從2.5核心就關注epoll的話,一定不會忘記手工mmap這一步的。



<4>核心微調



這一點其實不算epoll的優點了,而是整個linux平臺的優點。也許你可以懷疑linux平臺,但是你無法迴避linux平臺賦予你微調核心的能力。比如,核心TCP/IP協議棧使用記憶體池管理sk_buff結構,那麼可以在執行時期動態調整這個記憶體pool(skb_head_pool)的大小---通過echoXXXX>/proc/sys/net/core/hot_list_length完成。再比如listen函式的第2個引數(TCP完成3次握手的資料包佇列長度),也可以根據你平臺記憶體大小動態調整。更甚至在一個數據包面數目巨大但同時每個資料包本身大小卻很小的特殊系統上嘗試最新的NAPI網絡卡驅動架構。



(3)epoll的使用



令人高興的是,2.6核心的epoll比其2.5開發版本的/dev/epoll簡潔了許多,所以,大部分情況下,強大的東西往往是簡單的。唯一有點麻煩是epoll有2種工作方式:LT和ET。



LT(leveltriggered)是預設的工作方式,並且同時支援block和no-blocksocket.在這種做法中,核心告訴你一個檔案描述符是否就緒了,然後你可以對這個就緒的fd進行IO操作。如果你不作任何操作,核心還是會繼續通知你的,所以,這種模式程式設計出錯誤可能性要小一點。傳統的select/poll都是這種模型的代表.



ET(edge-triggered)是高速工作方式,只支援no-blocksocket。在這種模式下,當描述符從未就緒變為就緒時,核心通過epoll告訴你。然後它會假設你知道檔案描述符已經就緒,並且不會再為那個檔案描述符傳送更多的就緒通知,直到你做了某些操作導致那個檔案描述符不再為就緒狀態了(比如,你在傳送,接收或者接收請求,或者傳送接收的資料少於一定量時導致了一個EWOULDBLOCK錯誤)。但是請注意,如果一直不對這個fd作IO操作(從而導致它再次變成未就緒),核心不會發送更多的通知(onlyonce),不過在TCP協議中,ET模式的加速效用仍需要更多的benchmark確認。



epoll只有epoll_create,epoll_ctl,epoll_wait3個系統呼叫,具體用法請參考http://www.xmailserver.org/linux-patches/nio-improve.html,

在http://www.kegel.com/rn/也有一個完整的例子,大家一看就知道如何使用了



(4)Leader/follower模式執行緒pool實現,以及和epoll的配合



.....未完成,主要是要避免過多的epoll_ctl呼叫,以及嘗試使用EPOLLONESHOT加速......



(5)benchmark



.......未完成

/*********************************引用結束******************************/

[/QUOTE]

3、epoll的使用方法

這是epoll的man手冊提供的一個例子,這段程式碼假設一個非阻塞的socket監聽listener被建立並且一個epoll控制代碼kdpfd已經提前用epoll_create建立了:

[CODE]
structepoll_eventev,*events;



for(;;){

nfds=epoll_wait(kdpfd,events,maxevents,-1);/*waitforanI/Oevent.Allnoteshereaddedbyzhoulifa(http://zhoulifa.bokee.com)on2006-7-622:10:00*/



for(n=0;n<nfds;++n){

if(events[n].data.fd==listener){/*iflistensockethasanI/O,acceptthenewconnect*/

client=accept(listener,(structsockaddr*)&local,

&addrlen);

if(client<0){

perror("accept");

continue;

}

setnonblocking(client);

ev.events=EPOLLIN|EPOLLET;/*EPOLLIN-availableforread*/

ev.data.fd=client;

if(epoll_ctl(kdpfd,EPOLL_CTL_ADD,client,&ev)<0){/*addthenewsocketintotheepollfiledescriptors*/

fprintf(stderr,"epollsetinsertionerror:fd=%d\n",

client);

return-1;

}

}

else

do_use_fd(events[n].data.fd);/*readfromasocketwhichhasdatacome*/

}

}

[/CODE]
4、epoll使用方法示意程式碼

以下程式碼由chinaunix.net上BBS使用者safedead(http://bbs.chinaunix.net/viewpro.php?uid=407631)提供:



[CODE]
staticints_epfd;//epoll描述字



{//初始化epoll

structepoll_eventev;



//設定epoll

s_epfd=epoll_create(65535);



{//這個過程可以迴圈以便加入多個LISTEN套接字進入epoll事件集合

//伺服器監聽建立

rc=listen();//listen引數這裡省略



//加入epoll事件集合

ev.events=EPOLLIN;

ev.data.fd=rc;

if(epoll_ctl(s_epfd,EPOLL_CTL_ADD,rc,&ev)<0){

fprintf(stderr,"epollsetinsertionerror:fd=%d",rc);

return(-1);

}

}

}



{//epoll事件處理

inti,nfds,sock_new;

structepoll_eventevents[16384];

for(;;){

//等待epoll事件

nfds=epoll_wait(s_epfd,events,16384,-1);

//處理epoll事件

for(i=0;i<nfds;i++){

//events.data.fd是epoll事件中彈出的套接字

//接收連線

sock_new=accept(events.data.fd);//accept其它引數這裡省略了

if(0>sock_new){

fprintf(stderr,"接收客戶端連線失敗\n");

continue;

}

}

}

}


對照safedead和前面的一份程式碼,我想大家一定是明白了的。



5、參考文件

Improving(network)I/Operformance...

http://www.xmailserver.org/linux-patches/nio-improve.html

相關推薦

LinuxTCP網路伺服器實現原始碼

大家都知道各類網路伺服器程式的編寫步驟,並且都知道網路伺服器就兩大類:迴圈服務和併發服務。這裡附上原始碼來個小結吧。 首先,迴圈網路伺服器程式設計實現的步驟是這樣的: 這種伺服器模型是典型迴圈服務,如果不加上多程序/執行緒技術,此種服務吞吐量有限,大家都可以看到,如果前一

Linux簡易web伺服器實現

今天突然對http的web伺服器感興趣了,就研究了一下,發現linux下的web伺服器就是一個socket程式設計的伺服器端,而我們用的ie,chrome等瀏覽器就是客戶端,只不過傳送和接收資料按照http網頁格式,就相當於對資料進行了封裝,相當於加上了檔案頭和檔案

linuxdns主從伺服器實現

案例環境: 主域名伺服器:ns1.zdj.com,172.17.0.142 從域名伺服器:ns2.zdj.com,172.17.0.37 兩臺伺服器均能夠提供 zdj.com 區域的域名解析主域名伺服

linuxping命令的實現原始碼

相信大家一定遇到過上不了網的情形,都知道用個ping命令。這不小王就是這樣的女孩,老是上不了網,老是找我,我就先ping一下,逐步找找問題在哪兒,有的放矢,不至於盲目抓瞎(說心裡話,我真不願意幫小王弄,每次弄好了,她就和那個叫寒煙的Q友,使勁聊天,唉,心裡哇涼啊.)都說實

提升linuxTCP伺服器併發連線數(limit)

https://cloud.tencent.com/developer/article/1069900 1、修改使用者程序可開啟檔案數限制   在Linux平臺上,無論編寫客戶端程式還是服務端程式,在進行高併發TCP連線處理時,最高的併發數量都要受到系統對使用者單一程序同時可開

linux構建svn伺服器實現專案自動部署

在Linux系統中搭建svn服務所需要用到的軟體叫做subversion,可以通過yum來進行安裝. svn服務是為了實現協同工作,即一個團隊公眾開發一個專案而不導致程式碼混亂,不會出現今天我修改的程式碼在不通知同事的情況下又被他修改了.

基於linuxTCP網路聊天室設計與實現

利用Linux實現基於TCP模式的網路聊天程式 主要完成的兩大組成部分為:伺服器和客戶端。 伺服器程式主要負責監聽客戶端發來的訊息。 客戶端需要登入到伺服器端才可以實現正常的聊天功能。該程式是利用程序以及共享記憶體來實現群傳送訊息的。 以下簡單分析一下

linuxsocket程式設計 select實現非阻塞模式多臺客戶端與伺服器通訊

select函式原型如下: int select (int maxfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); select系統呼叫是用來讓我們的程式

Linux使用libevent庫實現伺服器端程式設計

一、背景 TCP伺服器端一般用到非阻塞Socket、IO複用,Linux可以支援epoll、select,而Windows支援select、IOCP,考慮平臺適用性,需要對IO事件進行封裝相容; 二、相關知識 2.1 事件驅動(I/O複用) 服務端常用到的 select

Linux基於UDP協議實現的聊天室專案(附原始碼

好久沒來更新了,這段時間一直在著手完成這個專案,由於之前沒有接觸過這種稍大型的專案,而且對於C/S架構以及UDP通訊的瞭解也不是很深,所以前面很大的一段時間都被浪費掉了,做了很大無用功。 剛開始弄的時候,也是在網上搜了很多資料,找了很多版本,發現大都有

linux埠掃描的實現(TCP connect、TCP SYN、TCP FIN、UDP四種方式)

一、TCP 常用的埠掃描方式有以下三種: 1.connect掃描 我們知道,常見的TCP的socket實現過程為 更本質的連線和結束的過程是如下這個樣子的: 從上面兩個圖我們可以看出來目標主機的一個埠如果是監聽狀態(LISTENING或者LINSTEN),那

Linuxmysql基於MyCat實現主從復制和讀寫分離

mycat1.1 MyCat介紹及應用場景MyCat介紹MyCat是一個開源的分布式數據庫系統,是一個實現了MySQL協議的服務器,前端用戶可以把它看作是一個數據庫代理,用MySQL客戶端工具和命令行訪問,而其後端可以用MySQL原生協議與多個MySQL服務器通信,也可以用JDBC協議與大多數主流數據庫服務器

LinuxTCP延遲重慶時 時 彩源碼下載確認(Delayed Ack)機制導致的時延問題分析

googl 分析 最終 end gin 沒有 奇怪 營銷 大於 重慶時 時 彩下載聯系方式:QQ:2747044651 網址在實際測試中發現,當N大於等於3的情況,第2秒之後,每次第三個recv調用,總會阻塞40毫秒左右,但在分析Server端日誌時,發現所有請求在Serv

LinuxWi-Fi的實現:wireless_tools和wpa_supplicant

erl 密碼 fig 而是 tar.gz 方式 控制 nec dbm 轉載於:https://www.cnblogs.com/lidabo/p/6069455.html 平臺為hi35XX,在Liunx下借助wireless_tools和wpa_supplicant(因為現

LinuxTCP程式設計

首先要建立伺服器建立起socket,然後與本地的埠進行繫結,接著就開始接收客戶端的請求並建立與它的連線,接下來,客戶端傳送的訊息。 tcpserver.c程式碼: int main() { struct sockaddr_in server_sockaddr,client_s

Linux的簡易shell實現

Linux系統的shell作為作業系統的外殼,為使用者提供使用作業系統的介面。 它是命令語言、命令解釋程式及程式設計語言的統稱。 相當於bash的一個子程序,父程序等待,子程序進行程式替換。 shell充當一個橋樑:將使用者的命令翻譯給核心(kernel)處理;同時,將核心的

Linux 網路攻擊

學習了虛擬機器的linux系統,想玩點有意思的,沒錯就是攻擊,給大家教一個簡單的arpspoof攻擊 這種攻擊會將對方電腦卡的掉線,注意這種操作是在同一區域網下的操作。 接下來說一說具體的步驟 第一步 安裝 sudo apt-get install dsniff ssl

系統日誌管理,時間同步服務,linux網路配置

####系統的日誌管理#### #2.rsyslog的管理# /var/log/messages ##服務資訊登陸 /var/log/secure ##系統登陸日誌 /var/log/cron ##定時任務日誌 /var/log/maillog ##郵件日誌 /var/log/boot.lo

linux網路通訊設定:openssh、PuTTY、tightVNC

OpenSSH的安裝: windows上安裝PuTTY:  PuZZY上傳檔案到linux:  1.在window下的cmd中cd到PuZZY所在的資料夾下 2.使用pscp命令上傳檔案 3.使用pscp命令下載檔案     ti

LinuxTCP/IP核心引數優化

/proc/sys/net目錄 所有的TCP/IP引數都位於/proc/sys/net目錄下(請注意,對/proc/sys/net目錄下內容的修改都是臨時的,任何修改在系統重啟後都會丟失),例如下面這些重要的引數: 引數(路徑+檔案) 描述