- 論壇徽章:
- 0
|
本帖最后由 wander__漫游世界 于 2014-09-04 19:43 編輯
學(xué)習(xí)select實(shí)現(xiàn)單線程實(shí)現(xiàn)并發(fā),在網(wǎng)上找到兩種版本。
一種能夠?qū)崿F(xiàn)并發(fā)通信,一種只有一個(gè)客戶段能夠通信,十分不解。下面上源碼,求幫忙分析一下。
第一種能實(shí)現(xiàn)并發(fā):- /*
- Thu Sep 4,4:12PM,2014
- @wander
- email:czpwander@gmail.com
- process: select socket server
- ps:?jiǎn)芜M(jìn)程用select實(shí)現(xiàn)并發(fā)socket
- 網(wǎng)上有幾種不同的實(shí)現(xiàn)方式,本程序是通過(guò)主進(jìn)程實(shí)現(xiàn)監(jiān)聽socket,然后用select實(shí)現(xiàn)連接socket
- select 程序中是每次有一個(gè)新連接,然后將連接描述符添加到select描述符中,也就是一個(gè)一個(gè)添加。
- * */
- #include <stdio.h>
- #include <sys/socket.h>
- #include <sys/types.h>
- #include <string.h>
- #include <netinet/in.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <sys/select.h>
- #include <sys/time.h>
- #define LEN 1024
- #define PORT 60606
- #define BACKLOG 2
- struct client{
- int fd;
- struct sockaddr_in cli;
- };
- int main(void)
- {
- struct client cl[FD_SETSIZE];//客戶端數(shù)組
- int connfd;
- int i;
- int maxfd,sockfd;
- int maxi = -1;
- int nready;
- char buf[LEN];// 接收發(fā)送socket buf
- int listenfd = socket(AF_INET,SOCK_STREAM,0);//創(chuàng)建監(jiān)聽socket
- if(listenfd < 0){
- perror("socket error\n");
- exit(-1);
- }
- int opt = SO_REUSEADDR;
- setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(int));//設(shè)置解綁地址
- struct sockaddr_in ser;
- socklen_t ser_len = sizeof(ser);
- ser.sin_family = AF_INET;
- ser.sin_port = htons(PORT);
- ser.sin_addr.s_addr = htonl(INADDR_ANY);
-
- int ret = bind(listenfd,(struct sockaddr*)&ser,ser_len);//bind
- if(ret < 0){
- perror("bind error\n");
- exit(-1);
- }
-
- ret = listen(listenfd,BACKLOG); //listen
- if(ret < 0){
- perror("listen error\n");
- exit(-1);
- }
-
- //select
- maxfd = listenfd;
- for(i = 0;i < FD_SETSIZE;i ++){
- cl[i].fd = -1;
- }
- fd_set read_fds;
- fd_set r_fds;
- FD_ZERO(&read_fds);
- FD_ZERO(&r_fds);
- FD_SET(listenfd,&read_fds);
- while(1){
- memset(buf,0,LEN);
- struct sockaddr_in addr;
- r_fds = read_fds;//將select描述符集,賦值給r_fds
- socklen_t addr_len = sizeof(addr);
- bzero(&addr,addr_len);
- nready = select(maxfd+1,&r_fds,NULL,NULL,NULL);// select
- if(ret < 0){
- perror("select error\n");
- exit(-1);
- }
- if(FD_ISSET(listenfd,&r_fds)){// 判斷是否有監(jiān)聽socket
- printf("get new connect\n");
- connfd = accept(listenfd,(struct sockaddr *)&addr,&addr_len);// accept socket
- if(connfd < 0){
- perror("accept error\n");
- exit(-1);
- }
- printf("accept ok\n");
-
- //將得到客戶端地址 連接socketfd
- for(i = 0;i < FD_SETSIZE;i ++){
- if(cl[i].fd < 0){
- cl[i].fd = connfd;
- cl[i].cli = addr;
- printf("got connect from %s\n",inet_ntoa(cl[i].cli.sin_addr));
- //printf("got connect from client %d\n",i);
- break;
- }
- }
- if(i == FD_SETSIZE){// 連接達(dá)到最大值
- printf("too many client connect\n");
- continue;
- }
- FD_SET(connfd,&read_fds);//將連接sockefd添加到select描述符集中
- if(connfd > maxfd)// 判斷最大
- maxfd = connfd;
- if(i > maxi){// 判斷最大連接數(shù)
- maxi = i;
- printf("maxi:%d\n",maxi);
- }
- if(--nready <= 0)
- continue;//如果沒有連接,就繼續(xù)循環(huán)
- }
- for(i = 0;i <= maxi;i ++){// 判斷是否有數(shù)據(jù)發(fā)送
- if((sockfd = cl[i].fd) < 0) //如果小于0,表示沒有客戶端連接
- continue;
- if(FD_ISSET(sockfd,&r_fds)){
- printf("get the data\n");
- int r_size = recv(sockfd,buf,LEN,0);//接收數(shù)據(jù)
- if(r_size == 0){
- close(sockfd);
- printf("close the %d client\n",i);
- }
- else if(r_size > 0){
- printf("the client %d:",i);
- printf("%s \n",buf);
- }
- if(--nready <= 0)
- break;
- }
- }
- }
-
- close(connfd);
- close(listenfd);
- }
復(fù)制代碼 第二種,網(wǎng)上很多是第二種,但是能夠建立連接,但是不能實(shí)現(xiàn)并發(fā)通信- /* 實(shí)現(xiàn)功能:通過(guò)select處理多個(gè)socket
- * 監(jiān)聽一個(gè)端口,監(jiān)聽到有鏈接時(shí),添加到select的w.
- */
- //#include "select.h"
- #include <stdio.h>
- #include <stdlib.h>
- #include <sys/socket.h>
- #include <sys/select.h>
- #include <sys/time.h>
- #include <netinet/in.h>
- #include <string.h>
- typedef struct _CLIENT{
- int fd;
- struct sockaddr_in addr; /* client's address information */
- } CLIENT;
- #define MYPORT 60606
- //最多處理的connect
- #define BACKLOG 2
- //最多處理的connect
- CLIENT client[BACKLOG];
- //當(dāng)前的連接數(shù)
- int currentClient = 0;
- //數(shù)據(jù)接受 buf
- #define REVLEN 10
- char recvBuf[REVLEN];
- //顯示當(dāng)前的connection
- void showClient();
- int main()
- {
- int i, ret, sinSize;
- int recvLen = 0;
- fd_set readfds, writefds;
- int sockListen, sockSvr, sockMax;
- struct timeval timeout;
- struct sockaddr_in server_addr;
- struct sockaddr_in client_addr;
- //將客戶端端描述符置1
- for(i=0; i<BACKLOG; i++)
- {
- client[i].fd = -1;
- }
- //socket
- if((sockListen=socket(AF_INET, SOCK_STREAM, 0)) < 0)
- {
- printf("socket error\n");
- return -1;
- }
- int opt = SO_REUSEADDR;
- setsockopt(sockListen,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(int));
- bzero(&server_addr, sizeof(server_addr));
- server_addr.sin_family = AF_INET;
- server_addr.sin_port = htons(MYPORT);
- server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
- //bind
- if(bind(sockListen, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0)
- {
- printf("bind error\n");
- return -1;
- }
- //listen
- if(listen(sockListen, 5) < 0)
- {
- printf("listen error\n");
- return -1;
- }
- for(i=0; i<BACKLOG; i++)
- {
- client[i].fd = -1;
- }
- //select
- while(1)
- {
- FD_ZERO(&readfds);
- FD_SET(sockListen, &readfds);
- sockMax = sockListen;
-
- //加入client
- for(i=0; i<BACKLOG; i++)
- {
- if(client[i].fd >0)
- {
- FD_SET(client[i].fd, &readfds);
- if(sockMax<client[i].fd)
- sockMax = client[i].fd;
- }
- }
-
- timeout.tv_sec=10;
- timeout.tv_usec=0;
- //select
- ret = select((int)sockMax+1, &readfds, NULL, NULL, &timeout);
- if(ret < 0)
- {
- printf("select error\n");
- break;
- }
- else if(ret == 0)
- {
- printf("timeout ...\n");
- continue;
- }
- //printf("test111\n");
-
- //讀取數(shù)據(jù)
- for(i=0; i<BACKLOG; i++)
- {
- if(client[i].fd>0 && FD_ISSET(client[i].fd, &readfds))
- {
- if(recvLen != REVLEN)
- {
- while(1)
- {
- //recv數(shù)據(jù)
- ret = recv(client[i].fd, (char *)recvBuf+recvLen, REVLEN-recvLen, 0);
- if(ret == 0)
- {
- client[i].fd = -1;
- recvLen = 0;
- break;
- }
- else if(ret < 0)
- {
- client[i].fd = -1;
- recvLen = 0;
- break;
- }
- //數(shù)據(jù)接受正常
- recvLen = recvLen+ret;
- if(recvLen<REVLEN)
- {
- continue;
- }
- else
- {
- //數(shù)據(jù)接受完畢
- printf("%s, buf = %s\n", inet_ntoa(client[i].addr.sin_addr) , recvBuf);
- //close(client[i].fd);
- //client[i].fd = -1;
- recvLen = 0;
- break;
- }
- }
- }
- }
- }
-
- //如果可讀
- if(FD_ISSET(sockListen, &readfds))
- {
- printf("isset\n");
- sockSvr = accept(sockListen, NULL, NULL);//(struct sockaddr*)&client_addr
-
- if(sockSvr == -1)
- {
- printf("accpet error\n");
- }
- else
- {
- currentClient++;
- }
-
- for(i=0; i<BACKLOG; i++)
- {
- if(client[i].fd < 0)
- {
- client[i].fd = sockSvr;
- client[i].addr = client_addr;
- printf("You got a connection from %s \n",inet_ntoa(client[i].addr.sin_addr) );
- break;
- }
- }
- //close(sockListen);
- }
- }
- printf("test\n");
- return 0;
- }
- //顯示當(dāng)前的connection
- void showClient()
- {
- int i;
- printf("client count = %d\n", currentClient);
- for(i=0; i<BACKLOG; i++)
- {
- printf("[%d] = %d", i, client[i].fd);
- }
- printf("\n");
- }
復(fù)制代碼 |
|