套接字编程TCP篇

TCP通信编程

TCP通信编程的流程:
在这里插入图片描述
模拟服务端和客户端通信的过程:
在这里插入图片描述

接口的使用:
1.创建套接字:

int socket(int domain,int type,int protocol)
domain:地址域 AF_INET/AF_INET6
type:SOCK_STREAM(TCP流式套接字)       SOCK_DGRAM(UDP数据报套接字)
protocol:IPPROTO_TCP        IPPROTO_UDP
返回socket操作句柄

2.绑定地址信息:

int bind(int sockfd,(struct sockaddr*)addr,socklen_t len)

3.开始监听

listen(int sockfd,int backlog)
sockfd:将哪个socket设置为监听套接字,就传入哪个套接字的句柄
backlog:同一时间的并发连接数,决定同一时间最多接受多少个客户端的连接请求

如果有一个恶意主机同一时间向服务器无限制的发送连接请求,服务端是不是要一直为每一条请求创建套接字?如果是这样,最终只有一个结构就是资源耗尽,系统崩溃。
backlog就是解决这个问题的,前面流程里面说到,监听阶段,监听服务器收到连接请求创建套接字放入未完成连接队列中,绑定好五元组信息后放入已完成连接队列中。
backlog就是决定这个未完成连接队列的节点个数的。但只决定同一时间的节点个数,但不决定系统可以接受的最大客户端连接。
在这里插入图片描述

4.获取新建连接

从已完成连接队列中取出一个socket,返回这个socket的操作句柄,这样就可以通信了。
int accept(int sockfd,(struct sockaddr*)cli_addr,socklen* len);
sockfd : 监听套接字,表示要获取哪个tcp服务端套接字的新建连接(可能会有很多TCP服务端)
cli_addr:这个新建套接字的客户端地址信息
len:地址信息长度
返回值:新建套接字的操作句柄 --- 外部程序中的套接字句柄

5.收发数据

ssize_t recv(int sockfd,char* buf,int len,int flag);
默认阻塞,没有数据则等待,连接断开返回0,不在阻塞
ssize_t send(int sockfd,char* data,int len,int flag);
默认阻塞,缓冲区满了则等待,连接断开则出发SIGPIPE异常

6.关闭套接字

close(sockfd);                #include<unistd.h>

7.发送连接请求

int connect(int sockfd,struct sockaddr* srv_addr, int len);
srv_addr:服务端的地址信息
connet这个接口中也会描述服务端的地址信息(五元组),所以收发数据只需要直接收发即可。

简易TCP通信流程

socket类的封装
封装一个TcpSocket类,实例化的每一个对象都是一个udp通信连接,通过这个对象的成员方法完成通信流程。

class TcpSocket{
public:
	TcpSocket():_sockfd(-1);
	bool Socket();
	bool Bind(const std::string& ip,uint16_t port);
	bool Listen(int backlog = MAX_LISTEN);
	bool Accept(TcpSocket* new_sock,std::string* ip =NULL,uint16_t* port);
	bool recv(std::string* buf);
	bool send(const std::string& data);
	bool Close();
	bool Connect(const std::string& ip,uint16_t port);
private:
	int _sockfd;
#pragma once                                                                                                                     
#include<iostream>    
#include<string>    
#include<unistd.h>    
#include<sys/socket.h>    
#include<netinet/in.h>    
#include<arpa/inet.h>    
using namespace std;    
    
class TcpSocket    
{    
  private:    
    int sockfd;    
  public:    
    TcpSocket()    
    {    
      if(!Socket())    
      {    
        cout<<"socket create failed"<<endl;    
      }    
    }    
    ~TcpSocket()    
    {    
      Close();    
    }    
  public:    
    bool Socket()
    {
      sockfd = socket(AF_INET,SOCK_STREAM,0);
      if(sockfd == 0)
      {
        cerr<<"scoket error"<<endl;
        return false;
      }
      return true;
    }

    //有可能错
    void Addr(struct sockaddr_in* addr, const string& ip,const uint16_t& port)
    {
      addr->sin_family = AF_INET; 
      addr->sin_port = htons(port);
      addr->sin_addr.s_addr = inet_addr(ip.c_str());                              
    }

    bool Bind(const string& ip,const uint16_t& port)
    {
      struct sockaddr_in addr;
      Addr(&addr,ip,port);                                                                                                       
      socklen_t len = sizeof(addr);

      int ret = bind(sockfd,(struct sockaddr*)&addr,len);
      if(ret < 0)
      {
              cerr<<"bind errror"<<endl;
        return false;
      }
      return true;
    }

    bool Listen()
    {
      int ret = listen(sockfd,5);
      if(ret == -1) 
      {
        cerr<<"listen error"<<endl;
        return false;
      }
      return true;
    }

    bool Connect(const string& ip,const uint16_t& port)
    {
      struct sockaddr_in addr;
      Addr(&addr,ip,port);
      socklen_t len = sizeof(addr);                                                                                              

      int ret = connect(sockfd,(struct sockaddr*)&addr,len);
      if(ret < 0)
      {
        cerr<<"connect error"<<endl;
        return false;
      }
      return true;
    }

    bool Accept(TcpSocket* new_sock,string* ip = NULL,uint16_t* port = NULL)
    {
      struct sockaddr_in addr;
      socklen_t len = sizeof(addr);
      int newfd = accept(sockfd,(struct sockaddr*)&addr,&len);
      if(newfd < 0)
      {
        cerr<<"accept error"<<endl;
        return false;
      }
      new_sock->sockfd = newfd;
      if(ip != NULL)
      {
        *ip = inet_ntoa(addr.sin_addr);
      }
      if(port != NULL)
      {                                                                                                                          
        *port = ntohs(addr.sin_port);
      }
      return true;
    }
    bool Recv(string* buf)
    {
      char tmp[4096] = {0};
      int ret = recv(sockfd,tmp,4095,0);;
      if(ret <0)
      {
        cerr<<"recv error"<<endl;
        return false;
      }
      *buf = tmp;
      buf->assign(tmp);
      return true;
    }
    bool Send(const string& data)
    {
      //UDP是数据报传输,整条交付
      //而TCP是字节流传输,能传输多少就传输多少,对数据边界并没有进行区分
      //如果对方的缓冲区只剩了50个字节的空间,send给对方100个字节,就只能成功发送50个字节,并返回传送大小50字节
      size_t total = 0 ;
      while(total < data.size())
      {
        int ret = send(sockfd,&data[0]+total,data.size()-total,0);
        if(ret < 0)
        {
          cerr<<"send error"<<endl;
          return false;
        }
        total+=ret;
      }
      return true;
    }

    bool Close()
    {
      close(sockfd);
      return true;
    }                                                                                                                            
};

  
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值