使用splice函式實現0拷貝的回顯伺服器
在平時的收發資料總是會呼叫系統呼叫recv和send等函式,這些函式都會進入核心進行資料的拷貝,所以說還是比較耗費資源的,所以就有了實現0拷貝的函式,這裡就使用其中的splice函式實現一個回顯伺服器,用於將客戶端傳送的訊息原樣傳送給客戶端,並且不使用recv、send函式
首先介紹一下splice:
splice函式用於在兩個檔案描述符之間移動資料,也是零拷貝操作,定義如下:
#include <fcntl.h>
ssize_t splice(int fd_in, loff_t * off_in,int fd_out, loff_t off_out,size_t len,unsingned int flags);
引數介紹:
fd_in 引數是待輸入資料的檔案描述符,如果fd_in是一個管道檔案描述符,那麼off_in引數必須設定為NULL;若不是,比如是socket,那麼off_in表示聰從輸入資料的何處開始讀取資料,若設定為null則表示從當前偏移位置開始讀入;
fd_out和fd_in 的引數含義相同
len指定移動資料的長度
flags則控制資料如何移動
flags的值可以是以下的值:
SPLICE_F_MOVE: 如果合適的話,按整頁記憶體移動資料
SPLICE_F_NONBLOCK: 非阻塞的splice操作,但是實際效果還是會受到檔案描述符本身的阻塞狀態限制
SPLICE_F_MORE: 給核心的一個提示:後續的splice操作將讀取更多的資料
SPLICE_F_GIFT: 對splice沒有效果
注意:使用splice時,fd_in和fd_out必須至少有一個是管道檔案描述符。
**返回值:**成功返回移動的位元組數;返回0表示沒有資料需要移動;失敗返回-1並設定errno
errno的常見以及含義:
EBADF 或 EINVAL :引數所指檔案描述符有錯
ENOMEM:記憶體不夠
ESPIPE:引數fd_in是管道檔案描述符,但off_in不是NULL
下面用splice實現一個回顯伺服器:
伺服器端程式碼:
#define _GNU_SOURCE 1
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <poll.h>
#include <fcntl.h>
#include <libgen.h> //basename
#include <errno.h>
int main(int argc,char ** argv){
if(argc <= 2){
printf("usage: %s ip_address port_number\n",basename(argv[0]));
return 1;
}
const char *ip = argv[1];
int port = atoi(argv[2]);
int ret = 0;
struct sockaddr_in serv_addr;
bzero(&serv_addr,sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
inet_pton(AF_INET,ip,&serv_addr.sin_addr);
serv_addr.sin_port = htons(port);
int listenFd = socket(AF_INET,SOCK_STREAM,0);
assert(listenFd >= 0);
ret = bind(listenFd,(struct sockaddr*)&serv_addr,sizeof(serv_addr));
assert(ret != -1);
ret = listen(listenFd,5);
assert(ret != -1);
struct sockaddr_in client_addr;
socklen_t clit_len = sizeof(client_addr);
int connfd = accept(listenFd,(struct sockaddr *)&client_addr,&clit_len);
if(connfd < 0){
printf("errno is %d\n",errno);
}else
{
int pipefd[2];
assert(ret != -1);
ret = pipe(pipefd);
ret = splice(connfd,NULL,pipefd[1],NULL,32678,SPLICE_F_MORE|SPLICE_F_MOVE);
ret = splice(pipefd[0],NULL,connfd,NULL,32768,SPLICE_F_MORE|SPLICE_F_MOVE);
assert(ret != -1);
close(connfd);
}
close(listenFd);
return 0;
}
客戶端:
#include <unistd.h>
#include <sys/socket.h>
#include <netdb.h>
#include <iostream>
#include <string>
#include <cassert>
#include <sys/types.h>
#include <netinet/in.h> //htons
#include <string.h> // memset
#include <arpa/inet.h> // inet_addr
using namespace std;
int main(){
int sockfd = socket(AF_INET,SOCK_STREAM,0);
// assert(sockfd == 0);
struct sockaddr_in servAddr;
memset(&servAddr,0,sizeof(servAddr));
servAddr.sin_family = AF_INET;
servAddr.sin_port = htons(6666);
servAddr.sin_addr.s_addr = inet_addr("192.168.10.128");
int ret = connect(sockfd,(struct sockaddr*)&servAddr,sizeof(servAddr));
if(ret == -1){
cout<<"connect eerror"<<endl;
cout<<strerror(errno)<<endl;
}
// assert(ret == 0);
while(1){
char sendbuffer[1024];
memset(sendbuffer,0,sizeof(sendbuffer));
// int ret = recv(sockfd,buffer,sizeof(buffer),0);
//
cout<<"Please Input:"<<endl;
cin>>sendbuffer;
send(sockfd,sendbuffer,sizeof(sendbuffer),0);
char recvbuf[1024];
int ret = recv(sockfd,recvbuf,sizeof(recvbuf),0);
if(ret){
cout<<"recv serv:"<<recvbuf<<endl;
}else if(ret == 0){
cout<<"link failure!"<<endl; //duankai
close(sockfd);
break;
}
}
}
因為伺服器沒有進行迴圈,所以傳送一次伺服器即斷開
效果:
相關文章
- 伺服器程式設計——函式splice實現零拷貝使用解釋伺服器程式設計函式
- linux網路程式設計九:splice函式,高效的零拷貝Linux程式設計函式
- C++拷貝建構函式(深拷貝,淺拷貝)C++函式
- 淺拷貝與深拷貝的實現
- 拷貝建構函式函式
- 自己寫的unix檔案拷貝指令cp實現函式函式
- C語言實現字串拷貝函式的幾種方法C語言字串函式
- 實現物件淺拷貝、深拷貝物件
- 拷貝建構函式的作用函式
- 深拷貝與淺拷貝的實現(一)
- js實現深拷貝和淺拷貝JS
- 【JS】深拷貝與淺拷貝,實現深拷貝的幾種方法JS
- 拷貝建構函式中的陷阱函式
- 拷貝建構函式(比較全的)函式
- 深拷貝和淺拷貝的區別是什麼?實現一個深拷貝
- [JS系列二]談談深拷貝和淺拷貝,如何實現深拷貝JS
- js實現深拷貝JS
- JavaScript實現淺拷貝的方法JavaScript
- 用 splice函式分別實現 push、pop、shift、unshi函式
- JS中的深淺拷貝以及實現深拷貝的幾種方法.JS
- C++拷貝建構函式詳解C++函式
- C++ 拷貝建構函式詳解C++函式
- 怎麼實現深拷貝
- 建構函式,拷貝賦值函式的N種呼叫情況函式賦值
- 實現深拷貝還在用JSON.parse(JSON.stringify(obj))?帶你用JS實現一個完整版深拷貝函式JSONOBJ函式
- C++之Big Three:拷貝構造、拷貝賦值、解構函式探究C++賦值函式
- C++複製控制:拷貝建構函式C++函式
- js物件實現深淺拷貝!!JS物件
- C++中建構函式,拷貝建構函式和賦值函式的詳解C++函式賦值
- Linux使用expect實現遠端拷貝檔案Linux
- c/c++ 拷貝控制 建構函式的問題C++函式
- 什麼要使用克隆、如何實現克隆物件、序列化與反序列實現克隆的好處、深拷貝與淺拷貝物件
- C++/CLI思辨錄之拷貝建構函式C++函式
- js的深拷貝和淺拷貝JS
- 物件的深拷貝與淺拷貝物件
- 基於Spring的流量拷貝框架實現Spring框架
- Java實現檔案拷貝的4種方法.Java
- vue深拷貝淺拷貝Vue