socket实现TCP网络通信程序

TCP网络通信

TCP通信是面向连接、可靠传输、面向字节流的

服务端流程

  1. 创建套接字:内核中创建socket结构体
  2. 绑定地址信息:通过socket描述源端地址信息
  3. 开始监听:告诉OS开始接收连接请求,由于TCP是面向连接的,通信前要建立连接。服务端要**接受客户端的连接请求【监听套接字】**会为客户端创建新的socket【通信套接字】,新的套接字中包含源端信息和对端信息,只用于和对应的客户端进行通信
  4. 服务端程序中获取新建通信套接字的操作句柄描述符,后续的通信都是通过这个操作句柄完成的
  5. 接收、发送数据:建立连接后直接进行
  6. 关闭套接字:释放资源

客户端流程

  1. 创建套接字
  2. 绑定地址信息
  3. 向服务端发起连接请求
  4. 收发数据
  5. 关闭套接字

TCP编程socket接口
1.创建套接字:int socket(int domain,int type,int protocol)
IPv4:AF_INET;流式套接字:SOCK_STREAM;TCP:IPPROTO_TCP

2.绑定地址信息:int bind(int sockfd,struct sockaddr *addr,socklen_t len);struct sockaddr_in;

3.服务端开始监听:int listen(int sockfd,int backlog)

SYN泛型攻击 :恶意主机不断向服务端发送连接请求,由于服务器要为连接创建socket,将会瞬间资源耗尽,服务器崩溃
监听接口:int listen(int sockfd,int backlog)
因此,服务端用connection pending queue来存放连接套接字,backlog参数决定该队列的最大节点数量,如果队列满了还有新连接请求到来就将其丢弃

4.获取新建通信套接字的操作句柄:从内核指定socket的pending queue中取出一个socket,与指定的客户端通信,返回操作句柄
int accpet(int sockfd,struct sockaddr *addr,socklen_t len)//len:指定地址信息想要的长度及返回实际的长度

5.通过新获取的套接字操作句柄与指定客户端通信
接收数据:ssize_t recv(int sockfd,char* buf,int len,int flag)//成功返回实际读取的数据长度,连接断开返回0,读取失败返回-1
发送数据:ssize_t send(int sockfd,char* data,int len,int flag)//成功返回实际发送的数据长度,连接断开触发异常导致进程退出,失败返回-1

6.关闭套接字:int close(int fd)

7.客户端向服务端发送连接请求:int connect(int sockfd,int sockaddr *addr,socklen_t len)

通过上面的理解写出以下流程:

while(1)
{
   
	int newfd=accept();//获取新连接
	//通信
	ssize_t ret=recv();
	ssize_t ret=send();
}

运行后发现服务端客户端无法持续通信
accept是阻塞函数,当没有获取到新连接时阻塞等待直到有新连接
recv/send也是阻塞函数,接收缓冲区没有数据(客户端没有发送数据)recv阻塞,发送缓冲区数据满了send阻塞
这两处都可能发生阻塞导致流程无法推进

解决
防止流程阻塞,就要保证一个执行流只负责一个功能:让一个执行流来获取新连接,获取成功后创建新执行流与客户端通信

实现

封装TCPsocket类向外提供简单接口

#include <cstdio>
#include <string>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define BACKLOG 10
#define CHECK_RET(q) if((q)==false){return -1;}

class TcpSocket
{
   
    public:
        TcpSocket():_sockfd(-1){
   
        }
        int GetFd() {
   
            return _sockfd;
        }
        void SetFd(int fd) {
   
            _sockfd = fd;
        }
        //创建套接字
        bool Socket() {
   
            //地址域,套接字类型,协议类型
            _sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
            if (_sockfd < 0) {
   
                perror("socket error");
                return false;
            }
            return true;
        }
        void Addr(struct sockaddr_in *addr, const std::string &ip, uint16_t port) {
   
            addr->sin_family = AF_INET;
            addr->sin_port = htons(port);
            inet_pton(AF_INET, ip.c_str(), &(addr->sin_addr.s_addr));
        }
        //绑定地址信息
        bool Bind(const std::string &ip, const uint16_t port) {
   
            //定义IPv4地址结构
            struct sockaddr_in addr;
            Addr(&addr, ip, port);
            socklen_t len = sizeof(struct sockaddr_in);
            int ret = bind(_sockfd, (struct sockaddr*)&addr, len);
            if (ret < 0) {
   
                perror("bind error");
                return false;
            }
            return true;
        }
        //服务端开始监听
        bool Listen(int backlog = BACKLOG) {
   
            int ret = listen(_sockfd, backlog);
            if (ret < 0) {
   
                perror("listen error");
                return false;
            }
            return true;
        }
        //客户端发起连接请求
        bool Connect
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值