TCP通信特性:(在網絡版塊詳細講解)
面向連接
可靠
面向字節流
TCP通信過程
c++封裝TCP通信
1 #include <iostream>
2 #include <arpa/inet.h>
3 #include <unistd.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <string>
8 #include <errno.h>
9 #include <netinet/in.h>
10 #include <sys/socket.h>
11 #define CHECK(T) if((T) == false) {return -1;}
12 class TcpSocket{
13 public:
14 bool Close(){
15 int ret = close(_sock);
16 if(ret < 0){
17 perror("close error\n");
18 return false;
19 }
20 _sock = -1;
21 return true;
22 }
23 TcpSocket():_sock(-1){
24 };
25 ~TcpSocket(){
26 };
27 bool Socket(){
28 int ret = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
29 if(ret < 0){
30 perror("socket error\n");
31 return false;
32 }
33 _sock = ret;
34 return true;
35 }
36 bool Bind(std::string& ip,uint16_t port){
37 struct sockaddr_in ser_addr;
38 ser_addr.sin_family = AF_INET;
39 ser_addr.sin_port = htons(port);
40 ser_addr.sin_addr.s_addr = inet_addr(ip.c_str());
41 socklen_t len = sizeof(ser_addr);
42 int ret = bind(_sock,(struct sockaddr*)&ser_addr,len);
43 if(ret < 0){
44 perror("bind error\n");
45 return false;
46 }
47 return true;
48 }
49 bool Listen(int backlog = 10){
50 int ret = listen(_sock,backlog);
51 if(ret < 0){
52 perror("listen error\n");
53 return false;
54 }
55 return true;
56 }
57 bool Connect(std::string& ip,uint16_t port){
58 //給定服務器端的地址
59 struct sockaddr_in addr;
60 addr.sin_family = AF_INET;
61 addr.sin_port = htons(port);
62 addr.sin_addr.s_addr = inet_addr(ip.c_str());
63 socklen_t len = sizeof(addr);
64 int ret = connect(_sock,(struct sockaddr*)&addr,len);
65 if(ret < 0){
66 perror("connect error\n");
67 return false;
68 }
69 return true;
70 }
71 bool Accept(TcpSocket& csock,struct sockaddr_in *cli = NULL){
72 struct sockaddr_in _addr;
73 socklen_t len = sizeof(_addr);
74 int newfd = accept(_sock,(struct sockaddr*)&_addr,&len);
75 if(newfd < 0){
76 perror("accept error\n");
77 return false;
78 }
79 //需要將獲取的客戶端的信息拷貝出去
80 memcpy(cli,&_addr,len);
81 csock.setSock(newfd);
82 return true;
83
84 }
85 void setSock(int sock){
86 _sock = sock;
87 }
88 bool Recv(std::string& str){
89 char temp[1024] = {0};
90 //設置成0默認是阻塞的
91 int ret = recv(_sock,temp,1024,0);
92 //返回-1出錯,返回0客戶端關閉,其餘返回接受到的數據大小
93 if(ret < 0){
94 perror("recv error\n");
95 return false;
96 }
97 if(ret == 0){
98 std::cout << "客戶端已經斷開了連接" << std::endl;
99 return false;
100 }
101 str.assign(temp,ret);
102 return true;
103 }
104 bool Send(std::string& str){
105 int ret = send(_sock,str.c_str(),str.size(),0);
106 if(ret < 0){
107 perror("send error\n");
108 return false;
109 }
110 return true;
111 }
112 private:
113 int _sock;
114 };
**重點:**我們tcp通信時需要使用多線程或者多進程來進行多個客戶端的連接請求,不然只能連接一個請求。
多進程服務端版本
1 #include "tcpsocket.hpp"
2 #include <signal.h>
3 #include <sys/wait.h>
4
5 void sigbc(int num){
6 while(waitpid(-1,NULL,WNOHANG)>0);
7 }
8
9 int main(int argc,char* argv[]){
10 if(argc != 3){
11 perror("tcp_ser ip port\n");
12 return -1;
13 }
14 std::string ip = argv[1];
15 uint16_t port = atoi(argv[2]);
16 signal(SIGCHLD,sigbc);
17 TcpSocket sock;
18 CHECK(sock.Socket());
19 CHECK(sock.Bind(ip,port));
20 CHECK(sock.Listen(10));
21 while(1){
22 //創建一個通信
23 TcpSocket com;
24 struct sockaddr_in cli;
25 if(sock.Accept(com,&cli) == false){
26 continue;
27 }
28 std::cout<<"新的連接Ip:"<<inet_ntoa(cli.sin_addr);
29 std::cout<<"端口port:"<<ntohs(cli.sin_port)<<std::endl;
30 int pid = fork();
31 if(pid == 0){
32 while(1){
33 std::string buf;
34 com.Recv(buf);
35 std::cout<<"client said:"<<buf<<std::endl;
36 buf.clear();
37 std::cout<<"server say:";
38 std::cin>>buf;
39 fflush(stdout);
40 com.Send(buf);
41 }
42 }
43 com.Close();
44 }
45 sock.Close();
46 return 0;
47 }
多線程服務端版本
1 #include "tcpsocket.hpp"
2 #include <pthread.h>
3 void* start_thr(void* arg){
4 TcpSocket* sock = (TcpSocket*)arg;
5 while(1){
6 std::string str;
7 sock->Recv(str);
8 std::cout<<"client said:"<<str<<std::endl;
9 str.clear();
10
11 std::cout<<"server say:";
12 fflush(stdout);
13 std::cin>>str;
14 sock->Send(str);
15 }
16 delete sock;
17 sock->Close();
18 return NULL;
19 }
20 int main(int argc,char* argv[]){
21 if(argc != 3){
22 perror("./pro ip port\n");
23 return -1;
24 }
25 std::string ip = argv[1];
26 uint16_t port = atoi(argv[2]);
27
28 TcpSocket sock;
29 CHECK(sock.Socket());
30 CHECK(sock.Bind(ip,port));
31 CHECK(sock.Listen(10));
32 while(1){
33 struct sockaddr_in cli;
34 //這裏在堆上開闢空間,是線程共用
35 TcpSocket* csock = new TcpSocket();
36 if(sock.Accept(*csock,&cli) == false){
37 continue;
38 }
39 std::cout<<"新的客戶端IP:"<<inet_ntoa(cli.sin_addr);
40 std::cout<<" 端口:"<<ntohs(cli.sin_port)<<std::endl;
41 pthread_t pid;
42 pthread_create(&pid,NULL,start_thr,(void*)csock);
43 pthread_detach(pid);
44 }
45 sock.Close();
46 return 0;
47 }
客戶端代碼實現
1 #include "tcpsocket.hpp"
2
3 int main(int argc,char* argv[]){
4 if(argc != 3){
5 perror("./tcp_cli ip port\n");
6 return -1;
7 }
8 std::string ip = argv[1];
9 uint16_t port = atoi(argv[2]);
10
11 TcpSocket sock;
12 CHECK(sock.Socket());
13 CHECK(sock.Connect(ip,port));
14 while(1){
15 std::string buf;
16 std::cout<<"client say:";
17 fflush(stdout);
18 std::cin>>buf;
19 sock.Send(buf);
20
21 buf.clear();
22 sock.Recv(buf);
23 std::cout<<"server said:"<<buf<<std::endl;
24 }
25 sock.Close();
26 return 0;
27 }