网络编程---TCP/UDP套接字编程原理

本篇介绍的是Linux下的网络编程,故有些接口是不适用于Windows的,但是具体概念和实现方法是大体一致的
本篇重在讲解原理,具体实现请戳这里->UDP套接字编程实现

介绍

  • 网络编程套接字(socket)也是进程间通信的一种方式,但是不同于管道,消息队列,共享内存的是socket不仅可以实现本机内不同进程间的通信,也可以实现网络中两台不同主机间的进程通信。
  • socket相当于是一个文件描述符,我们将数据写入socket中,再发到目标主机,目标主机接收socket,再从socket中读取数据,至此便实现了网络中两台主机间的数据传输,即进程通信。

  • 举个栗子:如果是两个人进行通信的话,首先要知道彼此的电话号码,而且要明确两个人谈论的事项,即使是闲聊也算生活事项的一种。网络中的两台主机进行通信也是如此,电话号码即彼此的ip地址,谈论的事项即彼此主机上确定的一个端口号

解释ip地址+端口号

网络编程套接字可以看做是ip地址+端口号,ip地址为了明确网络中唯一一台主机,而端口号确定了这台主机上的唯一一个进程,因为一个端口号只能被一个进程占用,而且只有网络进程才有端口号。

IP地址

IP协议有两个版本,IPv4和ipv6,现在普遍用的都是IPv4,IPv6是为了解决现有的IP地址可能不够的情况,但还没有普及开来。我们接下来使用的全是IPv4协议。

  • IP地址是在IP协议中,用来标识网络中不同主机的地址;
  • 对于IPv4来说,IP地址是一个4字节,32位的整数;
  • 我们通常也使用“点分十进制”的字符串标识IP地址,例如192.168.0.1;用点分割的每一个数字表示一个字节,范围是0~255。

端口号

端口号是传输层协议的内容

  • 端口号是一个2字节16位的整数;
  • IP地址+端口号用来标识网络中某一台主机上的唯一一个进程;
  • 一个端口号只能被一个进程占用,只有网络进程才有端口号。

网络字节序

大家可能刚看到这个觉得有点陌生,我来为大家解释一下。

  我们都知道我们的计算机存储数据时是分大端和小端的,而且主机和主机之间的存储模式是不固定的,也就是没有统一的标准,即如果你是大端的模式,而对方却是小端的模式,你们在交换数据时就会发生错误,所以为了避免差异化,TCP/IP协议规定,网络数据流应采用大端字节序,即低地址存放数据的高字节。

  无论当前主机是大端机还是小端机,是发送端还是接收端,都要按照这个TCP/IP协议规定的网络字节序来处理数据,如果当前发送主机是小端。则需要先将数据转成大端;否则就忽略,直接发送即可;

  调用以下库函数可以实现网络字节序和主机字节序之间的转换

#include <arpa/inet.h>

uint32_t htonl(uint32_t hostlong);
//将32位的长整数从主机字节序转换位网络字节序
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
  • h表示host,n表示network,l表示32位长整数,s表示16位短整数。
  • 如果主机是小端字节序,则函数内部会转换成大端,如果主机是大端,则函数内部不做转换

socket编程接口


socket常用API

扫描二维码关注公众号,回复: 1096654 查看本文章
//创建 socket 文件描述符(TCP/UDP, 客户端 + 服务器)
//domain: 一个地址描述。目前仅支持AF_INET,,代表sockaddr_in结构体
//type: 服务类型,UDP为SOCK_DGRAM, TCP为SOCK_STREAM
//protocol: 套接口所用的协议。如调用者不想指定,可用0指定,表示缺省。
int socket (int domain, int type, int protocol);

//绑定端口号(TCP/UDP, 服务器)
//客户端不用该接口,而且对于服务器而言,端口号必须是众所周知,不可修改的
int band (int socket, const struct sockaddr *address, socklen_t address_len);

//开始监听 socket(TCP, 服务器)
//作用:将套接字由新创建状态设置为监听状态,即监听是否有客户端请求连接
//backlog: 监听队列,不要设置的太大,5~10就差不多了,避免浪费资源
int listen(int socket, int backlog);

//接受请求(TCP, 服务器)
//与客户端连接成功后,返回一个用于服务的文件描述符,也可以当做是客户端的文件描述符,用于写入和读取数据
//否则进行阻塞式等待,失败返回-1
int accept (int socket, struct sockaddr* address, socklen_t* address_len);

//建立连接(TCP, 客户端)
int connect (int socket,const struct sockaddr* address, socklen_t addrlen);

sockaddr结构

socket常见API中经常出现sockaddr*,下面就为大家详解该结构体的作用以及用法 .

  • sockaddr用于存储参与套接字通信的一个计算机上的IP协议地址。为了统一地址结构的表示方法 ,统一接口函数,使得不同的地址结构可以被bind()、connect()、recvfrom()、sendto()等函数调用。

  • 但一般的编程中并不直接对此数据结构进行操作,而使用另一个与之等价的数据结构sockaddr_in。实际填充字段的每一部分则遵循sockaddr_in数据结构,两者大小都是16字节,所以二者之间可以进行切换。

这里写图片描述

  • sockaddr
struct sockaddr {
  unsigned short sa_family; //:是2字节的地址家族,一般都是“AF_xxx”的形式,通常用AF_INET
  char sa_data[14]; //14字节的地址数据
  }
  • sockaddr_in
struct sockaddr_in {
  short int sin_family; /* Address family */
  unsigned short int sin_port; /* Port number */
  struct in_addr sin_addr; /* Internet address */
  unsigned char sin_zero[8]; /* Same size as struct sockaddr */
  };
  //sin_family:指代协议族,在socket编程中只能是AF_INET
  //sin_port:存储端口号(使用网络字节顺序)
  //sin_addr:存储IP地址,使用in_addr这个数据结构
  //sin_zero:是为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节。
  • sockaddr_in 中的 in_addr
typedef struct in_addr {
  union {
  struct{ unsigned char s_b1,s_b2, s_b3,s_b4;} S_un_b;
  struct{ unsigned short s_w1, s_w2;} S_un_w;
  unsigned long S_addr;
  } S_un;
  } IN_ADDR;

阐述下in_addr的含义,很显然它是一个存储ip地址的共用体有三种表达方式:

第一种用四个字节来表示IP地址的四个数字;
第二种用两个双字节来表示IP地址;
第三种用一个长整型来表示IP地址。

  给in_addr赋值的一种最简单方法是使用inet_addr函数,它可以把一个代表IP地址的字符串赋值转换为in_addr类型,如addrto.sin_addr.s_addr=inet_addr(“192.168.0.2”);
  其反函数是inet_ntoa,可以把一个in_addr类型转换为一个字符串。

猜你喜欢

转载自blog.csdn.net/it_xiaoye/article/details/80432800