使用TCP套接字编程可以实现基于TCP/IP协议的面向连接的通信,它分为服务器端和客户端两部分,其主要实现过程如下 :
1、socket
函数
无论是客户端还是服务器端,都需要创建一个socket,该函数返回socket标识符。socket是一个结构体,被创建在内核中。
sockfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); //AF_INT:ipv4, SOCK_STREAM:tcp协议
2、connect
函数
客户端创建了socket后,需要和服务器端建立连接,此时使用connect函数和服务器端进行连接。
connect(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))
3、bind
函数
把一个本地协议地址和套接口绑定,比如把192.168.198.128的8000端口绑定到套接口。
为什么在上图客户端不推荐绑定?
这是因为如果没有调用bind函数绑定一个端口的话,当调用connect函数时,内核会为该套接口临时选定一个端口,因此可以不用绑定。而服务器之所以需要绑定的原因就是,所以客户端都需要知道服务器使用的哪个端口,所以需要提前绑定。
bind(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))
4、listen
函数
调用linsten函数后,内核将从该套接口接收连接请求。backlog代表服务端在同一时间能处理的最大连接数。
listen(sockfd,backlog)
5、accept
函数
此函数返回新建连接的描述符。
注意:
此处的套接口不同于服务器开始创建的监听套接口,此套接口是已经完成连接的套接口,监听套接口只是用来监听。
accept(sockfd,(struct sockaddr*)&clientaddr,&clientaddr_len);
6、close
函数
数据传输完成后,需要关闭套接口
close(fd);
代码
tcpsocket.hpp
1 #include <cstdio>
2 #include <iostream>
3 #include <string>
4 #include<string.h>
5 #include <unistd.h>
6 #include <arpa/inet.h>
7 #include <netinet/in.h>
8 #include <sys/socket.h>
9 #define CHECK_RET(q) if((q)==false){return -1;}
10 #define LISTEN_BACKLOG 5
11 using namespace std;
12
13 class TcpSocket
14 {
15 private:
16 int _sockfd;
17 public:
18 TcpSocket()
19 :_sockfd(-1)
20 {}
21
22 bool Socket()
23 {
24 _sockfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
25 if(_sockfd<0)
26 {
27 perror("sockfd error");
28 return false;
29 }
30 return true;
31 }
32
33 bool Bind(const string& ip,const uint16_t port)
34 {
35 struct sockaddr_in addr;
36 addr.sin_family=AF_INET;
37 addr.sin_port=htons(port);
38 addr.sin_addr.s_addr=inet_addr(&ip[0]);
39 socklen_t len = sizeof(struct sockaddr_in);
40 int ret = bind(_sockfd,(struct sockaddr*)&addr,len);
41 if(ret<0)
42 {
43 perror("bind error");
44 return false;
45 }
46 return true;
47 }
48
49 bool Listen(int backlog=LISTEN_BACKLOG)
50 {
51 int ret = listen(_sockfd,backlog);
52 if(ret<0)
53 {
54 perror("listen error");
55 return false;
56 }
57 return true;
58 }
59
60 bool Connect(const string& ip,const int port)
61 {
62 struct sockaddr_in addr;
63 addr.sin_family=AF_INET;
64 addr.sin_port=htons(port);
65 addr.sin_addr.s_addr=inet_addr(&ip[0]);
66 socklen_t len = sizeof(struct sockaddr_in);
67 int ret= connect(_sockfd,(struct sockaddr*)&addr,len);
68 if(ret<0)
69 {
70 perror("connect error");
71 return false;
72 }
73 return true;
74 }
75
76 bool Accept(TcpSocket* sock,string* ip=NULL,uint16_t* port=NULL)
77 {
78
79 struct sockaddr_in addr;
80 socklen_t len = sizeof(struct sockaddr_in);
81 int newfd=accept(_sockfd,(struct sockaddr*)&addr,&len);
82 if(newfd<0)
83 {
84 perror("accept error");
85 return false;
86 }
87 sock->_sockfd=newfd;
88 if(ip!=NULL)
89 {
90 *ip=inet_ntoa(addr.sin_addr);
91 }
92 if(port!=NULL)
93 {
94 *port=ntohs(addr.sin_port);
95 }
96 return true;
97 }
98
99 bool Recv(string* buf)
100 {
101 char tmp[1024]={0};
102 int ret =recv(_sockfd,tmp,1024,0);
102 int ret =recv(_sockfd,tmp,1024,0);
103 if(ret<0)
104 {
105 perror("recv error");
106 return false;
107 }
108 else if(ret==0)
109 {
110 printf("peer shutdown");
111 return false;
112 }
113 buf->assign(tmp,ret);
114 return true;
115 }
116
117 bool Send(const string& data)
118 {
119 int total=0;
120
121 while(total<data.size())
122 {
123 int ret = send(_sockfd,&data[0]+total,data.size()-total,0);
124 if(ret<0)
125 {
126 perror("send error");
127 return false;
128 }
129 total+=ret;
130 }
131 return true;
132 }
133 bool Close()
134 {
135 if(_sockfd!=-1)
136 {
137 close(_sockfd);
138 }
139 return true;
140 }
141
142 };
thread_srv.cpp
1 #include "tcpsocket.hpp"
2 #include <signal.h>
3 #include <sys/wait.h>
4 #include<cstdlib>
5 #include<pthread.h>
6 using namespace std;
7 void *thr_entry(void* arg)
8 {
9 TcpSocket* clisock=(TcpSocket*)arg;
10 bool ret;
11 while(1)
12 {
13 string buf;
14 ret = clisock->Recv(&buf);
15 if (ret == false)
16 {
17 clisock->Close();
18 delete clisock;
19 return NULL;
20 }
21 cout << "client say: " << buf << endl;
22 buf.clear();
23 cout << "server say: ";
24 cin >> buf;
25 ret = clisock->Send(buf);
26 if (ret == false)
27 {
28 clisock->Close();
29 delete clisock;
30 return NULL;
31 }
32 }
33 clisock->Close();
34 delete clisock;
35 return NULL;
36 }
37
38 int main(int argc, char *argv[])
39 {
40 if (argc != 3)
41 {
42 printf("usage: ./tcp_src192.168.189.128 8000\n");
43 return -1;
44 }
45 signal(SIGCHLD,SIG_IGN);
46 string srvip = argv[1];
47 uint16_t srvport = stoi(argv[2]);
48 TcpSocket lst_sock;
49 CHECK_RET(lst_sock.Socket());
50 CHECK_RET(lst_sock.Bind(srvip, srvport));
51 CHECK_RET(lst_sock.Listen());
52 while(1)
53 {
54 TcpSocket* clisock=new TcpSocket();
55 string cliip;
56 uint16_t cliport;
57 bool ret = lst_sock.Accept(clisock, &cliip,&cliport);
58 if (ret == false)
59 {
60 continue;
61
62 }
63 cout<<"get newcon:"<< cliip<<"-"<<cliport<<"\n";
64
65 pthread_t tid;
66 pthread_create(&tid,NULL,thr_entry,(void*)clisock);
67 pthread_detach(tid);
68
69 }
70
71 lst_sock.Close();
72 }
tcp_cli.cpp
1 #include "tcpsocket.hpp"
2 using namespace std;
3 int main(int argc,char* argv[])
4 {
5 if(argc!=3)
6 {
7 printf("usage: ./tcp_cli srvip srvport\n");
8 return -1;
9 }
10 string srvip=argv[1];
11 uint16_t srvport = stoi(argv[2]);
12 TcpSocket cli_sock;
13 CHECK_RET(cli_sock.Socket());
14 CHECK_RET(cli_sock.Connect(srvip,srvport));
15 while(1)
16 {
17 string buf;
18 cout<<"客户端:";
19 cin>>buf;
20 CHECK_RET(cli_sock.Send(buf));
21 buf.clear();
22 CHECK_RET(cli_sock.Recv(&buf));
23 cout<<"服务端:"<<buf<<endl;
24 }
25 CHECK_RET(cli_sock.Close());
26 return 0;
27
28 }