socket編程 -- epoll模型服務端/客戶端通信的實現

https://blog.csdn.net/y396397735/article/details/50680359

本例實現如下功能:?

支持多客戶端與一個服務端進行通信,客戶端給服務端發送字符串數據,服務端將字符串中小寫轉為大寫后發送回客戶端,客戶端打印輸出經轉換后的字符串。?
例如:發送 abcde,打印輸出 ABCDE?
服務端源碼如下:
/*server.c*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <errno.h>#define BUFSIZE 666
#define SERV_PORT 8000
#define OPEN_MAX 1024int main()
{int i, j, maxi, listenfd, connfd, sockfd;int nready, efd, res;ssize_t n;char buf[BUFSIZE], str[INET_ADDRSTRLEN];socklen_t clilen;int client[OPEN_MAX];struct sockaddr_in cliaddr, servaddr;struct epoll_event tep, ep[OPEN_MAX];//監聽事件/*分配一個網絡通信套接字,監聽文件描述符listenfd*/listenfd = socket(AF_INET, SOCK_STREAM, 0);/*初始化 IP類型 端口*/bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(SERV_PORT);/*將listenfd綁定服務端地址*/bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr));/*監聽請求*/listen(listenfd, 20);/*將客戶端標識初始化為-1*/for(i = 0; i < OPEN_MAX; i++){client[i] = -1;}maxi = -1;/*告訴內核要監聽的文件描述符個數 OPEN_MAX = 1024*/efd = epoll_create(OPEN_MAX);if(efd == -1){perror("epoll_create");}tep.events = EPOLLIN;/*監聽文件描述符的可讀事件*/tep.data.fd = listenfd;/*設置為監聽的文件描述符*//*控制epoll監控的文件描述符上的事件*/res = epoll_ctl(efd, EPOLL_CTL_ADD/*注冊新的fd到efd*/, listenfd, &tep);if(res == -1)perror("epoll_ctl");for(;;){/*等待所監控文件描述符上有事件的產生,阻塞監聽*/nready = epoll_wait(efd, ep, OPEN_MAX, -1);if(nready == -1)perror("epoll_wait");for(i = 0; i < nready; i++){if(!(ep[i].events & EPOLLIN))/*若不是EPOLLIN事件,不做往下的處理*/continue;if(ep[i].data.fd == listenfd){ /*若是EPOLLIN,執行連接,接受請求*/clilen = sizeof(cliaddr);/*接受請求,分配新文件描述符connfd進行通信*/connfd = accept(listenfd, (struct sockaddr*)&cliaddr, &clilen);printf("received from %s at PORT %d\n", (char*)inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)), ntohs(cliaddr.sin_port));/*若將此新客戶端添加至客戶端集中*/for(j = 0; j < OPEN_MAX; j++)if(client[j] < 0){client[j] = connfd;break;}if(j == OPEN_MAX)perror("客戶端超過限制");if(j > maxi)maxi = j;//保證maxi為最大文件描述符tep.events = EPOLLIN;tep.data.fd = connfd;res = epoll_ctl(efd, EPOLL_CTL_ADD/*注冊新的connfd到efd*/, connfd, &tep);if(res == -1)perror("epoll_ctl");}else{/*處理efd中監聽的客戶端請求*/sockfd = ep[i].data.fd;n = read(sockfd, buf, BUFSIZE);if(n == 0){ /*讀取若為空*/for(j = 0; j <= maxi; j++){if(client[j] == sockfd){client[j] = -1;break;}}/*清除對sockfd文件描述符事件的監聽*/res = epoll_ctl(efd, EPOLL_CTL_DEL, sockfd, NULL);if(res == -1)perror("epoll_ctl");close(sockfd);printf("client[%d] closed connection\n", j);}else{/*非空則處理客戶端信息*/for(j = 0; j<n; j++)buf[j] = toupper(buf[j]);/*寫入與客戶端通信的文件描述符sockfd*/write(sockfd, buf, n);}}}}/*關閉監聽*/close(listenfd);close(efd);return 0;
}

客戶端源碼如下:

/*client.c*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <errno.h>#define BUFSIZE 666
#define SERV_PORT 8000
#define OPEN_MAX 1024int main(int argc, char *argv[])
{struct sockaddr_in servaddr;char buf[BUFSIZE];int sockfd, n;sockfd = socket(AF_INET, SOCK_STREAM, 0);bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);servaddr.sin_port = htons(SERV_PORT);/*連接服務端*/connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr));while(fgets(buf, BUFSIZE, stdin) != NULL){/*通過sockfd給服務端發送數據*/write(sockfd, buf, strlen(buf));n = read(sockfd, buf, BUFSIZE);if(n == 0)printf("the other side has been closed.\n");else/*打印輸出服務端傳過來的數據*/write(STDOUT_FILENO, buf, n);}close(sockfd);return 0;
}

編譯及執行

在終端1先執行:

yu@ubuntu:~/Linux/217/epoll$ ls
client.c  server.c
yu@ubuntu:~/Linux/217/epoll$ gcc -o client client.c
yu@ubuntu:~/Linux/217/epoll$ gcc -o server server.c
yu@ubuntu:~/Linux/217/epoll$ ./server
  • 1
  • 2
  • 3
  • 4
  • 5

另開一終端2:

yu@ubuntu:~/Linux/217/epoll$ ./client
hey how are you
HEY HOW ARE YOU
bye
BYE

再另開一終端3:

yu@ubuntu:~/Linux/217/epoll$ ./client
i am here 007
I AM HERE 007
hehe
HEHE

最后開一終端4:

yu@ubuntu:~/Linux/217/epoll$ ./client
i am 008
I AM 008
zai jian
ZAI JIAN

關閉3個客戶端(CTR+C)后服務端窗口:

yu@ubuntu:~/Linux/217/epoll$ ./server
received from 127.0.0.1 at PORT 58385
received from 127.0.0.1 at PORT 58386
received from 127.0.0.1 at PORT 58387
client[2] closed connection
client[1] closed connection
client[0] closed connection

這里寫圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/383695.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/383695.shtml
英文地址,請注明出處:http://en.pswp.cn/news/383695.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Python3 面向對象程序設計

類的定義 Python使用class關鍵字來定義類 class Car:def infor(self):print("This is a car") car Car() car.infor()內置方法isinstance()來測試一個對象是否為某個類的實例 self參數 類的 所有實例方法都有一個默認的self參數&#xff0c;并且必須是方法的第一…

計算機網絡【二】物理層基礎知識

計算機網絡的性能 速率&#xff1a;連接在計算機網絡上的主機在數字信道上傳送數據位數的速率&#xff0c;也成為data rate 或bit rate&#xff0c;單位是b/s,kb/s,Mb/s,Gb/s。 我們平時所講的寬帶的速度是以字為單位的&#xff0c;但是實際中應用一般顯示的是字節 &#xff0…

Linux網絡編程——tcp并發服務器(多進程)

https://blog.csdn.net/lianghe_work/article/details/46503895一、tcp并發服務器概述一個好的服務器,一般都是并發服務器&#xff08;同一時刻可以響應多個客戶端的請求&#xff09;。并發服務器設計技術一般有&#xff1a;多進程服務器、多線程服務器、I/O復用服務器等。二、…

求序列第K大算法總結

參考博客&#xff1a;傳送門 在上面的博客中介紹了求序列第K大的幾種算法&#xff0c;感覺收益良多&#xff0c;其中最精巧的還是利用快速排序的思想O(n)查詢的算法。仔細學習以后我將其中的幾個實現了一下。 解法 1&#xff1a; 將亂序數組從大到小進行排序然后取出前K大&a…

Linux網絡編程——tcp并發服務器(多線程)

https://blog.csdn.net/lianghe_work/article/details/46504243tcp多線程并發服務器多線程服務器是對多進程服務器的改進&#xff0c;由于多進程服務器在創建進程時要消耗較大的系統資源&#xff0c;所以用線程來取代進程&#xff0c;這樣服務處理程序可以較快的創建。據統計&a…

計算機網絡【三】物理層數據通信

物理層傳輸媒介 導向傳輸媒體&#xff0c;比如光纖和銅線 雙絞線&#xff08;屏蔽雙絞線STP 五屏蔽雙絞線UTP&#xff09;電線扭曲在一起可以降低互相之間的電磁干擾 同軸電纜 (50歐姆的基帶同軸電纜&#xff0c;75歐姆的寬帶同軸電纜) 10M和100M網絡只使用了四根線&#xf…

02_算法分析

02_算法分析 0.1 算法的時間復雜度分析0.1.1 函數漸近增長概念&#xff1a;輸入規模n>2時&#xff0c;算法A1的漸近增長小于算法B1 的漸近增長隨著輸入規模的增大&#xff0c;算法的常數操作可以忽略不計測試二&#xff1a;隨著輸入規模的增大&#xff0c;與最高次項相乘的常…

Linux網絡編程——I/O復用之select詳解

https://blog.csdn.net/lianghe_work/article/details/46506143一、I/O復用概述I/O復用概念&#xff1a;解決進程或線程阻塞到某個 I/O 系統調用而出現的技術&#xff0c;使進程不阻塞于某個特定的 I/O 系統調I/O復用使用的場合&#xff1a;1.當客戶處理多個描述符&#xff08;…

Linux多進程拷貝文件

學習了mmap以后&#xff0c;實現一個簡單的小程序&#xff0c;進行多個進程對一個文件進行拷貝。 Linux mmap共享內存學習可以參考我的另一篇博客&#xff1a;傳送門 實現思想 我們可以將原來的文件利用mmap分成多個段分別進行傳輸。 實現代碼 #include<stdio.h> #…

斐波那契查找(Fibonacci Search)和折半查找

兩個查找算法都是針對有序數組進行查找&#xff0c;不同點在于分界點的取值不同。 算法介紹 折半查找很簡單&#xff0c;每次與當前區間的中點進行比較&#xff0c;然后決定查找前一部分還是后一部分。 Fibonacci查找利用了Fibonacci序列每一項等于前兩項和的特點進行劃分&a…

Linux網絡編程——tcp并發服務器(I/O復用之select)

https://blog.csdn.net/lianghe_work/article/details/46519633與多線程、多進程相比&#xff0c;I/O復用最大的優勢是系統開銷小&#xff0c;系統不需要建立新的進程或者線程&#xff0c;也不必維護這些線程和進程。代碼示例&#xff1a;#include <stdio.h> #include &l…

操作系統【二】死鎖問題以及處理方法

死鎖的概念 死鎖&#xff1a;在并發環境下&#xff0c;個進程因為競爭資源而造成的一種互相等待對方手里的資源&#xff0c;導致各進程都阻塞&#xff0c;無法向前推進的現象。 區別&#xff1a; 饑餓&#xff1a;由于長期得不到想要的資源進程無法向前推進的現象。死循環&a…

Linux網絡編程——I/O復用之poll函數

https://blog.csdn.net/lianghe_work/article/details/46534029一、回顧前面的selectselect優點&#xff1a;目前幾乎在所有的平臺上支持&#xff0c;其良好跨平臺支持也是它的一個優點select缺點&#xff1a;1.每次調用 select()&#xff0c;都需要把 fd 集合從用戶態拷貝到內…

操作系統【一】進程同步和信號量

基本概念 進程異步性特征&#xff1a;各并發執行的進程以各自獨立的&#xff0c;不可預知的速度向前推進。 進程同步又稱作直接制約關系&#xff0c;他是指為完成某種任務而建立的兩個或者多個進程&#xff0c;這些進程因為需要在某些位置上協調他們的工作順序而產生的制約關…

計算機網絡【四】數據鏈路層基本概念+點到點通信(PPP協議)

數據鏈路層基本概念 路由器是網絡層設備 數據鏈路層&#xff1a;數據管道&#xff0c;傳輸的是數據包加上發送地址&#xff0c;接收地址&#xff0c;校驗的數據幀 數據鏈路層的信道類型&#xff1a; 點到點信道&#xff1a;使用一對一的點到點通信方式&#xff08;兩個設備…

Linux網絡編程——tcp并發服務器(poll實現)

https://blog.csdn.net/lianghe_work/article/details/46535859想詳細徹底地了解poll或看懂下面的代碼請參考《Linux網絡編程——I/O復用之poll函數》 代碼&#xff1a;#include <string.h>#include <stdio.h>#include <stdlib.h>#include <unistd.h>#…

二分查找的最大比較次數

二分查找很簡單&#xff0c;可是對于一個區間長度為n的數組&#xff0c;最大的比較次數為多少呢&#xff1f; 對于標準的二分查找&#xff0c;我們每次從區間[l,r)中取一個值&#xff0c;和中間值mid(lr)>>1進行比較&#xff0c;然后將數組分為[l,mid) [mid1,r)&#xf…

Linux網絡編程——I/O復用函數之epoll

https://blog.csdn.net/lianghe_work/article/details/46544567一、epoll概述epoll 是在 2.6 內核中提出的&#xff0c;是之前的 select() 和 poll() 的增強版本。相對于 select() 和 poll() 來說&#xff0c;epoll 更加靈活&#xff0c;沒有描述符限制。epoll 使用一個文件描述…

操作系統【三】內存管理基礎+連續內存分配

內存的基礎知識 內存分為按字節編址&#xff08;8位&#xff09;和字編制&#xff08;不同計算機不一樣&#xff0c;64位計算機就是64位&#xff0c;即8個字節&#xff09; 相對地址邏輯地址 絕對地址物理地址 從邏輯地址到物理地址的轉換由裝入解決。 裝入的三種方式 絕對…