socket编程(六)

来源:互联网 发布:被淘宝内部优惠券骗了 编辑:程序博客网 时间:2024/06/06 19:01

1.TCP/IP回射客户/服务器

2.TCP是个流协议

(1)TCP是个基于字节流传输的,只维护发送出去多少,确认了多少,没有维护消息与消息之间的边界,因而可能导致粘包问题。

(2)粘包问题解决方法是在应用层维护消息边界。

3.僵进程与SIGCHLD信号

(1)signal(SIGCHLD, SIG_IGN);

(2)signal(SIGCHLD, handle_sigchld);

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);}int main () {//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;pid_t pid;while(1) {//accept必须同时为NULL; 或者同时不为NULLif((conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen)) < 0)ERR_EXIT("accept");//通过accept得到对端addr,portprintf("ip=%s port=%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));struct sockaddr_in connaddr;socklen_t connlen = sizeof(connaddr);//通过getpeername得到对端addr,port[conn必须是已连接的套接字]//conn包含本段ip,port; 也包含对端ip,portgetpeername(conn, (struct sockaddr*)&connaddr, &connlen);printf("ip=%s port=%d\n", inet_ntoa(connaddr.sin_addr), ntohs(connaddr.sin_port));pid = fork();if(-1 == pid) {ERR_EXIT("fork");}if(pid == 0) {  //child close(listenfd);echo_srv(conn);exit(EXIT_SUCCESS);} else {//parentclose(conn);}  }close(listenfd);close(conn);  return 0;}

echoclient.cpp

#include <unistd.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.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) {  char sendbuf[1024] = {0};  char recvbuf[1024] = {0};  int n;  while(fgets(sendbuf, sizeof(sendbuf), stdin) != NULL) {//将带有\n的buf发送过去  writen(sock, sendbuf, strlen(sendbuf));    int ret = readline(sock, recvbuf, sizeof(recvbuf));if(-1 == ret) {ERR_EXIT("readline");} else if(0 == ret) {printf("client close\n");break;}  fputs(recvbuf, stdout);  memset(sendbuf, 0, sizeof(sendbuf));  memset(recvbuf, 0, sizeof(recvbuf));  }}int main () {  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);  close(sock);  return 0;}

fiveclient.cpp

#include <unistd.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.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) {  char sendbuf[1024] = {0};  char recvbuf[1024] = {0};  int n;  while(fgets(sendbuf, sizeof(sendbuf), stdin) != NULL) {//将带有\n的buf发送过去  writen(sock, sendbuf, strlen(sendbuf));    int ret = readline(sock, recvbuf, sizeof(recvbuf));if(-1 == ret) {ERR_EXIT("readline");} else if(0 == ret) {printf("client close\n");break;}  fputs(recvbuf, stdout);  memset(sendbuf, 0, sizeof(sendbuf));  memset(recvbuf, 0, sizeof(recvbuf));  }}int main () {  int sock[5];int i;for(i=0; i<5; i++) {if(( sock[i]= 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[i], (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)ERR_EXIT("sockect");}  echo_cli(sock[0]);for(i=0; i<5; i++) {  close(sock[i]);}  return 0;}



原创粉丝点击