「项目复现」Web服务器编程之网络通信模块

[TOC]

​ 本文总结作者学习的Web服务器项目(WebServer),分析其中的网络通信模块的作用及相关知识点

一、socket的作用

​ TCP/IP协议是在操作系统的内核中实现的,因此操作系统需要实现一组系统调用,使得应用程序能够在用户空间访问这些协议提供的服务。实现这组系统调用的API(Application Programming Interface,应用程序编程接口)就是socket和XTI。

​ socket函数库主要提供如下两点功能:

​ 一是实现应用程序数据的send和read。send是将应用程序数据从用户缓冲区中复制到TCP/UDP内核发送缓冲区,以交付内核来发送数据,read是从内核TCP/UDP接收缓冲区中复制数据到用户缓冲区,以读取数据;

​ 二是精细地控制协议栈的通信行为。应用程序可以通过它们来修改内核中各层协议的某些头部信息或其他数据结构,从而精细地控制底层通信的行为。比如可以通过setsockopt函数来设置IP数据报在网络上的存活时间。

二、socket的用法

1、服务端的用法

(1)创建socket:CreateSocket()函数

作用:创建一个监听套接字

1、函数参数:

2、返回值

该函数创建套接字成功则返回一个套接字,失败则返回-1并设置errno。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include<sys/tpyes.h> 
#include<sys/socket.h>

#define PF_INET 2
#define SOCK_STREAM SOCK_STREAM
int m_listenfd;
//创建socket
int CreateSocket(){[
//创建一个监听套接字
m_listenfd = socket(PF_INET, SOCK_STREAM, 0);
if(m_listenfd == -1){
printf(" 创建socket出错: %s(errno:%d)\n" ,strerror(errno),errno);
return -1;
}
else {
printf("创建socket成功\n");
return m_listenfd;
}
}

(2)绑定socket:bind()函数

作用:把一个地址族中的特定socket地址(IP地址+端口号)赋给socket,通常用于服务端程序。

在 Linux 下使用 <sys/socket.h> 头文件中 socket() 函数来创建套接字,原型为:

1
int socket(int af, int type, int protocol);
  1. af 为地址族(Address Family),也就是 IP 地址类型,常用的有 AF_INET和AF_INET6 。AF 是“Address Family”的简写,INET是“Inetnet”的简写。AF_INET 表示 IPv4 地址,例如 127.0.0.1;AF_INET6 表示 IPv6 地址,例如 1030::C9B4:FF12:48AA:1A2B。

struct sockaddr_in结构体用来处理网络通信的地址。

image-20220514231421272

端口

socket地址:IP地址+端口号 211.211.1.2:3600

socket关闭的2种方式:

有close 和shutdown两种API,

close —– 在多进程的情况下,关闭本进程的socket,但这只是socket的引用计数减1,用这个socket的其它进程还能用这个链接,能读或写这个socket,直到所有的进程都进行了close,才真正关闭这个套接字,但当它真正执行关闭的时候是完全关闭,既不处理发送也不处理接收数据

shutdown – 即便在多进程的情况下面,也是直接进行关闭的,关闭了socket 文件描述符,其他进程的也会被关闭,但他关闭的时候只关闭一半,即发送数据通道关闭,接收数据还是可以的。如果对写操作被关闭的socket执行写操作会触发写就绪(EPOLLOUT)事件,同时触发一个SIGPIPE信号。

1、函数参数

int port:端口号

int m_listenfd:套接字

2、返回值

该函数绑定socket地址成功则返回true,失败则返回false并设置errno。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include<sys/tpyes.h> 
#include<sys/socket.h>
//绑定socket地址
bool bind(int port,int m_listenfd){
//(一)检查端口号是否合法
if(port> 65535 || port< 1024) {
printf(" Port:%d不符合要求!",port);
return false;
}

//(二)创建一个IPv4的socket地址
struct sockaddr_in address//socket地址
bzero(&address, sizeof(address));//将该地址清空为0
address.sin__family= AF_INET://设置IPv4的地址族
//inet_ pton(AF_ INET,ip,&address.sin_ addr);//手动设置IP地址
address.sin_addr.s_addr = htonl(INADDR_ ANY);// 自动获取本机的IP地址并且设置
address.sin_port = htons(port);//端口号

//(三)绑定socket地址
int flag= 1;
setsockopt(m_listenfd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));//允许重用本地地址和端口
int ret = bind(m_listenfd, (struct sockaddr *)&address, sizeof(address));//绑定socket地址(IP地址+端口号)

//(四)判断是否绑定成功
if(ret==-1){
printf(" 绑定socket出错: ]%s(errno:%d)\n' ,strerror(errno),errno);
return false;
}
else {
printf("绑定socket成功\n");
return true;
}
}

(3)监听socket

1、函数参数

int backlog

int m_listenfd:套接字

2、返回值

该函数监听socket成功则返回true,失败则返回false并设置errno。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//监听socket
bool Listen(int backlog,int m_listenfd){
//开启监听
int ret = listen(m_listenfd, backlog);
if(ret==-1){
printf(" 监听socket出错: %s(errno:%d)\n",strerror(errno),errno);
return false;
}
else {
printf(" 监听socket成功\n");
return true;
}
}

2、客户端的用法

(1)创建socket

(2)发起连接

三、socket的封装

服务器的socket类

1