- 網(wǎng)絡(luò)編程基礎(chǔ)
網(wǎng)絡(luò)編程,首先了解計(jì)算機(jī)網(wǎng)絡(luò)體系結(jié)構(gòu)是有必要的,著重掌握TCP、IP協(xié)議,理解socket的概念,理解數(shù)據(jù)報(bào)方式和流式套接字的原理,熟練掌握相關(guān)數(shù)據(jù)結(jié)構(gòu)的使用 -
數(shù)據(jù)報(bào)和流式套接字的工作過程
-
基本概念
linux操作系統(tǒng)一切皆文件的概念在socket通信過程中體現(xiàn)的十分完整,通過一個(gè)文件描述符來索引一個(gè)socket,創(chuàng)建socket的系統(tǒng)調(diào)用為
相關(guān)參數(shù)的含義可查看manual文檔,其中domain比較特殊,可取下表
一般我們使用的AF_INET域中的套接字地址
- 一個(gè)簡單的例子
server.c
client.c#include<stdio.h> #include<unistd.h> #include<arpa/inet.h> #include<sys/socket.h> #include<sys/types.h> #include<netinet/in.h> int main(){ int server_fd, client_fd; int server_addr_len, client_addr_len; struct sockaddr_in server_addr, client_addr; server_addr_len = sizeof(server_addr); server_fd = socket(AF_INET, SOCK_STREAM, 0); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); server_addr.sin_port = 8000; bind(server_fd, (struct sockaddr *)&server_addr, server_addr_len); listen(server_fd, 5); while(1){ printf("服務(wù)器等待消息!\n"); char ch; client_addr_len = sizeof(client_addr); client_fd = accept(server_fd, (struct sockaddr *) &client_addr, (socklen_t *)&client_addr_len); read(client_fd, &ch, 1); ch++; write(client_fd, &ch, 1); close(client_fd); } return 0; }
#include<stdio.h> #include<netinet/in.h> #include<sys/types.h> #include<sys/socket.h> #include<unistd.h> #include<arpa/inet.h> #include<stdlib.h> #include<string.h> int main(){ struct sockaddr_in server_addr; int server_addr_len = sizeof(sockaddr_in); server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); server_addr.sin_family = AF_INET; server_addr.sin_port = 8000; int socket_fd = socket(AF_INET, SOCK_STREAM, 0); char ch = 'A'; int res; if((res = connect(socket_fd, (sockaddr*) &server_addr, (socklen_t) server_addr_len)) == -1){ perror("連接目的地址出錯(cuò)!"); exit(1); } printf("請(qǐng)輸入字符:\n"); scanf("%c", &ch); write(socket_fd, &ch, 1); read(socket_fd, &ch, 1); printf("%c\n", ch); close(socket_fd); return 0; }
- 熟悉以下用來初始化socket地址的函數(shù)
inet_ntoa/inet_aton
inet_addr
htonl/htons/ntohl/ntohs - 異步I、O之epoll
有時(shí)為了處理大量的文件句柄,需要一種機(jī)制,在單線程中也可以處理多個(gè)socket請(qǐng)求,輪詢的機(jī)制可以實(shí)現(xiàn)但是對(duì)系統(tǒng)的負(fù)載較高,epoll是linux系統(tǒng)用來處理類似socket這樣的文件句柄的讀寫等操作的一種IO復(fù)用機(jī)制。服務(wù)端維護(hù)一個(gè)socket列表,使用epoll調(diào)用來取出準(zhǔn)備好讀或?qū)懙膕ocket,隨后可以對(duì)socket句柄進(jìn)行操作
用到的系統(tǒng)調(diào)用epoll_create/epoll_ctl/epoll_wait
用到的數(shù)據(jù)結(jié)構(gòu)epoll_event/epoll_data_t
查閱manual了解函數(shù)用法
一個(gè)簡單的例子
common.h
server.c//include the header file socket,io,epoll,list,string,unistd,stdlib #include<iostream> #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<fcntl.h> #include<string.h> #include<list> #include<errno.h> #include<sys/epoll.h> #include<sys/types.h> #include<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h> //namespace using namespace std; //client list, C++ stl list<T> list<int> client_list; //macro statement server_ip,server_port,epollsize,buf_size,Welcome string,server_string #define SERVER_IP "127.0.0.1" #define SERVER_PORT 8888 #define EPOLL_SIZE 500 #define BUF_SIZE 2048 #define WELCOME_STRING "Welcome to join the chat room, your chat ID is: Client# %d" #define SERVER_STRING "Client id #%d say >> %s" #define CAUTION "only one client in the chat room" //set fd in nonblocking io mode, using fnctl void setNonBlocking(int fd){ int res = fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | O_NONBLOCK); if(res != 0){ perror("set fd nonblock failed"); return; } } //add fd to the epoll,with epoll_ctl void addFdToEpoll(int epollfd, int sockfd, bool enable_et){ struct epoll_event event; event.events = EPOLLIN; if(enable_et){ event.events = EPOLLIN | EPOLLET; } event.data.fd = sockfd; if(epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd, &event) != 0){ perror("add fd to epoll failed"); }; setNonBlocking(sockfd); } //send broadcast message to the client in clients list, except for which we received the data void sendBroadcastMessage(int sockfd){ char buf[BUF_SIZE], message[BUF_SIZE]; //initialize the space bzero(buf, BUF_SIZE); bzero(message, BUF_SIZE); int rlen = recv(sockfd, buf, BUF_SIZE, 0); printf("read data from Client #%d: %s\n", sockfd, buf); if(rlen == 0){ close(sockfd); client_list.remove(sockfd); printf("client #%ld closed, Now, there are %d clients in the chatroom\n",sockfd, client_list.size()); }else{ if(client_list.size() == 1){ int slen = send(sockfd, CAUTION, strlen(CAUTION), 0); return; } sprintf(message, SERVER_STRING, sockfd, buf); printf("will send:%s\n", message); list<int>::iterator iter; for( iter = client_list.begin(); iter != client_list.end(); iter++ ){ puts("sending data..."); if(*iter != sockfd){ send(*iter, message, BUF_SIZE, 0); } } } }
client.c#include "common.h" int main(){ //socket address statement struct sockaddr_in server_addr; //initialize the address server_addr.sin_addr.s_addr = inet_addr(SERVER_IP); server_addr.sin_port = htons(SERVER_PORT); server_addr.sin_family = AF_INET; //create server socket int listener = socket(AF_INET, SOCK_STREAM, 0); //bind the server address if(bind(listener, (struct sockaddr*)&server_addr, sizeof(server_addr)) != 0){ perror("bind failed"); return 1; } //listen on the address if(listen(listener, 5) != 0){ perror("listen failed"); return 2; } //create epoll int epollfd = epoll_create(EPOLL_SIZE); if(epollfd < 0){ perror("create epollfd failed"); return 3; } //add server fd to epoll addFdToEpoll(epollfd, listener, true); //epoll_event array static struct epoll_event events[EPOLL_SIZE]; //main loop int clientfd; while(1){ //epoll_wait puts("waiting clients..."); int event_count = epoll_wait(epollfd,events, EPOLL_SIZE, -1); printf("epoll event count is %d\n", event_count); for(int i = 0; i< event_count; i++){ clientfd = events[i].data.fd; //if fd is serverfd, accept a connection, print information, add new sockfd to epoll if(clientfd == listener){ struct sockaddr_in client_addr; socklen_t addrlen = sizeof(struct sockaddr_in); int newfd = accept(listener, (struct sockaddr*)&client_addr, &addrlen); if(newfd < 0){ perror("accept new connections failed"); exit(EXIT_FAILURE); } printf( "new connection from %s:%d, client id #%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), newfd ); addFdToEpoll(epollfd, newfd, true); client_list.push_back(newfd); printf("welcome message\n"); char message[BUF_SIZE]; bzero(message, BUF_SIZE); sprintf(message, WELCOME_STRING, newfd); send(newfd, message, strlen(message), 0); }//if not, sendbroadcast message else{ sendBroadcastMessage(clientfd); } } } }
#include"common.h" int main(){ //server address statement struct sockaddr_in server_addr; //initilize server address server_addr.sin_family = AF_INET; server_addr.sin_port = htons(SERVER_PORT); server_addr.sin_addr.s_addr = inet_addr(SERVER_IP); //create sock int sock = socket(AF_INET, SOCK_STREAM, 0); if(sock < 0){ perror("create socket failed"); return 1; } if(connect(sock, (struct sockaddr*)&server_addr, sizeof(struct sockaddr_in)) != 0){ perror("connect server failed"); return 0; } int pipefd[2]; if(pipe(pipefd) != 0){ perror("create pipe failed"); return 2; } int epollfd = epoll_create(EPOLL_SIZE); if(epollfd< 0){ perror("create epollfd failed"); return 3; } addFdToEpoll(epollfd, sock, true); addFdToEpoll(epollfd, pipefd[0], true); pid_t pid = fork(); if(pid < 0){ perror("create child process failed"); return 4; } int client_running = 1; char message[BUF_SIZE]; char buf[BUF_SIZE]; struct epoll_event events[2]; if(pid == 0){ close(pipefd[0]); printf("please input your message, and type exit to terminate\n"); while(client_running){ //printf("input>"); bzero(message, BUF_SIZE); //bzero(buf, BUF_SIZE); fgets(message, BUF_SIZE, stdin); if(strncmp(message, "exit", strlen("exit")) == 0){ client_running = 0; close(sock); close(pipefd[1]); return 0; }else{ write(pipefd[1], message, strlen(message)); } } }else{ close(pipefd[1]); while(client_running){ int event_count = epoll_wait(epollfd, events, 2, -1); for(int i = 0; i < event_count; i++){ if(events[i].data.fd == pipefd[0]){ bzero(buf, BUF_SIZE); if(read(pipefd[0], buf, BUF_SIZE) == 0){ client_running = 0; }else{ printf("\nyour input:%s\n", buf); send(sock, buf, strlen(buf) - 1, 0); } }else{ recv(sock, buf, BUF_SIZE, 0); printf("\nServer message:%s\n", buf); } } } } close(sock); if(pid){ close(pipefd[0]); }else{ close(pipefd[1]); } return 0; }
- 異步IO之libevent事件框架
事件驅(qū)動(dòng)相比較于輪詢更有效
參見libevent文檔
七、linux網(wǎng)絡(luò)編程
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
- 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
- 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
- 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
- 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
- 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
- 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
- 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
- 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
- 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
- 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
- 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
- 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
推薦閱讀更多精彩內(nèi)容
- 大綱 一.Socket簡介 二.BSD Socket編程準(zhǔn)備 1.地址 2.端口 3.網(wǎng)絡(luò)字節(jié)序 4.半相關(guān)與全相...
- 網(wǎng)絡(luò)中進(jìn)程之間如何通信 為了方便大家獲取源代碼,可以移步這里,GitHub源代碼 進(jìn)程通信的概念最初來源于單機(jī)系統(tǒng)...
- 大綱 一.Socket簡介 二.BSD Socket編程準(zhǔn)備 1.地址 2.端口 3.網(wǎng)絡(luò)字節(jié)序 4.半相關(guān)與全相...
- 原生API select intselect(int numfds, fd_set *readfds, fd_se...
- 下面為Daytime這個(gè)服務(wù)的源代碼例子,同時(shí)兼容IPV6和IPV4的地址,最后部分有更多說明。 單播模式下的Se...