socket套接字编程

目录

常见的socket API

sockaddr结构

封装UDPSocket

udp客户端代码

udp服务端代码

封装TCPSocket

tcp服务端

 tcp客户端


常见的socket API

// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);
// 绑定端口号 (TCP/UDP, 服务器)      
int bind(int socket, const struct sockaddr *address,socklen_t address_len);
// 开始监听socket (TCP, 服务器)
int listen(int socket, int backlog);
// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address,socklen_t* address_len);
// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

sockaddr结构

socket API 是一层抽象的网络编程接口 , 适用于各种底层网络协议 , IPv4 IPv6. 然而 , 各种网络协议的地址格式并不相同
  • IPv4IPv6的地址格式定义在netinet/in.h,IPv4地址用sockaddr_in结构体表示,包括16位地址类型, 16 位端口号和32IP地址.
  • IPv4IPv6地址类型分别定义为常数AF_INETAF_INET6. 这样,只要取得某种sockaddr结构体的首地址, 不需要知道具体是哪种类型的sockaddr结构体,就可以根据地址类型字段确定结构体中的内容.
  • socket API可以都用struct sockaddr *类型表示, 在使用的时候需要强制转化成sockaddr_in; 这样的好 处是程序的通用性, 可以接收IPv4, IPv6, 以及UNIX Domain Socket各种类型的sockaddr结构体指针做为参数;
虽然 socket api 的接口是 sockaddr, 但是我们真正在基于 IPv4 编程时 , 使用的数据结构是 sockaddr_in; 这个结构里主 要有三部分信息: 地址类型 , 端口号 , IP 地址 .

封装UDPSocket

//udpsocket.cpp
#include<cstdio>
#include<unistd.h>
#include<iostream>
#include<string.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<sys/socket.h>
using namespace std;
class UdpSocket
{

  public:
    UdpSocket()
      :_sockfd(-1)
    {}
    bool Socket(){
      _sockfd=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
      if(_sockfd<0)
      {
        perror("socket error");
        return false;
      }
      return true;
    }
    bool Bind(const std::string &ip,int port)
    {
      struct sockaddr_in addr;
      addr.sin_family=AF_INET;
      addr.sin_port=htons(port);
      addr.sin_addr.s_addr=inet_addr(ip.c_str());
      socklen_t len=sizeof(struct sockaddr_in);
      if(bind(_sockfd,(struct sockaddr*)&addr,len)<0)
      {
        perror("bind error");
        return false;
      }
      return true;
    }
    bool Send(const std::string &data,const std::string &ip,int port)
    {
      struct sockaddr_in peeraddr;
      peeraddr.sin_family=AF_INET;
      peeraddr.sin_port=htons(port);
      peeraddr.sin_addr.s_addr=inet_addr(ip.c_str());
      socklen_t len=sizeof(struct sockaddr_in);
      int ret=sendto(_sockfd,&data[0],data.size(),0,(struct sockaddr*)&peeraddr,len);
      if(ret<0)
      {
        perror("sendto error");
        return false;
      }
      return true;
    }
    bool Recv(std::string *buf,std::string *ip=NULL,int *port=NULL)
    {
      struct sockaddr_in peeraddr;
      socklen_t len=sizeof(struct sockaddr_in);
      char tmp[4096]={0};
      int ret=recvfrom(_sockfd,tmp,4095,0,(struct sockaddr*)&peeraddr,&len);
      if(ret<0)
      {
        perror("recvfrom error");
        return false;
      }
      buf->assign(tmp,ret);
      if(ip!=NULL)
        *ip=inet_ntoa(peeraddr.sin_addr);
      if(port!=NULL)
        *port=ntohs(peeraddr.sin_port);
      return true;
    }
    bool Close()
    {
      if(_sockfd>0)
      {
        close(_sockfd);
        _sockfd=-1;
      }
      return true;
    }

  private:
    int _sockfd;
};

udp客户端代码

#include<iostream>
#include<cstdlib>
#include"udpsocket.cpp"
#include<string.h>
#define CHECK_RET(q) if((q)==false){return -1;}
using namespace std;
int main(int argc,char*argv[])
{
  if(argc<3)
  {
    cout<<"usage:请输入服务端地址信息!\n";
    cout<<"\t./udp_client 192.168.1.2 9000\n";
    return -1;
  }
  string srv_ip=argv[1];
  int srv_port=atoi(argv[2]);
  UdpSocket sock;
  CHECK_RET(sock.Socket());
  while(1)
  {
    string buf;
    cout<<"client input";
    cin>>buf;
    CHECK_RET(sock.Send(buf,srv_ip,srv_port));
    buf.clear();
    CHECK_RET(sock.Recv(&buf));
    cout<<"sever response: "<<buf<<endl;
  }
  sock.Close();
  return 0;
  
}

udp服务端代码

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<string.h>
int main(int argc,char*argv[])
{
  if(argc<3)
  {
    printf("usage: ./udp_srv 192.168.1.2 9000\n");
    return -1;
  }
  char* srv_ip=argv[1];
  int srv_port=atoi(argv[2]);
  int sockfd=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
  if(sockfd<0)
  {
    perror("socket error");
    return -1;
  }
  struct sockaddr_in addr;
  addr.sin_family=AF_INET;
  addr.sin_port=htons(srv_port);
  addr.sin_addr.s_addr=inet_addr(srv_ip);
  socklen_t len=sizeof(addr);
  int ret=bind(sockfd,(struct sockaddr*)&addr,len);
  if(ret<0)
  {
    close(sockfd);
    perror("bind error");
    return -1;
  }
  //3.接收数据,接收对端地址
  while(1){
    char buf[4096]={0};
    struct sockaddr_in client_addr;
    len=sizeof(client_addr);
    ret=recvfrom(sockfd,buf,4095,0,(struct sockaddr*)&client_addr,&len);
    if(ret<0)
    {
      close(sockfd);
      perror("recvfrom error");
      return -1;
    }
    printf("%s:%d - %s\n",inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port),buf);
    printf("server input");
    fflush(stdout);
    memset(buf,0x00,4096);
    scanf("%s",buf);
    ret=sendto(sockfd,buf,strlen(buf),0,(struct sockaddr*)&client_addr,len);
    if(ret<0)
    {
      close(sockfd);
      perror("sendto error");
      return -1;
    }
  }
  close(sockfd); 
 return 0;
}
sockaddr_in 中的成员 struct in_addr sin_addr 表示 32 位 的 IP 地址
但是我们通常用点分十进制的字符串表示 IP 地址 , 以下函数可以在字符串表示 和 in_addr 表示之间转换 ;
字符串转in_addr的函数
in_addr转字符串的函数

 其中inet_ptoninet_ntop不仅可以转换IPv4in_addr,还可以转换IPv6in6_addr,因此函数接口是void *addrptr。

封装TCPSocket

#include<cstdio>
#include<iostream>

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

#define MAX_LISTEN 5
#define CHECK_RET(q) if((q)==false){return -1;}
class TcpSocket
{
  public:
    TcpSocket()
      :_sockfd(-1)
    {}
    bool Socket()
    {
      _sockfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
      if(_sockfd<0)
      {
        perror("socket error");
        return false;
      }
      return true;
    }
    bool Bind(const std::string&ip,int port)
    {
      struct sockaddr_in addr;
      addr.sin_family=AF_INET;
      addr.sin_port=htons(port);
      addr.sin_addr.s_addr=inet_addr(ip.c_str());
      socklen_t len=sizeof(addr);
      if(bind(_sockfd,(struct sockaddr*)&addr,len)<0)
      {
        perror("bind error");
        return false;
      }
      return true;
    }
    bool Listen(int backlog=MAX_LISTEN)
    {
      if(listen(_sockfd,backlog)<0)
      {
        perror("listen error");
        return false;
      }
      return true;
    }
    bool Connect(const std::string &srv_ip,int srv_port)
    {
      struct sockaddr_in addr;
      addr.sin_family=AF_INET;
      addr.sin_port=htons(srv_port);
      addr.sin_addr.s_addr=inet_addr(srv_ip.c_str());
      socklen_t len=sizeof(addr);
      if(connect(_sockfd,(struct sockaddr*)&addr,len)<0)
      {
        perror("connect error");
        return false;
      }
      return false;
    }
    bool Accept(TcpSocket *new_sock,std::string *cli_ip,int *cli_port)
    {
      struct sockaddr_in addr;
      socklen_t len=sizeof(addr);
      int newfd=accept(_sockfd,(struct sockaddr*)&addr,&len);
      if(newfd<0)
      {
        perror("accept error");
        return false;
      }
      std::cout<<"new connect:"<<inet_ntoa(addr.sin_addr)<<":"<<ntohs(addr.sin_port)<<"\n";
      new_sock->_sockfd=newfd;
      if(cli_ip!=NULL)
      {
        *cli_ip=inet_ntoa(addr.sin_addr);
      }
      if(cli_port!=NULL)
      {
        *cli_port=ntohs(addr.sin_port);
      }
      return true;
    }
    bool Send(const std::string &data)
    {
      ssize_t ret=send(_sockfd,data.c_str(),data.size(),0);
      {
        if(ret<0)
        {
          perror("send error");
          return false;
        }
        return true;
      }
    }
    bool Recv(std::string *buf)
    {
      char tmp[4096]={0};
      ssize_t ret=recv(_sockfd,tmp,4096,0);
      if(ret<0)
      {
        perror("recv error");
        return false;
      }
      else if(ret==0)
      {
        std::cout<<"perr shutdown!\n";
        return false;
      }
      buf->assign(tmp,ret);//从tmp截取指定长度的数据,防止数据里面有字符串结尾标志;
      return true;
    }
    bool Close()
    {
      if(_sockfd>0)
      {
        close(_sockfd);
        _sockfd=-1;
      }
      return true;
    }

  private:
    int _sockfd;

};

tcp服务端

#include"tcp_socket.cpp"
int main(int argc,char *argv[])
{
  if(argc<2)
  {
    std::cout<<"usage: ./tcp_srv 9000\n";
    return -1;
  }
  int port = std::stoi(argv[1]);
  //创建套接字
  TcpSocket lst_sock;
  CHECK_RET(lst_sock.Socket());
  //绑定地址信息
  CHECK_RET(lst_sock.Bind("0.0.0.0",port));
  //开始监听
  CHECK_RET(lst_sock.Listen());
  while(1)
  {
    TcpSocket new_sock;
    std::string cli_ip;
    int cli_port;
    CHECK_RET(lst_sock.Accept(&new_sock,&cli_ip,&cli_port));
    std::cout<<"new connect: "<<cli_ip<<":"<<cli_port<<"\n";
    std::string buf;
    new_sock.Recv(&buf);//通过新建连接与指定客户端进行通信
    std::cout<<"client say: "<<buf<<std::endl;
    buf.clear();
    std::cout<<"server say: ";
    std::cin>>buf;
    new_sock.Send(buf);
  }
  lst_sock.Close();
  return 0;
}

 tcp客户端

#include"tcp_socket.cpp"
int main(int argc,char*argv[])
{
  //客户端虽然不用主动绑定地址,但是必须知道服务端的地址;
  if(argc<3)
  {
    std::cout<<"usage: arg error\n";
    std::cout<<"\t ./tcp_cli 192.168.2.2 9000\n";
    return -1;
  }
  int srv_port=std::stoi(argv[2]);
  std::string srv_ip=argv[1];
  TcpSocket sock;
  //创建套接字
  CHECK_RET(sock.Socket());
  //向服务端发起连接请求
  CHECK_RET(sock.Connect(srv_ip,srv_port));
  while(1)
  {
    std::string data;
    std::cout<<"client say:";
    fflush(stdout);
    std::cin>>data;
    sock.Send(data);
    CHECK_RET(sock.Send(data));
    data.clear();
    CHECK_RET(sock.Recv(&data));
    std::cout<<"server say:"<< data<<std::endl;
  }
 // 关闭套接字
 sock.Close();
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值