Linux网络:TCPsocket接口使用

重温

TCP的特点

  • 传输层协议
  • 有连接
  • 可靠传输
  • 面向字节流

创建套接字

_sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
//这里是面向字节流

因为之前介绍过UDP的接口,TCP和UDP相似。
从创建套接字,进行绑定都一模一样(除了SOCK_STREAM)

面向连接

UDP面向无连接,所以创建完套接字可以直接发送信息
TCP是面向连接的,所以发送信息前要先建立连接

服务器端:listen()

服务器是被连接的,要一直等待客户的连接。
需要linsten接口,来确定客户端有无发起连接

#include <sys/types.h>         
#include <sys/socket.h>

int listen(int sockfd, int backlog);

将一个已绑定地址的 TCP 套接字设为监听状态,用于接受客户端的连接请求

参数名说明
sockfdsocket() 创建,并用 bind() 绑定的套接字描述符
backlog系统允许排队的最大连接请求数量(连接队列长度)
内核维护两个队列:
🔹 半连接队列(握手未完成)
🔹 全连接队列(握手已完成,等待 accept()
返回值说明
0调用成功 套接字已进入监听状态
-1调用失败

使用示例

n = ::listen(_sockfd, gbacklog);
if(n<0)
{
    LOG(FATAL, "listen error\n");
    exit(LISTEN_ERROR);
    
}

服务器端:accept()

在UDP中,创建完客户端即可接收数据
在TCP中,需要先建立连接

#include <sys/types.h>         
#include <sys/socket.h>

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数名说明
sockfd监听套接字的文件描述符,必须是通过 socket() 创建,并使用 bind() + listen() 设置过的
addr客户端地址信息结构体指针(如 struct sockaddr_in*),接收客户端 IP 和端口,可为 NULL
addrlen指向 socklen_t 类型的指针,传入 addr 的结构体大小,返回时填入实际地址大小

后两个参数和UDP的recvfrom一样是输出型参数,其指向客户端

返回值说明
>=0成功,返回一个新的已连接 socket 描述符,用于与客户端通信
-1失败,连接未接收成功

其返回值是新的文件描述符(参考UDP返回值是0和-1)

使用示例

struct sockaddr_in peer;
socklen_t len = sizeof(peer);
int sockfd = ::accept(_listensockfd, (struct sockaddr*)&peer, &len);
if (sockfd < 0)
{
    LOG(WARNING, "accept error\n");
    continue;
}

listen()和accept()的套接字

对比项listen 套接字accept 返回的套接字
作用用于监听客户端连接请求,建立连接的“门户”用于与某个客户端进行数据通信
来源通过 socket() + bind() + listen() 创建accept()listen 套接字中派生出来
是否用于通信不能直接用于收发数据可以用于 recv() / send() 与客户端通信
连接数量一个 listen 套接字 可以接受多个连接请求每调用一次 accept(),生成一个新连接套接字
生命周期通常贯穿整个服务器进程,用于持续监听客户端断开后就关闭,代表一次具体连接
示例变量名server_fd, listen_fdclient_fd, conn_fd
是否阻塞调用 listen() 不阻塞accept() 默认阻塞,直到有新连接

listen是看大门的,accept是接客的(

服务器端:read()

UDP是面向数据报的,读取的是报文
TCP是面向字节流的,可以用文件的读取和写入

值得关注的是read()在网络中的返回值

返回值含义说明
> 0实际读取到的字节数,表示成功读取到数据
== 0表示连接已被对方关闭(对方调用了 close()shutdown()
< 0发生错误,读取失败,可通过 errno 获取具体错误信息

使用示例

while (true)
{
    char inbuffer[1024];
    ssize_t n = read(sockfd, inbuffer, sizeof(inbuffer) - 1);
    if (n > 0)
    {
        inbuffer[n] = '\0';
        std::cout << inbuffer << std::endl;
    }
    else if (n == 0)
    {
        // client 退出且关闭了连接
        LOG(INFO, "%s quit\n", clientaddr.c_str());
        break;
    }
    else
    {
        LOG(ERROR, "read error\n");
        break;
    }
}

::close(sockfd);

客户端:connect()

向服务器发起连接

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

int connect(int sockfd, const struct sockaddr *addr,
            socklen_t addrlen);
参数名说明
sockfd客户端使用 socket() 创建的套接字描述符(必须是 SOCK_STREAM 类型)
addr指向服务器地址结构体(如 struct sockaddr_in)的指针
addrlenaddr 结构体的大小,通常是 sizeof(struct sockaddr_in)
返回值含义说明
0成功建立连接(TCP 三次握手完成)
-1连接失败,可通过 errno 查看具体原因

使用示例

struct sockaddr_in server;
int n = connect(sockfd, (sockaddr*)&server, sizeof(server));
if (n<0)
{
    std::cerr << "connect error" << std::endl;
    exit(3);
}

客户端:send()

客户端依然可以用read()和write()
send()用于字节流的发送

ssize_t send(int sockfd, const void *buf, size_t len, int flags);
参数名类型说明
sockfdint已连接的 socket 文件描述符(由 connect()accept() 获得)
bufconst void*指向要发送的数据缓冲区
lensize_t要发送的字节数
flagsint发送控制标志,常设为 0(可选其他如 MSG_DONTWAITMSG_NOSIGNAL
返回值说明
> 0实际成功发送的字节数(可能小于 len,需循环发送
== 0理论上不会出现,若出现可能是连接已关闭
< 0发送失败,返回 -1,可通过 errno 查看错误原因

使用示例

std::string outstring;
std::getline(std::cin, outstring);

ssize_t s = send(sockfd, outstring.c_str(), sizeof(outstring), 0);
if (s > 0)
{
    char inbuffer[1024];
    //recv用于接收数据
    //ssize_t recv(int sockfd, void *buf, size_t len, int flags);
    ssize_t m = recv(sockfd, inbuffer, sizeof(inbuffer) - 1, 0);
    if (m > 0)
    {
        inbuffer[m] = '\0';
        std::cout << inbuffer << std::endl;
    }
    else 
    {
        break;
    }
}
else
{
    break;
}

小结

介绍了TCP常用接口和使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值