博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
socket 编程入门教程(三)TCP原理:3、设计TCP socket的类(中)
阅读量:7064 次
发布时间:2019-06-28

本文共 2480 字,大约阅读时间需要 8 分钟。

hot3.png

当激活监听的TCPListenSock等待远程client的connect()握手请求的时候,是调用了accept()并且产生阻塞(默认情况下),如果accept()成功返回意味着conect()握手请求请求成功,这时候就通过accept()产生了一个新的sockFD用于TCP通讯。我们把这个新的sockFD构建为TCPServerSock类:

class TCPServerSock: public BaseSock{

private:
    sockaddr_in clientSockAddr;
protected:
    
char* preBuffer;
    
int preBufferSize;
    mutable 
int preReceivedLength;
public:
    
explicit TCPServerSock(
        
const TCPListenSock& listen_sock,
        
int pre_buffer_size = 32);
    
virtual ~TCPServerSock();
    
int TCPReceive() const;
    
int TCPSend(const char* send_data,
            
const int& data_length) const;
};

这里,我们为TCPServerSock预留一个缓存,这个缓存并不是必须的,但是设置这样一个缓存至少有两个好处:

1、可以在使用时不必专门为recv()建立缓存;
2、类方法TCPReceive()和TCPSend()可以共享这个缓存,在处理很多问题时候很方便,比如echo,就不需要先把recv()的缓存读出来再由send()来发送。
将缓存已用长度preReceiveLength加上关键字mutable表示我们不关心这个长度会被更改,我们只在乎有一个缓存可以用,但是实际用了多少不重要,这样我们就可以为接受和发送的类方法加上const。
我们回到TCPServerSock的建立,TCPServerSock通过TCPListenSock accept()一个远程的client connect()握手请求而建立,所以,TCPServerSock的构造在默认情况下是阻塞的。

TCPServerSock::TCPServerSock(

                const TCPListenSock& listen_sock,
                
int pre_buffer_size):
preBufferSize(pre_buffer_size),
preReceivedLength(
0)
{
    preBuffer 
= new char[preBufferSize];
    socklen_t
 clientSockAddrLen = sizeof(clientSockAddr);
    sockFD 
= accept(    listen_sock.showSockFD(),
                        (sockaddr
*)&clientSockAddr,
                        
&clientSockAddrLen);
    
if (sockFD < 0) {
        sockClass::error_info(
"accept() failed.");
    }
    std::cout    
<< "Client (IP: "
                
<< inet_ntoa(clientSockAddr.sin_addr)
                
<< ") conneted." << std::endl;
}
TCPServerSock::
~TCPServerSock()
{
    delete [] preBuffer;
    close(sockFD);
}

这里需要注意一个Linux和Windows下的不同:

对于sockaddr_in(也包括sockaddr)的大小,被accept()指定的时候,Linux中用的是socklen_t,其实这就是size_t,也就是unsigned int。而WinSock中却用的是int。因为在编译中不会自动转换,所以会提示错误。
再次强调,TCPServerSock的sockFD是通过accept()建立的而不是socket(),这也是唯一一个不用socket()建立的sockFD(包括UDP的)。在client发出的connect()握手请求的数据报中,同时包含着client端的地址信息(IP地址和端口)和server端的地址信息(IP地址和端口),正是这个握手请求数据报中的两边的地址信息通过accept()被传递到TCPServerSock的sockFD中。请注意,server端的信息并非由TCPListenSock提供,因为TCPListenSock中listenSockAddr的IP地址为空(INADDR_ANY == 0),而TCPServerSock中server端的SockAddr却是具体的,由客户端的握手协议传来的(但是没有具体的体现出来)。只有具体的地址(IP地址和端口)才能提供IP数据包的目的地方向。而端口号,则因为client事先知道监听端口号,从而在握手请求中包含,最终传递给TCPListenSock中server端的SockAddr,虽然这个过程决定了这个端口号等于监听端口号,但是需要明白的是,这个端口号来自握手请求的数据报而不是TCPListenSock的listenSockAddr。
新的sockFD具有来向(本机)和去向(远程)的信息,所以可以收发数据。TCPServerSock的sockFD一旦建立,马上向远程返回一个数据报,这个数据报有两层意义:
1、表示server已经接收了client的握手请求;
2、对client发出与server这个新sockFD握手的请求。
这就是所谓第二次握手,并且也是以数据报的形式传送的。我们说过,TCP协议的目标是建立“可靠”的数据流形式的通讯,在这个数据流的通道建立起来以前,只能采用数据报的形式传送数据。

转载于:https://my.oschina.net/GeorgeSu/blog/264194

你可能感兴趣的文章
boost 1.52在windows下的配置
查看>>
素材锦囊——50个高质量的 PSD 素材免费下载《上篇》
查看>>
【转】oc中消息传递机制-附:对performSelector方法的扩充
查看>>
oracle的nvl和sql server的isnull
查看>>
[转]虚拟机下Ubuntu共享主机文件(Ubuntu、VMware、共享)
查看>>
高血压 治疗 偏方
查看>>
HtmlAttribute HTML属性处理类
查看>>
[书目20130316]jQuery UI开发指南
查看>>
Sql Server系列:开发存储过程
查看>>
Find INTCOL#=1001 in col_usage$?
查看>>
AutoCAD 命令统计魔幻球的实现过程--(3)
查看>>
dp学习笔记1
查看>>
newlisp debugger
查看>>
Java进阶02 异常处理
查看>>
java 动态代理
查看>>
微信5.0绑定银行卡教程
查看>>
数字转换为壹仟贰佰叁拾肆的Java方法
查看>>
一个表单对应多个提交按钮,每个提交按钮对应不同的行为
查看>>
tomcat集群时统计session与在线人数
查看>>
Android程序完全退出
查看>>