socket编程模型

socket网络编程

在TCP/IP协议中,“IP地址+TCP或UDP端口号”唯一标识网络通讯中的一个进程。“IP地址+端口号”就对应一个socket。欲建立连接的两个进程各自有一个socket来标识,那么这两个socket组成的socket pair就唯一标识一个连接。因此可以用Socket来描述网络连接的一对一关系。

  • 一个连接对应两个进程,分别对应客户端和服务器端的socket.

背景知识

大端、小端与网络字节序

内存背景知识:

大端(Big-Endian)

数据的高位更加靠近低地址。以数据0x12345678在内存中的存储为例:

内存地址 0x00001000 0x00001001 0x00001002 0x00001003
存放内容 0x12 0x34 0x56 0x78
小端(Little-Endian)

数据的低位更加靠近低地址。以数据0x12345678在内存中的存储为例:

内存地址 0x00001000 0x00001001 0x00001002 0x00001003
存放内容 0x78 0x56 0x34 0x12
ip地址

以6.7.8.9为例, 其对应的32位无符号整形为0x06070809,正常在内存中存储的顺序(这里以小端存储为例子)应为 0x09 0x08 0x07 0x06, 但是经过inet_pton将点分十进制Ip地址转为无符号整形时采用了网络字节序,所以0x06070809 在内存中的存储顺序变为 0x06 0x07 0x08 0x09, 此时通过printf打印此值,会打成0x09080706

详见

tcp 网络编程模型

基本概念:

  • 网络io: 等价于客户端与服务器建立连接的socket, 可以通过此socket进行读写io操作,从而传递信息
  • 多路复用网络io: 通过多线程等方式实现多个客户端同时访问服务器

服务器端

基本模型如下:

socket(...); //创建socket
bind(...); //绑定ip+端口
listen(...);//监听

while(1)//循环处理客户端的连接请求
{
   
   c_fd  = accept(...);//三次握手建立连接

   while(1)
   {
   
      int nr = read(...);//读取客户端消息
      if(nr==0)
      {
   
         break;
      }
      process(...);//业务处理
      write(...);//发送处理后的数据
   }
   close(c_fd);//关闭连接
}
创建socket

类似于open()打开文件,返回文件描述符, 创建socket网络通讯端口, 返回socket的文件描述符,可以像文件一下read/write在网络上进行收发数据.

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

domain:

  • AF_INET 这是大多数用来产生socket的协议,使用TCP或UDP来传输,用IPv4的地址
  • AF_INET6 与上面类似,不过是来用IPv6的地址。
  • AF_UNIX 本地协议,使用在Unix和Linux系统上,一般都是当客户端和服务器在同一台及其上的时候使用。

type:

  • SOCK_STREAM , 用于TCP可靠传输
  • SOCK_DGRAM, 用于UDP不可靠传输

protocol:

  • 0表示默认协议?
绑定ip+端口
  • 绑定端口的目的:当内核收到 TCP 报文,通过 TCP 头里面的端口号,来找到我们的应用程序,然后把数据传递给我们.
  • 绑定 IP 地址的目的:一台机器是可以有多个网卡的,每个网卡都有对应的 IP 地址,当绑定一个网卡时,内核在收到该网卡上的包,才会发给我们.

将socket与addr ip地址进行绑定, 调用函数:

int bind (int sockfd, const struct sockaddr *addr, socklen_t addrlen);

addr:
存放ip地址+port端口号的结构体

addrlen:
=sizeof(addr), 不是sizeof(struct sockaddr)

监听listen

socket被创建出来的时候都默认是一个主动socket,也就说,内核会认为这个socket之后某个时候会调用connect()主动向别的设备发起连接。这个默认对客户端socket来说很合理,但是监听socket可不行,它只能等着客户端连接自己,因此我们需要调用listen()将监听socket从主动设置为被动,明确告诉内核:你要接受指向这个监听socket的连接请求!

监听socket绑定的ip端口号,是否有连接请求

int listen (int sockfd, int backlog);

backlog:

相当于客户端可以同时连接服务器的个数, 如果超过了怎么办,进入未决队列?

循环处理客户端的连接请求
  1. 三次握手建立连接

服务端进入了监听状态后,通过调用 accept() 函数,来从内核获取客户端的连接,如果没有客户端连接,则会阻塞直到接收到客户的连接请求等待客户端连接的到来。

客户端对server的连接请求会被放入未决连接队列, 服务端通过accept()函数提取队列中的第一个连接请求, 创建并返回一个已连接 Socket, 原始的监听 Socket并不受影响, 未被处理的连接将在队列中排队

int accept (int sockfd, struct sockaddr *addr, socklen_t *addrlen);

addr:
传出参数,返回客户端的地址信息,含IP地址和端口号

返回值:
成功返回一个新的socket文件描述符,用于和客户端通信

  1. 读取客户端消息
read()
  1. 业务处理
process()
  1. 发送处理后的数据
write()

将读取客户端消息,业务处理,发送处理后的数据进行封装:


int recv_send(int c_fd)
{
   
   int nr = read(...);//读取客户端消息
   if(nr==-1)
   {
   
      exit();
   }
   else if(nr 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值