网络编程套接字(详细)

一.IP地址和目的IP地址
在IP数据包头部,有两个IP地址,一个是源IP地址,一个是目的IP地址
二.端口号
1.端口号是一个2字节16位的整数
2.端口号是用来标识一个进程,告诉操作系统当前的数据需要交给哪一个进程来处理。
3.ip地址和端口号可以标识一台主机的某一个进程。
4.一个端口号只能被一个进程占用
三.什么是TCP和UDP协议?
TCP协议:有连接, 可靠传输,面向字节流
UDP协议:无连接,不可靠传输,面向数据报
二者均为传输层协议。
四.网络字节序
1.网络数据流当中同样有大端和小端之分。
2.TCP/IP协议规定网络字节流采用大端字节序,即低地址高字节
3.无论当前主机是大端机还是小端机,都要遵循这个协议。
4.如果当前主机是小端,则应该讲数据转化为大端。如果当前主机是小端,则不用进行转换。
在这里我们常用 htons库函数(如果是小端就转换为大端,如果是大端就返回)来进行转换,

#include<arpa/inet.h>
uint16_t htons(uint16_t hostshort)

五.套接字是什么?
套接字是在两个程序进行通讯连接的一个端点,是连接应用程序和网络驱动程序的桥梁。运行在不同机器上的进程彼此通过向套接字发送报文来进行通信。如果将进程比作是一座房子,那么套接字就相当于是一扇门。
六.什么是sockaddr结构
1.IPV4和IPV6定义在netinet/in.h,IPV4地址用sockaddr_in结构体表示,包含16位地址,16位端口号和32位IP地址
2.IPV4和IPV6地址类型分别为AF_INET,AF_INET6表示,只要取得sockaddr结构体的首地址,就可以根据地址类型字段确定结构体当中的内容
七.socket编程接口
1.创建socket

int socket(int domain,int type,int protocol)

这个接口通常是创建并初始化socket。domain通常为AF_INET(IPV4)或AF_INET6(IPV6),type表示socket的类型,通常为流stream(SOCK_STREAM)或数据报文(SOCK_DGRAM),protocol通常设为0,以便让系统自动选择合适的协议。
2.绑定

int bind(int sockfd,const struct sockaddr *address.socklen_t address_len)

这个接口将socket与特定的主机地址和端口绑定在一起,成功绑定返回0,失败返回-1.成功绑定后根据协议的不同,我们可以对socket进行不同的操作。对UDP协议来说,因为他是无连接的,所以绑定成功后便可以利用socket来传输数据了。对TCP来说绑定成功后还需要监听操作:

listen(int sockfd,int backlog)

这里的backlog是已完成连接队列的大小。
3.接收请求

int accept(int sockfd,struct sockaddr* address,socklen_t address_len)

这个接口接收客户端的请求,并将客户端的网络地址信息保存到address中。当客户端接收连接请求后,客户端和服务器之间的链路就建立好了,二者便可以进行通信了。
4.连接

int connect(int sockfd,const struct sockaddr* addr,socklen_t addr_len)

客户端向特定的网络地址的服务器发送连接请求,连接成功返回0,失败返回-1。只要连接建立好了,就可以通过send/receive接口发送或接收数据了。调用了connect设置了默认网络地址的UDP socket也可以调用该接口来发送数据。这里addr为服务端的地址信息(当前客户端需要连接哪一个服务端的地址信息),addr_len为地址信息的长度。
5.发送

size_t send(int sockfd,void* buf,size_t len,ing flags)

这里的buf是要发送的数据,len是发送的数据长度,flags,0:阻塞发送
6.接收

size_t recv(int sockfd,void* buf,size_t len,int flags)

这里的buf是收到的数据所放的地方,len是最大能接收的长度,flags 0:阻塞接收
7.关闭socket

int close(int socket)

八.下来我们通过上面的知识与接口来用TCP来建立通信
1.封装TCP socket

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

class TcpSvr
{
  public:
    TcpSvr()
    {
        Sock=-1;
    }
    ~TcpSvr()
    {

    }
    bool Createsock()
    {
      Sock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);//:调用socket接口创建socket,成功返回0,失败返回-1
      if(Sock<0)
      {
        perror("socket");
        return false;
      }
      return true;
    }

    bool Bind(std::string& ip,uint16_t 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());
      int ret=bind(Sock,(struct sockaddr*)&addr,sizeof(addr));//:调用bind接口进行绑定
      if(ret<0)
      {
        perror("bind");
        return false;
      }
      return true;


    }
    
    bool Listen(int Backlog=5)//:对tcp来说绑定完成后还需要监听,三次握手就是在监听阶段完成的
    {
     int ret= listen(Sock,Backlog);//:调用监听接口
     if(ret<0)
     {
        perror("listen");
       return false;
     }
     return true;

    }

    bool Connect(std::string& ip,uint16_t port)//:连接接口,对客户端而言,他需要知道要连接的IP地址和端口信息
    { 
      struct sockaddr_in destaddr;
      destaddr.sin_family=AF_INET;
      destaddr.sin_port=htons(port);
      destaddr.sin_addr.s_addr=inet_addr(ip.c_str());
      int ret=connect(Sock,(struct sockaddr*)&destaddr,sizeof(destaddr));//:调用连接接口
      if(ret<0)
      {
        perror("connect");
        return false;
      }
      return true;
        

    }
    bool Accept(TcpSvr& ts,struct sockaddr_in* addr=NULL)
    {
      struct sockaddr_in peeraddr;
      socklen_t addrlen=sizeof(struct sockaddr_in);
      int newsock=accept(Sock,(struct sockaddr*)&peeraddr,&addrlen);//:调用接收请求接口
      if(newsock<0)
      {
        return false;
      }
      ts.Sock=newsock;
      if(addr!=NULL)
      {
        memcpy(addr,&peeraddr,addrlen);
      }
      return true;

    }
    bool Recv(std::string& buffer)
    { 
        char buf[1024]={0};
        int ret=recv(Sock,buf,sizeof(buf)-1,0);
        if(ret<0)
        {
            perror("recv failed");
            return false;
        }
        else if(ret==0)
        {
          //:对端连接将关闭
          printf("peer close this connect");
          return false;
        }
        buffer.assign(buf,ret);
        return true;
    }

    bool Send(std::string buffer)
    {
        int ret=send(Sock,buffer.c_str(),buffer.size(),0);
        if(ret<0)
        {
          perror("send");
          return false;
        }
        return 0;
    }
    
    void Close()
    {
        close(Sock);
        Sock=-1;
    }
  private:
    int Sock;
  
};

2.服务端

#include"tcpsvr.hpp"

int main(int argc,char* argv[])
{
  if(argc!=3)
  {
    printf("./svr[ip][port]\n");
    return 0;
  }
  std::string ip=argv[1];
  uint16_t port=atoi(argv[2]);
  TcpSvr ts;
  if(!ts.Createsock())
  {
      return 0;
  }
  if(!ts.Bind(ip,port))
  {
    return 0;
  }
  if(!ts.Listen())
  {
    return 0;
  }
  while(1)
  {
    TcpSvr peerts;
    struct sockaddr_in peeraddr;
    if(!ts.Accept(peerts,&peeraddr))
    {
        printf("svr have a new connect,ip,port->%s:%d\n",inet_ntoa(peeraddr.sin_addr),ntohs(peeraddr.sin_port));
    }
    std::string buf;
    peerts.Recv(buf);
    printf("client say:%s\n",buf.c_str());
    printf("sever say:");
    fflush(stdout);
    std::cin>>buf;
    peerts.Send(buf);
  }
  ts.Close();
  return 0;
}

3.客户端

#include"tcpsvr.hpp"

int main(int argc,char* argv[])
{
      if(argc!=3)
      {
        printf("./cli[ip][port]\n");
        return 0;
      }

      std::string ip=argv[1];
      uint16_t port=atoi(argv[2]);


      TcpSvr ts;
      if(!ts.Createsock())
      {
        return 0;
      }
      if(!ts.Connect(ip,port))
      {
        return 0;
      }
      while(1)
      {
        printf("client say:");
        fflush(stdout);
        std::string buf;
        std::cin>>buf;
        ts.Send(buf);
        ts.Recv(buf);
        printf("server say:%s\n",buf.c_str());
      }
      ts.Close();
      return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值