TCP(三)

TCP server

在cs模型中,server只需要等待client的连接,因此server包括下面四个步骤:

  1. 调用socket(),初始化一个socket实例,
  2. 调用bind(),将socket绑定到网卡上,
  3. 调用listen(),等待客户端的连接,
  4. 与客户端三次握手后,调用accept()。

server的代码如下:

//server.cpp

#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <errno.h>
#include <sys/socket.h>
#include <iostream>
#include <cstring>


int main(){
    const int port = 9007;
    const std::string ip= "127.0.0.1";
//初始化socket
    int server_sock = socket(AF_INET, SOCK_STREAM, 0);
    if(server_sock < 0){
        std::cout << "create socket failed:" << strerror(errno) << std::endl;
        return 0;
    }
//绑定地址,sockaddr_in是sockaddr的升级,sockaddr已经弃用
    struct sockaddr_in server;
    bzero(&server,sizeof(server));
    server.sin_family = AF_INET;
    //将主机字节序转换为网络字节序
    server.sin_port = htons(port);
    server.sin_addr.s_addr = inet_addr(ip.c_str());
    socklen_t len = sizeof(server);
//绑定时,由于bind函数设计较早,因此还是需要将sockarr_in转换成sockaddr
    if(bind(server_sock, (struct sockaddr*) &server, len)<0){
        std::cout << "bind socket failed:" << strerror(errno) << std::endl;
        return 0;
    }
//调用listen,等待客户端连接,listen第二个参数为全连接队列的大小
    listen(server_sock, 5);  

    char buf[1024];
    while(1){
        struct sockaddr_in client;
        socklen_t client_len = sizeof(client);
        bzero(&client,sizeof(client));
        std::cout << "------------waiting for connect-------------" << std::endl;
        int client_sock = accept(server_sock, (struct sockaddr*) &client, &client_len);
        if(client_sock < 0){
            std::cout << "accept socket failed:" << strerror(errno) << std::endl;
            continue; 
        }

        int client_port = ntohs(client.sin_port);
        char client_ip[1024];
        const char *ptr = inet_ntop(AF_INET,&client.sin_addr, client_ip, sizeof(client_ip)); 
        if(!ptr){
            std::cout << "transform client address failed:" << strerror(errno) << std::endl;
        }
        int nbytes;
        while(nbytes = read(client_sock, buf, sizeof(buf)-1)>0){
            std::cout << "receive msg:" << buf << std::endl;
        }
        close(client_sock);
    }
    close(server_sock);
    return 0;
}

需要注意的是,server调用accept后,创建了一个新的socket与客户端通讯,而不是直接用server_sock,否则每个server只能连接一个client。

TCP client

client需要主动向server发起请求,需要的操作如下:

  1. 调用socket(),初始化一个socket实例,
  2. 调用connect(),发起三次握手,建立连接,
  3. 向server发送数据。
//client.cpp

#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <errno.h>
#include <sys/socket.h>
#include <iostream>
#include <cstring>


int main(){
    const int port = 9007;
    const std::string ip= "127.0.0.1";

    int client_sock = socket(AF_INET, SOCK_STREAM, 0);
    if(client_sock < 0){
        std::cout << "create socket failed:" << strerror(errno) << std::endl;
        return 0;
    }

    struct sockaddr_in server;
    bzero(&server,sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(port);
    server.sin_addr.s_addr = inet_addr(ip.c_str());
    socklen_t len = sizeof(server);

    if(connect(client_sock, (struct sockaddr*) &server, len)<0){
        std::cout << "connect socket failed:" << strerror(errno) << std::endl;
        return 0;
    }

    std::string msg;
    while(std::cin >> msg){
        if(msg == "EOF")break;
        write(client_sock, msg.c_str(), sizeof(msg));
    }
    close(client_sock);
    return 0;
}

c/s模型通信验证

将上面的server.cpp编译为server,client.cpp编译为client,运行程序建立通讯,并使用wireshark抓包。
在这里插入图片描述
在上面的过程中,client已经调用了connect()函数,wireshark中捕获到了相应的三次握手操作:
在这里插入图片描述
端口48154是操作系统自动为client分配的端口,三次握手与我们在TCP(二)中描述的一致。此时可以从client发送数据给server:
在这里插入图片描述
此时wireshark的抓包情况如下:
在这里插入图片描述
client发送了32字节的数据给server,这32字节的数据包括20字节的TCP固定首部和12字节的“hello,world!",server确认ack为32+1 = 33,这序号从0开始是因为wireshark为了方便分析,将序列号都转换为了相对序列号。
最后我们断开连接,看看四次挥手的情况。
在这里插入图片描述
在这里插入图片描述
这里四次挥手变成了三次,是因为在close_wait状态server没有需要发送的数据,因此直接进入了last_ack状态。
这里我们再通过netstat,可以看到client的time_wait状态。
在这里插入图片描述
localhost的端口号位48452是因为之前忘记截图了,重新运行client进行四次挥手,因此操作系统重新分配了端口号,这也说明client的端口号是随机分配的。
netstat是Linux下非常强大的网络工具,通过netstat可以看到tcp连接各状态的变化,但由于变化很快不好截图,感兴趣的小伙伴可以自己尝试一下。

总结

本文通过运行tcp通信例程,验证了前面介绍的内容,同时介绍了几个linux下强大的网络分析工具,这些工具还有很多,有机会接触我们继续介绍,下篇文章将对tcp连接的疑难杂症进行介绍和试验。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值