传输层使用tcp实现客户端和服务端通信的小程序

    xiaoxiao2023-10-22  169

     

    通信流程:     服务端         1.创建套接字         2.绑定地址信息         3.开始监听         4.获取新的socket         5.接受数据         6.发送数据         7.关闭套接字     客户端         1.创建socket         2.向服务端发起连接请求         3.发送数据         4.接受数据         5.关闭套接字     接口         创建套接字             socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)                 第一个参数是地址域(IPV4地址)                 第二个参数是套接字类型(流式套接字)                 第三个参数是是协议类型(TCP协议)         绑定地址             bind(sockfd, &srv_addr, len)                 第一个参数是套接字描述符                 第二个参数是服务端的地址信息                 第三个参数是地址信息长度         服务端开始监听             listen(sockfd, backlog)                 第一个参数是套接字描述符                 第二个参数是最大并发连接数(已完成队列最大节点数),即同一时间允许连接的最大客户端数         客户端发起连接请求             connect(sockfd, &srv_addr, len)                 第一个参数是套接字描述符                 第二个参数是要连接的服务端的地址信息                 第三个参数是地址信息的长度         服务端获取新的socket             accept(sockfd, &cli_addr, &len)                 第一个参数是套接字描述符                 第二个参数是客户端的地址信息(为空代表不接受客户端地址信息)                 第三个参数是地址信息长度                 返回值:为该客户端新建的socket描述符         发送数据             send(sockfd, buf, len, flags)                 第一个参数是套接字文件描述符                 第二个参数是要发送的数据放在哪                 第三个参数是要发送数据的长度                 第四个参数0表示阻塞发送                 返回值:实际发送的字节数         接受数据             recv(sockfd, buf, len, flags)                 第一个参数是套接字描述符                 第二个参数是接受的数据放在哪                 第三个参数是想接受的数据的字节数                 第四个参数                     0:阻塞接受                     MSG_PEEK:获取数据,但是不从缓冲区中移除(即探测性接收)                 返回值                     >0:实际接收的数据字节数个数                     ==0:表示连接断开                         连接断开原理                             tcp连接管理中,内建有"保活计时器",当2小时没有数据往来时,每隔75秒会向对方发送一个"保活探测包",要求对方回复,当连续发送9次对方都没有回复,则认为连接已经断开,会关闭连接。若连接断开recv会返回0,这时候send会触发异常(SIGPIPE,导致进程退出)                     <0:出错         关闭套接字             close(sockfd)

    .h文件:

    #include <iostream> 2 #include <sys/wait.h> 3 #include <signal.h> 4 #include <stdlib.h> 5 #include <string.h> 6 #include <stdio.h> 7 #include <sys/socket.h> 8 #include <netinet/in.h> 9 #include <arpa/inet.h> 10 #include <errno.h> 11 #include <unistd.h> 12 13 #define CHECK(q) if(((q)==false)) {return -1;} 14 15 using namespace std; 16 17 class TcpSocket 18 { 19 private: 20 int _sockfd; 21 public: 22 TcpSocket()//构造函数 23 :_sockfd(-1) 24 {} 25 26 void SetFd(int fd) 27 { 28 _sockfd=fd; 29 } 30 ~TcpSocket()//析构函数 31 {} 32 33 //创建socket 34 bool Socket(); 35 36 //绑定地址 37 bool Bind(string& ip, uint16_t port); 38 39 //开始监听 40 bool Listen(int backlog=10); 41 42 //向服务端发起连接建立请求 43 bool Connect(string& ip, uint16_t port); 44 45 //获取为新客户端建立的新的socket 46 bool Accept(TcpSocket& clisock, struct sockaddr_in* addr); 47 48 //发送数据 49 bool Send(string& buf); 50 51 //接收数据 52 bool Recv(string& ip); 53 54 //关闭套接字 55 bool Close(); 56 57 }; 58 59 60 //创建socket 61 bool TcpSocket::Socket() 62 { 63 _sockfd=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 64 if(_sockfd<0) 65 { 66 perror("socket error"); 67 return false; 68 } 69 return true; 70 } 71 72 //绑定地址 73 bool TcpSocket::Bind(string& ip, uint16_t port) 74 { 75 struct sockaddr_in srv_addr;//初始化服务器的地址信息 76 srv_addr.sin_family=AF_INET; 77 srv_addr.sin_port=htons(port); 78 srv_addr.sin_addr.s_addr=inet_addr(ip.c_str()); 79 80 socklen_t len=sizeof(struct sockaddr_in); 81 82 int ret=bind(_sockfd, (struct sockaddr*)&srv_addr, len); 83 if(ret<0) 84 { 85 perror("bind error"); 86 return false; 87 } 88 return true; 89 } 90 91 //开始监听 92 bool TcpSocket::Listen(int backlog) 93 { 94 int ret=listen(_sockfd, backlog); 95 if(listen<0) 96 { 97 perror("listen error"); 98 return false; 99 } 100 return true; 101 } 102 103 104 //向服务端发起连接建立请求 105 bool TcpSocket::Connect(string& ip, uint16_t port) 106 { 107 //建立服务器的地址信息 108 struct sockaddr_in srv_addr; 109 srv_addr.sin_family=AF_INET; 110 srv_addr.sin_port=htons(port); 111 srv_addr.sin_addr.s_addr=inet_addr(ip.c_str()); 112 113 socklen_t len=sizeof(struct sockaddr_in); 114 115 int ret=connect(_sockfd, (struct sockaddr*)&srv_addr, len); 116 if(ret<0) 117 { 118 perror("connect error"); 119 return false; 120 } 121 return true; 122 } 123 124 //获取为新客户端建立的新的socket 125 bool TcpSocket::Accept(TcpSocket& clisock, struct sockaddr_in* addr= NULL) 126 { 127 //用于获取客户端地址信息 128 struct sockaddr_in cli_addr; 129 socklen_t len=sizeof(struct sockaddr_in); 130 int newfd=accept(_sockfd, (struct sockaddr*)&cli_addr, &len ); 131 if(newfd<0) 132 { 133 perror("accept error"); 134 return false; 135 } 136 //如果参数不为空,则将获取到的客户端地址信息拷给它 137 if(addr!=NULL) 138 { 139 memcpy(addr, &cli_addr, len); 140 } 141 //用获取的套接字描述符传给参数clisock 142 clisock.SetFd(newfd); 143 return true; 144 } 145 146 //发送数据 147 bool TcpSocket::Send(string& buf) 148 { 149 int ret=send(_sockfd, buf.c_str(), buf.size(), 0); 150 if(ret<0) 151 { 152 perror("send error"); 153 return false; 154 } 155 return true; 156 } 157 158 //接收数据 159 bool TcpSocket::Recv(string& buf) 160 { 161 char tmp[4096]={0}; 162 int ret=recv(_sockfd, tmp, 4096, 0); 163 if(ret<0) 164 { 165 perror("recv error"); 166 return false; 167 } 168 else if(ret==0)//对端关闭 169 { 170 perror("perr shutdown"); 171 return false; 172 } 173 //说明接收成功 174 buf.assign(tmp, ret); 175 return true; 176 } 177 178 //关闭套接字 179 bool TcpSocket::Close() 180 { 181 close(_sockfd); 182 _sockfd=-1; 183 } 184 185

    服务端:

    //服务端 2 3 #include "tcp.hpp" 4 using namespace std; 5 6 int main(int argc, char* argv[]) 7 { 8 //检查参数合法性 9 if(argc!=3) 10 { 11 perror("./server ip port"); 12 return 0; 13 } 14 15 //组织地址信息 16 string ip=argv[1]; 17 uint16_t port=atoi(argv[2]); 18 19 //创建TcpSocket对象 20 TcpSocket sock; 21 22 //创建socket,绑定地址,开始监听 23 CHECK(sock.Socket()); 24 CHECK(sock.Bind(ip, port)); 25 CHECK(sock.Listen()); 26 27 //获取新的socket,开始接受数据 28 while(1) 29 { 30 TcpSocket clisock;//用于接受新的socket 31 32 if(sock.Accept(clisock)==false)//对于一个客户端的新socket接受失败,应该继续上去接受其他客户端的socket 33 { 34 continue; 35 } 36 //来到这说明接收成功了 37 string buf; 38 39 //接受数据 40 clisock.Recv(buf); 41 //打印出接收到的数据 42 cout<<"client say:"<<buf<<endl; 43 44 buf.clear(); 45 //收到客户端数据后,恢复数据给客户端 46 cout<<"server say:"; 47 fflush(stdout); 48 cin>>buf; 49 clisock.Send(buf);//给对应的客户端恢复消息 50 } 51 sock.Close();//关闭套接字 52 53 return 0; 54 }

    客户端:

    //客户端 2 #include "tcp.hpp" 3 using namespace std; 4 5 6 int main(int argc, char* argv[]) 7 { 8 //检查参数的合法性 9 if(argc!=3) 10 { 11 perror("./client ip port"); 12 return -1; 13 } 14 15 //组织出服务器的地址信息 16 17 string ip=argv[1]; 18 uint16_t port=atoi(argv[2]); 19 20 //创建TcpSocket对象 21 TcpSocket sock; 22 23 //创建socket,请求建立连接 24 CHECK(sock.Socket()); 25 CHECK(sock.Connect(ip, port)); 26 27 //开始发数据,收数据 28 while(1) 29 { 30 string buf; 31 //客户端准备开始说话了 32 cout<<"client say:"; 33 fflush(stdout); 34 35 cin>>buf; 36 sock.Send(buf);//将输入内容发到服务端 37 38 buf.clear(); 39 //准备接受服务端的消息 40 sock.Recv(buf); 41 cout<<"server say:"<<buf<<endl;//将接受到服务端的话显示出来 42 } 43 sock.Close();//关闭套接字 44 45 return 0; 46 }

     

    最新回复(0)