今天交了,该了几改的程序。是一个服务器转发聊天程序,第一次是在别人例子的基础上直接做得。后来,发现我用得时阻塞,当时考虑的问题太多。包括一个包分了好几次才收到,或者一次收到了,相互独立且完整的几个包。
最重要的一个问题是,在使用费阻塞的io以后,因为对select的FD_ISSET理解错误(我认为是不是变化,但事实上是不是在集合内),我对select返回可读套接字后能不能一定读到数据产生了疑问。我相信了直觉。用了一个结构体,里面分别放上recvbuffer[1024]和已经接受的数据的大小。并且在接受数据的时候使用for(;;),于是出现了如下的变态函数。
void recvFromClient(int m, struct targetPack *ss, int *testint, char *content) { int recvsize; int totalsize; char *ptr; totalsize = 1024; ptr = conns[m].totalBuffer; memset(ss,0,sizeof(struct targetPack)); //memset(conns[m].totalBuffer,0,1024); for(;;) { recvsize = 0; recvsize = recv(conns[m].clientfd, ptr, totalsize-conns[m].edSize, MSG_DONTWAIT); if(recvsize > 0) { ptr += recvsize; conns[m].edSize += recvsize; printf("Received %d bytes\n",recvsize); } else if(recvsize < 0) { if(errno == EAGAIN) { errno=0; printf("errno == EAGAIN"); break; } else { perror("recv"); exit(EXIT_FAILURE); } } else if(recvsize = 0) { printf("client has discontent\n"); break; } if(conns[m].edSize >= border_size) { memcpy(ss, conns[m].totalBuffer, sizeof(struct targetPack)); printf("xml length is %d\n", ss->XmlLength); if((conns[m].edSize - border_size) > (ss->XmlLength)) { char xmlstr[900]; memset(xmlstr,'\0',900); memcpy(xmlstr, conns[m].totalBuffer+border_size,ss->XmlLength); char *file_name = "xmlFileFromClient.xml"; generateTimeFile(file_name, xmlstr); char xmlbuf[300]; getFDandContent(file_name, testint, xmlbuf); printf("content from client xml:\n%s\n", xmlbuf); sprintf(content, "%s", xmlbuf); conns[m].recverfd = *testint; } } else if(conns[m].edSize < 0) { printf("conns[m].edSize < 0: %d\n", conns[m].edSize); break; } } }
但是非阻塞的io的话,以我现在的理解,貌似只是让客服端出现问题的时候服务器不会卡在那里。
我把程序写成这样唯一的好处是,如果客户端吧一个完整的数据包,分为大小不同的很多包。在不同的时间里发过来,服务器仍然能够收到。
说好听点,就是网络延时极高的话。我的程序仍然可以收到数据。
unix环境高级编程和unix网络编程。有个问题,就是总是用他自己写的函数。估计他是想减少复杂性,却不知让人更不好理解。没仔细看过他给的例子,网上找得例子,要不太小儿科,要不没有一点注释。今天心血来潮用了下google
找到一个相对较好的程序,注释什么的也比较详细:
#include <stdio.h> #include <stdlib.h> #include <sys/ioctl.h> #include <sys/socket.h> #include <sys/time.h> #include <netinet/in.h> #include <errno.h> #define SERVER_PORT 12345 #define TRUE 1 #define FALSE 0 main (int argc, char *argv[]) { int i, len, rc, on = 1; int listen_sd, max_sd, new_sd; int desc_ready, end_server = FALSE; int close_conn; char buffer[80]; struct sockaddr_in addr; struct timeval timeout; struct fd_set master_set, working_set; /*************************************************************/ /* Create an AF_INET stream socket to receive incoming */ /* connections on */ /*************************************************************/ listen_sd = socket(AF_INET, SOCK_STREAM, 0); if (listen_sd < 0) { perror("socket() failed"); exit(-1); } /*************************************************************/ /* Allow socket descriptor to be reuseable */ /*************************************************************/ rc = setsockopt(listen_sd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)); if (rc < 0) { perror("setsockopt() failed"); close(listen_sd); exit(-1); } /*************************************************************/ /* Set socket to be non-blocking. All of the sockets for */ /* the incoming connections will also be non-blocking since */ /* they will inherit that state from the listening socket. */ /*************************************************************/ rc = ioctl(listen_sd, FIONBIO, (char *)&on); if (rc < 0) { perror("ioctl() failed"); close(listen_sd); exit(-1); } /*************************************************************/ /* Bind the socket */ /*************************************************************/ memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(INADDR_ANY); addr.sin_port = htons(SERVER_PORT); rc = bind(listen_sd, (struct sockaddr *)&addr, sizeof(addr)); if (rc < 0) { perror("bind() failed"); close(listen_sd); exit(-1); } /*************************************************************/ /* Set the listen back log */ /*************************************************************/ rc = listen(listen_sd, 32); if (rc < 0) { perror("listen() failed"); close(listen_sd); exit(-1); } /*************************************************************/ /* Initialize the master fd_set */ /*************************************************************/ FD_ZERO(&master_set); max_sd = listen_sd; FD_SET(listen_sd, &master_set); /*************************************************************/ /* Initialize the timeval struct to 3 minutes. If no */ /* activity after 3 minutes this program will end. */ /*************************************************************/ timeout.tv_sec = 3 * 60; timeout.tv_usec = 0; /*************************************************************/ /* Loop waiting for incoming connects or for incoming data */ /* on any of the connected sockets. */ /*************************************************************/ do { /**********************************************************/ /* Copy the master fd_set over to the working fd_set. */ /**********************************************************/ memcpy(&working_set, &master_set, sizeof(master_set)); /**********************************************************/ /* Call select() and wait 5 minutes for it to complete. */ /**********************************************************/ printf("Waiting on select()...\n"); rc = select(max_sd + 1, &working_set, NULL, NULL, &timeout); /**********************************************************/ /* Check to see if the select call failed. */ /**********************************************************/ if (rc < 0) { perror(" select() failed"); break; } /**********************************************************/ /* Check to see if the 5 minute time out expired. */ /**********************************************************/ if (rc == 0) { printf(" select() timed out. End program.\n"); break; } /**********************************************************/ /* One or more descriptors are readable. Need to */ /* determine which ones they are. */ /**********************************************************/ desc_ready = rc; for (i=0; i <= max_sd && desc_ready > 0; ++i) { /*******************************************************/ /* Check to see if this descriptor is ready */ /*******************************************************/ if (FD_ISSET(i, &working_set)) { /****************************************************/ /* A descriptor was found that was readable - one */ /* less has to be looked for. This is being done */ /* so that we can stop looking at the working set */ /* once we have found all of the descriptors that */ /* were ready. */ /****************************************************/ desc_ready -= 1; /****************************************************/ /* Check to see if this is the listening socket */ /****************************************************/ if (i == listen_sd) { printf(" Listening socket is readable\n"); /*************************************************/ /* Accept all incoming connections that are */ /* queued up on the listening socket before we */ /* loop back and call select again. */ /*************************************************/ do { /**********************************************/ /* Accept each incoming connection. If */ /* accept fails with EWOULDBLOCK, then we */ /* have accepted all of them. Any other */ /* failure on accept will cause us to end the */ /* server. */ /**********************************************/ new_sd = accept(listen_sd, NULL, NULL); if (new_sd < 0) { if (errno != EWOULDBLOCK) { perror(" accept() failed"); end_server = TRUE; } break; } /**********************************************/ /* Add the new incoming connection to the */ /* master read set */ /**********************************************/ printf(" New incoming connection - %d\n", new_sd); FD_SET(new_sd, &master_set); if (new_sd > max_sd) max_sd = new_sd; /**********************************************/ /* Loop back up and accept another incoming */ /* connection */ /**********************************************/ } while (new_sd != -1); } /****************************************************/ /* This is not the listening socket, therefore an */ /* existing connection must be readable */ /****************************************************/ else { printf(" Descriptor %d is readable\n", i); close_conn = FALSE; /*************************************************/ /* Receive all incoming data on this socket */ /* before we loop back and call select again. */ /*************************************************/ do { /**********************************************/ /* Receive data on this connection until the */ /* recv fails with EWOULDBLOCK. If any other */ /* failure occurs, we will close the */ /* connection. */ /**********************************************/ rc = recv(i, buffer, sizeof(buffer), 0); if (rc < 0) { if (errno != EWOULDBLOCK) { perror(" recv() failed"); close_conn = TRUE; } break; } /**********************************************/ /* Check to see if the connection has been */ /* closed by the client */ /**********************************************/ if (rc == 0) { printf(" Connection closed\n"); close_conn = TRUE; break; } /**********************************************/ /* Data was recevied */ /**********************************************/ len = rc; printf(" %d bytes received\n", len); /**********************************************/ /* Echo the data back to the client */ /**********************************************/ rc = send(i, buffer, len, 0); if (rc < 0) { perror(" send() failed"); close_conn = TRUE; break; } } while (TRUE); /*************************************************/ /* If the close_conn flag was turned on, we need */ /* to clean up this active connection. This */ /* clean up process includes removing the */ /* descriptor from the master set and */ /* determining the new maximum descriptor value */ /* based on the bits that are still turned on in */ /* the master set. */ /*************************************************/ if (close_conn) { close(i); FD_CLR(i, &master_set); if (i == max_sd) { while (FD_ISSET(max_sd, &master_set) == FALSE) max_sd -= 1; } } } /* End of existing connection is readable */ } /* End of if (FD_ISSET(i, &working_set)) */ } /* End of loop through selectable descriptors */ } while (end_server == FALSE); /*************************************************************/ /* Cleanup all of the sockets that are open */ /*************************************************************/ for (i=0; i <= max_sd; ++i) { if (FD_ISSET(i, &master_set)) close(i); } }