码海拾遗:简单Socket(TCP)类实现

Posted 落雷

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了码海拾遗:简单Socket(TCP)类实现相关的知识,希望对你有一定的参考价值。

  最近刚开始啃Unix网络编程(卷1:套接字联网API),为加深TCP连接的建立和终止的理解与记忆,记下本文,方便以后翻看。

  同时留下的还有简单的Socket(TCP)类:

  mySocket.h

 1 #pragma once
 2 
 3 #include <unistd.h>
 4 #include <sys/socket.h>
 5 #include <arpa/inet.h>
 6 #include <strings.h>
 7 #include <errno.h>
 8 #include <fcntl.h>
 9 
10 #include <iostream>
11 #include <string>
12 
13 using namespace std;
14 
15 const int MAXLISTEN = 20;
16 const int MAXLINE = 1024;
17 
18 class mySocket
19 {
20 public:
21     mySocket();
22     ~mySocket();
23 
24     bool Init();
25     bool Bind(const long port);
26     bool Listen();
27     bool Accept(mySocket client);
28     bool Connect(const string host,const long port);
29 
30     bool Send(mySocket client,string msg);
31     int Receive(mySocket client,string& msg);
32 
33     //设置阻塞或非阻塞
34     bool setNonBlock(bool flag);
35 
36     struct sockaddr_in getAddr();
37     int getFD();
38 
39 private:
40     int m_fd;
41     int m_rtn;
42     struct sockaddr_in m_addr;
43 };
View Code

  mySocket.cpp

  1 #include <iostream>
  2 
  3 #include "mySocket.h"
  4 
  5 mySocket::mySocket()
  6 {
  7     m_fd = -1;
  8 }
  9 
 10 mySocket::~mySocket()
 11 {
 12     if(m_fd >= 0)
 13     {
 14         close(m_fd);
 15         m_fd = -1;
 16     }
 17 }
 18 
 19 bool mySocket::Init()
 20 {
 21     m_fd = socket(AF_INET,SOCK_STREAM,0);
 22     if(m_fd < 0)
 23     {
 24         cout<<"init socket error:"<<endl;
 25         return false;
 26     }
 27     return true;
 28 }
 29 
 30 bool mySocket::Bind(const long port)
 31 {
 32     bzero(m_addr,sizeof(m_addr));
 33     m_addr.sin_family = AF_INET;
 34     m_addr.sin_port = htons(port);
 35     m_addr.sin_addr.s_addr = htonl(INADDR_ANY);
 36 
 37     if((m_rtn = bind(m_fd,(struct sockaddr*)&m_addr,sizeof(m_addr))) < 0)
 38     {
 39         cout<<"bind error"<<endl;
 40         return false;
 41     }
 42     else
 43         return true;
 44 }
 45 
 46 bool mySocket::Listen()
 47 {
 48     if(listen(m_fd,MAXLISTEN) < 0)
 49     {
 50         cout<<"listen error"<<endl;
 51         return false;
 52     }
 53     else
 54         return true;
 55 }
 56 
 57 bool mySocket::Accept(mySocket client)
 58 {
 59     int cliLen = sizeof(client.getAddr());
 60 again:
 61     if((m_rtn = accept(m_fd,(struct sockaddr*)&(client.getAddr()),&cliLen)) < 0)
 62     {
 63         if(errno == ECONNABORTED || errno == EINTR)
 64             goto again;
 65         else
 66         {
 67             cout<<"accept error"<<endl;
 68             return false;
 69         }
 70     }
 71     else
 72         return true;
 73 }
 74 
 75 bool mySocket::Connect(const string host,const long port)
 76 {
 77     m_addr.sin_family = AF_INET;
 78     m_addr.sin_port = htons(port);
 79     m_addr.sin_addr.s_addr = inet_addr(host.c_str());
 80 
 81     if(connect(m_fd,(struct sockaddr *)&m_addr,sizeof(m_addr)) < 0)
 82     {
 83         cout<<"connect error"<<endl;
 84         return false;
 85     }
 86     else
 87         return true;
 88 }
 89 
 90 bool mySocket::Send(mySocket client,string msg)
 91 {
 92     m_rtn = send(client.m_fd,msg.c_str(),msg.size());
 93     if(rtn < 0)
 94     {
 95         cout<<"send error"<<endl;
 96         return false;
 97     }
 98     return true;
 99 }
100 
101 int mySocket::Receive(mySocket client,string& msg)
102 {
103     char buf[MAXLINE] = {0};
104     msg.clear();
105 
106     tn = recv(client.m_fd,buf,sizeof(buf));
107     if(rtn < 0)
108     {
109         cout<<"receive error"<<endl;
110         return -1;
111     }
112     else if(rtn == 0)
113         return 0;
114     else
115     {
116         msg = buf;
117         return rtn;
118     }
119 }
120 
121 void mySocket::setNonBlock(bool flag)
122 {
123     int opt = fcntl(m_fd,F_GETFL);
124     if(opt < 0)
125     {
126         cout<<"SetNonBlock error"<<endl;
127         return;
128     }
129 
130     if(flag)
131         opt = (opt | O_NONBLOCK);
132     else
133         opt = (opt & O_NONBLOCK);
134 
135     fcntl(m_fd,F_SETFL,opt);
136 }
137 
138 struct sockaddr_in mySocket::getAddr()
139 {
140     return m_addr;
141 }
142 
143 int mySocket::getFD()
144 {
145     return m_fd;
146 }
View Code

  1、TCP连接的建立

  TCP通过三次握手建立连接。在建立连接之前,服务器必须准备好接受外来的连接,通常通过socket、bind、listen这3个函数来完成,此过程被称为被动打开

  (1)客户端(外来连接)通过调用connect发起主动打开。此时客户端发送一个SYN(同步)分节,通知服务器客户端将在连接中发送的数据的初始序列号。通常SYN分节不携带数据,其所在的IP数据报只包含一个IP首部、一个TCP首部及可能有的TCP选项。

  (2)服务器必须确认(ACK)客户端的SYN,同时自己也得发送一个SYN分节,包含服务器将在同一连接中发送的数据的初始序列号。服务器在单个分节中发送SYN和对客户端SYN的ACK(确认)。

  (3)客户端必须确认服务器的SYN。

  具体过程如下图:

  

  2、TCP连接终止

  TCP断开连接需要通过四次挥手来完成,具体过程如下:

  (1)某个应用进程首先调用close函数,称为该端的主动关闭。该端TCP发送一个FIN分节,表示数据发送完毕。

  (2)接收到这个FIN的对端执行被动关闭。这个FIN由TCP确认。它的接收也作为一个文件结束符(eof)传递给接收端的应用进程,因为FIN的接收意味着接收端的应用进程在相应的连接上再无额外数据可接收。

  (3)一段时间后,接收到这个文件结束符的应用进程将调用close关闭它的套接字,导致它的TCP也发送一个FIN。

  (4)接收到这个最终的FIN的原发送端TCP确认这个FIN。

  图示如下:

  

 

以上是关于码海拾遗:简单Socket(TCP)类实现的主要内容,如果未能解决你的问题,请参考以下文章

码海拾遗:二分查找

码海拾遗:二叉树的遍历(递归实现)

码海拾遗:内存四区

码海拾遗:Linux多线程mutex锁

码海拾遗:Linux常用命令

码海拾遗:高并发服务器模型简述