socket编程(十)

来源:互联网 发布:查询pid占用了哪个端口 编辑:程序博客网 时间:2024/06/02 05:36

1.close与shutdown区别

(1)close终止了数据传输的两个方向

(2)shutdown可以有选择的终止某个方向的数据传送或者终止数据的两个方向

(3)shutdown how=1就可以保证对等方接收到一个EOF字符,而不管其他进程是否已经打开了套接字。而close不能保证,直到套接字引用计数减为0时才发送。也就是说直到所有的进程都关闭了套接字。

(4)int conn;

          conn = accept(sock, NULL, NULL);

          pid_t pid = fork();

          if(-1 == pid)

      ERR_EXIT("fork");

          if(0 == pid) {

                      close(sock);

                      //通信

                       close(conn);  //这时回向对方发送FIN段(因为这个时候conn引用计数减为0)

           } else if (pid > 0) {

                       shutdown(conn, SHUT,WR);  // 不会引用计数,直接向对方发送FIN段

                       close(conn);  //不会像客户端发送FIN段,仅仅只是将套接字的引用记数减1

           }

2.进一步改进回射客户程序

(1)原始客户端使用close(sock),如果客户端发送到服务器,第一次发送AAAA,第二次发送BBBB,ctrl+D关闭客户端

          [1]客户端无法收到服务器端返回的数据;原因:close关闭双向数据流,客户端不能接收服务器端发送的数据

          [2]服务器端会挂掉;原因:客户端ctrl+D,客户端发送FIN段到服务器端,服务器端sleep(4)后,向发送AAAA,此时客户端不能接收,客户端向服务器发送RST段,服务发送BBBB,服务器端产生SIGPIPE信号。服务器端设置signal(SIGPIPE, SIG_IGN);

(2)改进后客户端使用shutdown(sock, SHUT_WR);客户端不能向服务器端发送数据,但是可以接收server的数据。

           服务器设置signal(SIGPIPE, SIG_IGN);保证server不会挂掉


echoclient.cpp

#include <unistd.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <signal.h>#include <stdlib.h>#include <stdio.h>#include <errno.h>#include <string.h>#define ERR_EXIT(m) \    do \    { \    perror(m); \    exit(EXIT_FAILURE); \    }while(0)    ssize_t readn(int fd, void* buf, size_t count) {size_t nleft = count;ssize_t nread;  //已经读了char* bufp = (char*)buf;while(nleft > 0) {if((nread = read(fd, bufp, nleft)) < 0) {if(errno == EINTR) continue;return -1;} else if (0 == nread) {return count - nleft;}bufp += nread;nleft -= nread;}return count;}ssize_t writen(int fd, void* buf, size_t count) {size_t nleft = count;ssize_t nwrite;char* bufp = (char*)buf;while(nleft > 0) {if((nwrite = write(fd, bufp, nleft)) < 0) {if(errno == EINTR)continue;return -1;} else if(0 == nwrite) {continue;}bufp += nwrite;nleft -= nwrite;}return count;}ssize_t recv_peek(int sockfd, void* buf, size_t len) {while(1) {int ret = recv(sockfd, buf, len, MSG_PEEK);if(-1 == ret && errno == EINTR) continue;return ret;}}//读取遇到\r\n截止,最大不能超过maxlinessize_t readline(int sockfd, void* buf, size_t maxline) {int ret;int nread;char* bufp = (char*)buf;int nleft = maxline;while(1) {ret = recv_peek(sockfd, bufp, nleft);if(ret < 0) //信号中断 return ret;if(0 == ret)  //表示对方关闭套接口 return ret;nread = ret;int i;//该缓冲区中有\n,read读走for(i=0; i<nread; i++) {if(bufp[i] == '\n') {ret = readn(sockfd, bufp, i+1);  //包括\n都读走if(ret != i+1) exit(EXIT_FAILURE);return ret;}}//没有\n,read先读走这部分,然后bufp偏移if(nread > nleft) exit(EXIT_FAILURE);nleft -= nread;ret = readn(sockfd, bufp, nread); if(ret != nread) exit(EXIT_FAILURE);bufp += nread;}return -1;}void echo_cli(int sock) {fd_set rset;FD_ZERO(&rset);int nready;int maxfd;int fd_stdin = fileno(stdin);if(fd_stdin > sock) maxfd = fd_stdin;elsemaxfd = sock;  char sendbuf[1024] = {0};  char recvbuf[1024] = {0};int stdineof = 0;while(1) {if(stdineof == 0) FD_SET(fd_stdin, &rset);FD_SET(sock, &rset);nready = select(maxfd+1, &rset, NULL, NULL, NULL);if(-1 == nready) ERR_EXIT("select");if(0 == nready) continue;if(FD_ISSET(sock, &rset)) {int ret = readline(sock, recvbuf, sizeof(recvbuf));if(-1 == ret) {ERR_EXIT("readline");} else if(0 == ret) {printf("server close\n");break;}fputs(recvbuf, stdout);memset(sendbuf, 0, sizeof(sendbuf));memset(recvbuf, 0, sizeof(recvbuf));}if(FD_ISSET(fd_stdin, &rset)) {if(fgets(sendbuf, sizeof(sendbuf), stdin) == NULL) {stdineof = 1;/* close(sock);sleep(5);exit(EXIT_FAILURE);*/shutdown(sock, SHUT_WR);   } else {writen(sock, sendbuf, strlen(sendbuf));memset(sendbuf, 0, sizeof(sendbuf));}}}close(sock);}void handle_sigpipe(int sig) {printf("recv a sig=%d\n", sig); }int main () {//signal(SIGPIPE, handle_sigpipe);signal(SIGPIPE, SIG_IGN);  int sock;if(( sock= socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)  ERR_EXIT("socket");    sock = socket(PF_INET, SOCK_STREAM, 0);    struct sockaddr_in servaddr;  memset(&servaddr, 0, sizeof(servaddr));  servaddr.sin_family = AF_INET;  servaddr.sin_port = htons(5188);  servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");    if(connect(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)  ERR_EXIT("sockect");    //获得本端ip,portstruct sockaddr_in localaddr;socklen_t addrlen= sizeof(localaddr);if(getsockname(sock,(struct sockaddr*)&localaddr, &addrlen) < 0)ERR_EXIT("getsockname");printf("ip=%s port=%d\n", inet_ntoa(localaddr.sin_addr), ntohs(localaddr.sin_port));struct sockaddr_in connaddr;socklen_t connlen = sizeof(connaddr);//通过getpeername得到对端addr,port[conn必须是已连接的套接字]getpeername(sock, (struct sockaddr*)&connaddr, &connlen);printf("ip=%s port=%d\n", inet_ntoa(connaddr.sin_addr), ntohs(connaddr.sin_port));  echo_cli(sock);  return 0;}

echoserver.cpp

#include <unistd.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <signal.h>#include <sys/wait.h>#include <stdlib.h>#include <stdio.h>#include <errno.h>#include <string.h>#define ERR_EXIT(m) \    do \    { \    perror(m); \    exit(EXIT_FAILURE); \    }while(0)    /* param1:fd, param2:buf, param3:countreturn:读取成功字节数ssize_t:有符号size_t:无符号 */ssize_t readn(int fd, void* buf, size_t count) {size_t nleft = count;ssize_t nread;  //已经读了char* bufp = (char*)buf;while(nleft > 0) {if((nread = read(fd, bufp, nleft)) < 0) {if(errno == EINTR) continue;return -1;} else if (0 == nread) {return count - nleft;}bufp += nread;nleft -= nread;}return count;}/*param1:fd, param2:buf, param3:countreturn:已经发送了多少*/ssize_t writen(int fd, void* buf, size_t count) {size_t nleft = count;ssize_t nwrite;char* bufp = (char*)buf;while(nleft > 0) {if((nwrite = write(fd, bufp, nleft)) < 0) {if(errno == EINTR)continue;return -1;} else if(0 == nwrite) {continue;}bufp += nwrite;nleft -= nwrite;}return count;}//从套接口接收数据,但是不从缓冲区中移除MSG_PEEK//只要有偷看到数据就接收,没有头看到就是阻塞//对方套接口关闭,返回0ssize_t recv_peek(int sockfd, void* buf, size_t len) {while(1) {int ret = recv(sockfd, buf, len, MSG_PEEK);if(-1 == ret && errno == EINTR) continue;return ret;}}//读取遇到\r\n截止,最大不能超过maxlinessize_t readline(int sockfd, void* buf, size_t maxline) {int ret;int nread;int nleft = maxline;char* bufp = (char*)buf;while(1) {//信号中断已在recv_peek中处理ret = recv_peek(sockfd, bufp, nleft);if(ret < 0)  return ret;if(0 == ret)  //表示对方关闭套接口 return ret;nread = ret;  //实际偷看到的字节数int i;//该缓冲区中有\n,read读走for(i=0; i<nread; i++) {if(bufp[i] == '\n') {ret = readn(sockfd, bufp, i+1);  //包括\n都读走if(ret != i+1) exit(EXIT_FAILURE);return ret;}}//没有\n,read先读走这部分,然后bufp偏移if(nread > nleft) exit(EXIT_FAILURE);nleft -= nread;  //更新剩余量ret = readn(sockfd, bufp, nread); if(ret != nread) exit(EXIT_FAILURE);bufp += nread;}return -1;}void echo_srv(int conn) {  char recvbuf[1024];  //struct packet recvbuf;int n;  while(1) {  memset(&recvbuf, 0, sizeof(recvbuf));  int ret = readline(conn, recvbuf, 1024);if(-1 == ret) {ERR_EXIT("readline");} else if(0 == ret) {printf("client close\n");break;}  fputs(recvbuf, stdout);  writen(conn, recvbuf, strlen(recvbuf));  }}void handle_sigchild(int sig) {//wait(NULL);//waitpid(-1, NULL, WNOHANG);while(waitpid(-1, NULL, WNOHANG) >0);}void handle_sigpipe(int sig) {printf("recv a sig=%d\n", sig);}int main () {signal(SIGPIPE, handle_sigpipe);//1.避免僵尸进程//signal(SIGCHLD, SIG_IGN);signal(SIGCHLD, handle_sigchild);  int listenfd;if(( listenfd= socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)  //if((listenfd= socket(PF_INET, SOCK_STREAM, 0)) <0)   ERR_EXIT("socket");      struct sockaddr_in servaddr;  memset(&servaddr, 0, sizeof(servaddr));  servaddr.sin_family = AF_INET;  servaddr.sin_port = htons(5188);  //servaddr.sin_addr.s_addr = htonl(INADDR_ANY);  servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");  //inet_aton("127.0.0.1", &servaddr.sin_addr);  int on = 1;if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)ERR_EXIT("setsockopt");  if(bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr))<0)  ERR_EXIT("bind");    if(listen(listenfd, SOMAXCONN) < 0)  ERR_EXIT("listen");    struct sockaddr_in peeraddr;  socklen_t peerlen = sizeof(peeraddr);  int conn;int i;int client[FD_SETSIZE];int maxi = 0;for(i=0; i<FD_SETSIZE; i++) {client[i] = -1;}int nready;int maxfd = listenfd;fd_set rset;fd_set allset;FD_ZERO(&rset);FD_ZERO(&allset);FD_SET(listenfd, &allset);while(1) {rset = allset;nready = select(maxfd+1, &rset, NULL, NULL, NULL);if(nready == -1) { if(errno == EINTR)continue;ERR_EXIT("select");}if(nready == 0)continue;if(FD_ISSET(listenfd, &rset)) {peerlen = sizeof(peeraddr);conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen);if(-1 == conn) {ERR_EXIT("accept");}for(i=0; i<FD_SETSIZE; i++) {if(client[i] < 0) {client[i] = conn;if(i > maxi) maxi = i;break;}}if( FD_SETSIZE == i) {fprintf(stderr, "too many client\n");exit(EXIT_FAILURE);}printf("ip=%s port=%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));FD_SET(conn, &allset);if(maxfd < conn) maxfd = conn;if(--nready == 0) continue;}for(i=0; i<=maxi; i++) {conn = client[i];if(-1 == conn)continue;if(FD_ISSET(conn, &rset)) {char recvbuf[1024] = {0};int ret = readline(conn, recvbuf, 1024);if(-1 == ret) {ERR_EXIT("readline");} else if(0 == ret) {printf("client close\n");FD_CLR(conn, &allset);client[i] = -1;close(conn);}fputs(recvbuf, stdout);sleep(4);writen(conn, recvbuf, strlen(recvbuf));if(--nready == 0) break;}}}  return 0;}