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截止,最大不能超过maxline
ssize_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:count
return:读取成功字节数
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:count
return:已经发送了多少
*/
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
//只要有偷看到数据就接收,没有头看到就是阻塞
//对方套接口关闭,返回0
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截止,最大不能超过maxline
ssize_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编程(十)相关推荐

  1. “解放号”大讲堂直播第十一期:Socket编程知识解析

    "解放号"大讲堂直播第十一期开讲啦!!! 网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket. 本期解放号大讲堂将为你分享:Socket编程知 ...

  2. Java从零开始学四十五(Socket编程基础)

    一.网络编程中两个主要的问题 一个是如何准确的定位网络上一台或多台主机,另一个就是找到主机后如何可靠高效的进行数据传输. 在TCP/IP协议中IP层主要负责网络主机的定位,数据传输的路由,由IP地址可 ...

  3. TCP/IP详解 第十二章(10) TCP Socket 编程

    前言 不管面试 Java .C/C++.Python 等开发岗位, TCP 的知识点可以说是的必问的了. 任 TCP 虐我千百遍,我仍待 TCP 如初恋. 遥想小林当年校招时常因 TCP 面试题被刷, ...

  4. python基础之socket编程

    阅读目录 一 客户端/服务器架构 二 osi七层 三 socket层 四 socket是什么 五 套接字发展史及分类 六 套接字工作流程 七 基于TCP的套接字 八 基于UDP的套接字 九 粘包现象 ...

  5. Go中的Socket编程

    在很多底层网络应用开发者的眼里一切编程都是Socket,话虽然有点夸张,但却也几乎如此了,现在的网络编程几乎都是用Socket来编程.你想过这些情景么?我们每天打开浏览器浏览网页时,浏览器进程怎么和W ...

  6. 游戏编程十年总结(下)(转)

    游戏编程十年总结(下)(转) 一.总结 一句话总结:看完作者的文章,我的感悟是:不到顶尖,终成废土,而且我要是不居安思危,不能够明确自己的处境和社会的竞争,必挂无疑. 这样的故事,真的需要多看. 作者 ...

  7. python网络编程-socket编程

    一.服务端和客户端 BS架构 (腾讯通软件:server+client) CS架构 (web网站) C/S架构与socket的关系: 我们学习socket就是为了完成C/S架构的开发 二.OSI七层模 ...

  8. 【转】Java Socket编程基础及深入讲解

    原文:https://www.cnblogs.com/yiwangzhibujian/p/7107785.html#q2.3.3 Socket是Java网络编程的基础,了解还是有好处的, 这篇文章主要 ...

  9. java socket 重连复用_Java Socket编程基础及深入讲解(示例代码)

    Socket是Java网络编程的基础,了解还是有好处的, 这篇文章主要讲解Socket的基础编程.Socket用在哪呢,主要用在进程间,网络间通信.本篇比较长,特别做了个目录: 一.Socket通信基 ...

  10. go socket编程

    8.1 Socket编程 在很多底层网络应用开发者的眼里一切编程都是Socket,话虽然有点夸张,但却也几乎如此了,现在的网络编程几乎都是用Socket来编程.你想过这些情景么?我们每天打开浏览器浏览 ...

最新文章

  1. Python+OpenCV 图像处理系列(2)—— 视频捕获、播放和保存
  2. 计算机cmd入门,ODS(cmd)入门命令大全,果断收藏!!!
  3. Linux课程第十三天学习笔记
  4. 队列顺序结构C/C++实现(数据结构严蔚敏版)
  5. linux——不同系统间的文件传输和打包压缩
  6. sql server2008禁用远程连接
  7. 飞腾 linux 内核,FT2004-Xenomai
  8. mysql status lock_MySQL性能突发事件问题排查技巧
  9. 2017-2018-1 20155327 《信息安全系统设计基础》第十三周学习总结
  10. iOS:界面适配(二)--iPhone/iPad适配(关于xib)
  11. 紧贴潮流,初心未改:OpenInfra Days China升级回归,打造专属OpenStackers的开源大趴
  12. swft入门学习-第二天
  13. 基于Go语言Echo+Layui的OA办公系统
  14. R6034问题的解决
  15. YOLOv5目标检测全流程:从标注数据到检测模型
  16. 2022-05-02 一.统一响应码
  17. 3. Twitter的客户端框架——Bootstrap
  18. 大数据教学实训沙盘介绍
  19. 音频设备常见测试指标及测试方法
  20. 网络爬虫逆向(企名片,创业项目数据获取)

热门文章

  1. 原来你是这样的JAVA[01]-基础一瞥
  2. DEDECMS中,会员中心的常用知识
  3. 使用Android简单实现有道电子词典
  4. 段错误之 memset 对类对象的误用
  5. DWG/DGN格式导入Arcgis;转化为shp格式;更改地理坐标;导入Google Earth
  6. 支持向量机SVM推导
  7. eclipse 中配置php的 XDebug调试
  8. redis cluster 集群拓展
  9. window server2012服务器上如何安装nginx并启动
  10. Aspose.Pdf for .NET控件PDF文档到Excel、EPS、SVG等的转换