日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux下网络socket编程——实现服务器(select)与多个客户端通信

發(fā)布時間:2023/12/20 linux 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux下网络socket编程——实现服务器(select)与多个客户端通信 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

一、關于socket通信

服務器端工作流程:

  • 調(diào)用 socket() 函數(shù)創(chuàng)建套接字 用 bind() 函數(shù)將創(chuàng)建的套接字與服務端IP地址綁定
  • 調(diào)用listen()函數(shù)監(jiān)聽socket() 函數(shù)創(chuàng)建的套接字,等待客戶端連接 當客戶端請求到來之后
  • 調(diào)用 accept()函數(shù)接受連接請求,返回一個對應于此連接的新的套接字,做好通信準備
  • 調(diào)用 write()/read() 函數(shù)和 send()/recv()函數(shù)進行數(shù)據(jù)的讀寫,通過 accept() 返回的套接字和客戶端進行通信 關閉socket(close)

客戶端工作流程:

  • 調(diào)用 socket() 函數(shù)創(chuàng)建套接字
  • 調(diào)用 connect() 函數(shù)連接服務端
  • 調(diào)用write()/read() 函數(shù)或者 send()/recv() 函數(shù)進行數(shù)據(jù)的讀寫
  • 關閉socket(close)

?

二、用select實現(xiàn)服務器端編程:

select函數(shù)樓主在之前文章中(select函數(shù)用法)已經(jīng)提及,不在多做綴述。下面貼上服務器端代碼servce.c

#include <stdio.h> #include <netinet/in.h> //for souockaddr_in #include <sys/types.h> #include <sys/socket.h> #include <errno.h> #include <stdlib.h>#include <arpa/inet.h>//for select #include <sys/time.h> #include <sys/types.h> #include <unistd.h> #include <sys/select.h>#include <strings.h> //for bzero #include <string.h>#define BUFF_SIZE 1024 #define backlog 7 #define ser_port 11277 #define CLI_NUM 3int client_fds[CLI_NUM];int main(int agrc,char **argv) {int ser_souck_fd;int i; char input_message[BUFF_SIZE];char resv_message[BUFF_SIZE];struct sockaddr_in ser_addr;ser_addr.sin_family= AF_INET; //IPV4ser_addr.sin_port = htons(ser_port); ser_addr.sin_addr.s_addr = INADDR_ANY; //指定的是所有地址//creat socketif( (ser_souck_fd = socket(AF_INET,SOCK_STREAM,0)) < 0 ){perror("creat failure");return -1;} //bind soucketif(bind(ser_souck_fd, (const struct sockaddr *)&ser_addr,sizeof(ser_addr)) < 0){perror("bind failure");return -1;}//listenif(listen(ser_souck_fd, backlog) < 0) {perror("listen failure"); return -1;}//fd_setfd_set ser_fdset;int max_fd=1;struct timeval mytime;printf("wait for client connnect!\n");while(1){mytime.tv_sec=27;mytime.tv_usec=0;FD_ZERO(&ser_fdset);//add standard inputFD_SET(0,&ser_fdset);if(max_fd < 0){max_fd=0; }//add serverceFD_SET(ser_souck_fd,&ser_fdset);if(max_fd < ser_souck_fd){max_fd = ser_souck_fd;}//add client for(i=0;i<CLI_NUM;i++) //用數(shù)組定義多個客戶端fd{if(client_fds[i]!=0) {FD_SET(client_fds[i],&ser_fdset);if(max_fd < client_fds[i]){max_fd = client_fds[i]; }}}//select多路復用int ret = select(max_fd + 1, &ser_fdset, NULL, NULL, &mytime);if(ret < 0) { perror("select failure\n"); continue; } else if(ret == 0){printf("time out!");continue;}else{if(FD_ISSET(0,&ser_fdset)) //標準輸入是否存在于ser_fdset集合中(也就是說,檢測到輸入時,做如下事情){printf("send message to");bzero(input_message,BUFF_SIZE);fgets(input_message,BUFF_SIZE,stdin);for(i=0;i<CLI_NUM;i++){if(client_fds[i] != 0){printf("client_fds[%d]=%d\n", i, client_fds[i]);send(client_fds[i], input_message, BUFF_SIZE, 0);}}}if(FD_ISSET(ser_souck_fd, &ser_fdset)) {struct sockaddr_in client_address;socklen_t address_len;int client_sock_fd = accept(ser_souck_fd,(struct sockaddr *)&client_address, &address_len);if(client_sock_fd > 0){int flags=-1;//一個客戶端到來分配一個fd,CLI_NUM=3,則最多只能有三個客戶端,超過4以后跳出for循環(huán),flags重新被賦值為-1for(i=0;i<CLI_NUM;i++){if(client_fds[i] == 0){flags=i; client_fds[i] = client_sock_fd;break;}}if (flags >= 0){printf("new user client[%d] add sucessfully!\n",flags);}else //flags=-1{ char full_message[]="the client is full!can't join!\n";bzero(input_message,BUFF_SIZE);strncpy(input_message, full_message,100);send(client_sock_fd, input_message, BUFF_SIZE, 0);}} }}//deal with the messagefor(i=0; i<CLI_NUM; i++){if(client_fds[i] != 0){if(FD_ISSET(client_fds[i],&ser_fdset)){bzero(resv_message,BUFF_SIZE);int byte_num=read(client_fds[i],resv_message,BUFF_SIZE);if(byte_num > 0){printf("message form client[%d]:%s\n", i, resv_message);}else if(byte_num < 0){printf("rescessed error!");}//某個客戶端退出else //cancel fdset and set fd=0{printf("clien[%d] exit!\n",i);FD_CLR(client_fds[i], &ser_fdset);client_fds[i] = 0;// printf("clien[%d] exit!\n",i);continue; //這里如果用break的話一個客戶端退出會造成服務器也退出。 }}}} }return 0; }

select實現(xiàn)多路復用,多路復用,顧名思義,就是說各做各的事,標準輸入事件到來,有相關函數(shù)處理。服務器處理服務器的事件,客戶端到來時有相關函數(shù)對其進行處理,通過select遍歷各fd的讀寫情況,就不用擔心阻塞了。

三、用epoll實現(xiàn)客戶端編程:

1、客戶端程序(epoll_client.c):

#include<stdio.h> #include<stdlib.h> #include<netinet/in.h> #include<sys/socket.h> #include<arpa/inet.h> #include<string.h> #include<unistd.h> #include <sys/epoll.h> #include <errno.h> #include <fcntl.h>#define BUFFER_SIZE 1024 int main(int argc, const char * argv[]) { int i,n;int connfd,sockfd;struct epoll_event ev,events[20]; //ev用于注冊事件,數(shù)組用于回傳要處理的事件int epfd=epoll_create(256);//創(chuàng)建一個epoll的句柄,其中256為你epoll所支持的最大句柄數(shù)struct sockaddr_in client_addr;struct sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_port = htons(11277); server_addr.sin_addr.s_addr =INADDR_ANY; bzero(&(server_addr.sin_zero), 8); int server_sock_fd = socket(AF_INET, SOCK_STREAM, 0); ev.data.fd=server_sock_fd;//設置與要處理的事件相關的文件描述符ev.events=EPOLLIN|EPOLLET;//設置要處理的事件類型epoll_ctl(epfd,EPOLL_CTL_ADD,server_sock_fd,&ev);//注冊epoll事件if(server_sock_fd == -1) { perror("socket error"); return 1; } char recv_msg[BUFFER_SIZE]; char input_msg[BUFFER_SIZE]; if(connect(server_sock_fd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_in)) == 0) { for(;;){int nfds=epoll_wait(epfd,events,20,500);//等待epoll事件的發(fā)生for(i=0;i<nfds;++i){ if(events[i].events&EPOLLOUT) //有數(shù)據(jù)發(fā)送,寫socket{bzero(input_msg, BUFFER_SIZE); fgets(input_msg, BUFFER_SIZE, stdin); sockfd = events[i].data.fd;write(sockfd, recv_msg, n);ev.data.fd=sockfd;ev.events=EPOLLIN|EPOLLET;epoll_ctl(epfd,EPOLL_CTL_ADD,sockfd,&ev);} else if(events[i].events&EPOLLIN)//有數(shù)據(jù)到來,讀socket{bzero(recv_msg, BUFFER_SIZE);if((n = read(server_sock_fd, recv_msg, BUFFER_SIZE)) <0 ){printf("read error!");}ev.data.fd=server_sock_fd;ev.events=EPOLLOUT|EPOLLET;printf("%s\n",recv_msg);}} }} return 0; }

2、關于epoll函數(shù):

相比于select,epoll最大的好處在于它不會隨著監(jiān)聽fd數(shù)目的增長而降低效率。因為在內(nèi)核中的select實現(xiàn)中,它是采用輪詢來處理的,輪詢的fd數(shù)目越多,自然耗時越多。并且,在linux/posix_types.h頭文件有這樣的聲明:
#define __FD_SETSIZE 1024
表示select最多同時監(jiān)聽1024個fd

一共三個函數(shù):

1、 int epoll_create (int size); 創(chuàng)建一個epoll的句柄

size用來告訴內(nèi)核這個監(jiān)聽的數(shù)目一共有多大。這個參數(shù)不同于select()中的第一個參數(shù),給出最大監(jiān)聽的fd+1的值。需要注意的是,當創(chuàng)建好epoll句柄后,它就是會占用一個fd值,在linux下如果查看/proc/進程id/fd/,是能夠看到這個fd的,所以在使用完epoll后,必須調(diào)用close()關閉,否則可能導致fd被耗盡。

2、 int epoll_ctl (int epfd , int op, int fd, struct epoll_event *event);

epoll的事件注冊函數(shù),它不同與select()是在監(jiān)聽事件時告訴內(nèi)核要監(jiān)聽什么類型的事件,而是在這里先注冊要監(jiān)聽的事件類型。第一個參數(shù)是epoll_create()的返回值,第二個參數(shù)表示動作,用三個宏來表示:

EPOLL_CTL_ADD:注冊新的fd到epfd中;

EPOLL_CTL_MOD:修改已經(jīng)注冊的fd的監(jiān)聽事件;

EPOLL_CTL_DEL:從epfd中刪除一個fd;

第三個參數(shù)是需要監(jiān)聽的fd

第四個參數(shù)是告訴內(nèi)核需要監(jiān)聽什么事,struct epoll_event結構如下:

struct epoll_event{__uint32_t events; /* Epoll events */epoll_data_t data; /* User data variable */ };

  

typedef union epoll_data{void *ptr;int fd;__uint32_t u32;__uint64_t u64; } epoll_data_t;

events可以是以下幾個宏的集合:

  • EPOLLIN :表示對應的文件描述符可以讀(包括對端SOCKET正常關閉);
  • EPOLLOUT:表示對應的文件描述符可以寫;
  • EPOLLPRI:表示對應的文件描述符有緊急的數(shù)據(jù)可讀(這里應該表示有帶外數(shù)據(jù)到來);
  • EPOLLERR:表示對應的文件描述符發(fā)生錯誤;
  • EPOLLHUP:表示對應的文件描述符被掛斷;
  • EPOLLET: 將EPOLL設為邊緣觸發(fā)(Edge Triggered)模式,這是相對于水平觸發(fā)(Level Triggered)來說的。
  • EPOLLONESHOT:只監(jiān)聽一次事件,當監(jiān)聽完這次事件之后,如果還需要繼續(xù)監(jiān)聽這個socket的話,需要再次把這個socket加入到EPOLL隊列里
3、 int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

等待事件的產(chǎn)生,類似于select()調(diào)用。

參數(shù)events用來從內(nèi)核得到事件的集合,

maxevents告之內(nèi)核這個events有多大,這個 maxevents的值不能大于創(chuàng)建epoll_create()時的size,

參數(shù)timeout是超時時間(毫秒,0會立即返回,-1將不確定,也有說法說是永久阻塞)。該函數(shù)返回需要處理的事件數(shù)目,如返回0表示已超時。

使用步驟:

<1>首先通過create_epoll(int maxfds)來創(chuàng)建一個epoll的句柄,其中maxfds為你epoll所支持的最大句柄數(shù)。這個函數(shù)會返回一個新的epoll句柄,之后的所有操作將通過這個句柄來進行操作。在用完之后,記得用close()來關閉這個創(chuàng)建出來的epoll句柄。

<2>然后每一幀的調(diào)用epoll_wait (int epfd, epoll_event events, int max events, int timeout) 來查詢所有的網(wǎng)絡接口。

<3>kdpfd為用epoll_create創(chuàng)建之后的句柄,events是一個epoll_event*的指針,當epoll_wait這個函數(shù)操作成功之后,epoll_events里面將儲存所有的讀寫事件。max_events是當前需要監(jiān)聽的所有socket句柄數(shù)。最后一個timeout是 epoll_wait的超時,為0的時候表示馬上返回,為-1的時候表示一直等下去,直到有事件范圍,為任意正整數(shù)的時候表示等這么長的時間,如果一直沒有事件,則返回。一般如果網(wǎng)絡主循環(huán)是單獨的線程的話,可以用-1來等,這樣可以保證一些效率,如果是和主邏輯在同一個線程的話,則可以用0來保證主循環(huán)的效率。 epoll_wait返回之后應該是一個循環(huán),遍歷所有的事件。

基本上都是如下的框架:

for( ; ; ){nfds = epoll_wait(epfd,events,20,500);for(i=0;i<nfds;++i){if(events[i].data.fd==listenfd) //有新的連接{connfd = accept(listenfd,(sockaddr *)&clientaddr, &clilen); //accept這個連接ev.data.fd=connfd;ev.events=EPOLLIN|EPOLLET;epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev); //將新的fd添加到epoll的監(jiān)聽隊列中}else if( events[i].events&EPOLLIN ) //接收到數(shù)據(jù),讀socket{n = read(sockfd, line, MAXLINE)) < 0 //讀ev.data.ptr = md; //md為自定義類型,添加數(shù)據(jù)ev.events=EPOLLOUT|EPOLLET;epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);//修改標識符,等待下一個循環(huán)時發(fā)送數(shù)據(jù),異步處理的精髓}else if(events[i].events&EPOLLOUT) //有數(shù)據(jù)待發(fā)送,寫socket{struct myepoll_data* md = (myepoll_data*)events[i].data.ptr; //取數(shù)據(jù)sockfd = md->fd;send( sockfd, md->ptr, strlen((char*)md->ptr), 0 ); //發(fā)送數(shù)據(jù)ev.data.fd=sockfd;ev.events=EPOLLIN|EPOLLET;epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev); //修改標識符,等待下一個循環(huán)時接收數(shù)據(jù)}else{//其他的處理}}}

  

?

轉載于:https://www.cnblogs.com/wuyepeng/p/9726771.html

總結

以上是生活随笔為你收集整理的Linux下网络socket编程——实现服务器(select)与多个客户端通信的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 欧美日b片 | 亚洲最大视频网 | 草逼免费视频 | 色综合激情 | 黄色香蕉软件 | 欧美久久久久久久久久久久久久 | 国产69精品久久久久久久 | 国产suv精品一区二区四 | 亚洲福利社 | 一级黄色美女 | 午夜成人免费影院 | 无码aⅴ精品一区二区三区浪潮 | 丁香花高清在线观看完整动漫 | 中国老熟女重囗味hdxx | 日韩一区二区影院 | 男女床上拍拍拍 | 国产又色又爽无遮挡免费 | 麻豆三级| 特级a级片| 女教师痴汉调教hd中字 | 国产喷水福利在线视频 | 欧美一级爽aaaaa大片 | 成年人国产精品 | 在线观看特色大片免费网站 | 婷婷国产一区二区三区 | 国产精品自拍偷拍视频 | 特级西西444www高清大胆免费看 | 伊人中文字幕 | 美国一级片网站 | 久久国产精品一区 | 嫩草国产精品 | 成人在线播放视频 | 欧美色亚洲| 小视频黄色 | 国产第九页 | 99久久久无码国产精品性色戒 | 看一级黄色 | 人人澡人人添 | 成人日韩欧美 | 国产麻豆一精品一av一免费 | 色不卡 | 国产乱妇4p交换乱免费视频 | 在线观看免费高清在线观看 | 男女一级特黄 | 国语对白一区二区 | 五月婷婷国产 | 中文字幕在线精品 | 最好看十大无码av | 国产精品一区二区三 | 插我一区二区在线观看 | 欧美777| 蜜桃视频欧美 | 亚洲激情图 | 五月激情综合网 | 男女做爰真人视频直播 | 91尤物国产福利在线观看 | 免费一区视频 | 影音先锋男人的天堂 | www.精品久久| 偷偷操不一样的久久 | 欧美大肥婆大肥bbbbb | 亚洲视频自拍 | 中文亚洲av片不卡在线观看 | 日韩第二页 | 亚洲欧美日韩另类在线 | 欧美大片免费高清观看 | 一区高清 | 伊人网在线免费观看 | 松本一香在线播放 | 国产精品久久国产 | 最近最好的2019中文 | 国产人妻aⅴ色偷 | 国产免费黄色 | 亚洲激情综合网 | 欧美一区国产一区 | 欧美性猛交xxxx久久久 | 成年人毛片视频 | 国产毛片自拍 | 亚洲在线精品 | 91网址入口 | 美国av片 | 久久伊人精品视频 | 日韩插 | 欧美日日夜夜 | 日韩a毛片 | 精品免费看 | 亚洲欧美一区二区三区孕妇 | 色屁屁ts人妖系列二区 | 国产伦精品一区二区三区视频1 | 亚洲视频二区 | 污视频在线观看网站 | 欧美用舌头去添高潮 | 三级自拍 | 黄色视屏在线播放 | 在线日本中文字幕 | 51嘿嘿嘿国产精品伦理 | 懂色av一区二区三区免费 | 99热精品久久| 久久久www免费人成人片 |