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;}
- socket编程(十)
- socket编程原理(十)
- 十、Socket之UDP编程
- socket编程(十)a'c'cept()
- Socket网络编程--第二十一天
- Linux下的socket编程实践(十) 基本UDP编程细节
- Linux Socket 学习(十)
- Linux Socket 学习(十)
- Linux Socket 学习(十)
- Linux Socket 学习(十)
- linux网络编程之socket(十):shutdown 与 close 函数 的区别
- linux网络编程之socket(十):shutdown 与 close 函数 的区别
- linux网络编程之socket(十):shutdown 与 close 函数 的区别
- linux网络编程之socket(十):shutdown 与 close 函数 的区别
- linux网络编程之socket(十):shutdown 与 close 函数 的区别
- linux网络编程二十:socket选项:SO_RCVTIMEO和SO_SNDTIMEO
- 网络编程(Socket编程)
- socket编程(二)
- Python编程之解释器
- 背包问题总结
- 微信小程序实例源码大全
- 跑步中的感受
- 雄才伟略,深谋远虑,立足国家,力排众议
- socket编程(十)
- Linux 下用exim4 bypass php disable_functions
- (回文串)Manacher算法
- Hrbust 1849 商品中心【贪心+思维+并查集】好题!好题!
- 字符串的排列组合问题-java
- 初学vue
- ACM 干货
- 继承AppCompatActivity的Activity无法隐藏标题栏
- Spring的生命周期分析