是理想化的并没有完全实现的模型。
应用层
提供响应的应用服务
表示层
数据的表示和加密
会话层
建立会话关系
传输层
通过传输协议传输数据
网络层
实现跨子网通信,路由转发,维护路由表。
数据链路层
实现以太网内数据帧的转发
物理层
按照一定的传输规则传输电信号。
详细介绍见https://blog.csdn.net/Stars____/article/details/108694074
SCTP协议:TCP的升级版
unix域套接字:用于本地进程间的通信。
Socket:是一个特殊的文件描述符。是一种通用的网络编程的接口。
IP地址
端口
字节序
struct sockaddr{
sa_family_t sa_family; //协议
char sa_data[14];
};
struct sockaddr_in{
sa_family_t sin_family; // 协议
in_port_t sin_port; // 端口号的网络字节序
struct in_addr sin_addr;//
};
struct in_addr{
uint32_t s_addr; // IP地址 网络字节序
};
#include "net.h"
#define BUFF_SIZE 128
int main(int argc, char* argv[])
{
int listenfd, connfd;
struct sockaddr_in servaddr, cliaddr;
socklen_t peerlen;
char buf[BUFF_SIZE];
if(argc < 3){
printf("入参有误!\n");
return -1;
}
// 建立socket连接
if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
perror("socket");
return -1;
}
printf("listenfd = %d\n", listenfd);
// 设置sockaddr_in 结构体中的相关参数
bzero(&servaddr, sizeof(servaddr)); // 擦除内存 全写0
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(atoi(argv[2]));
servaddr.sin_addr.s_addr = inet_addr(argv[1]);
// 绑定函数
if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0){
perror("bind");
return -1;
}
printf("bind success!\n");
// 调用listen()函数,设置监听模式
if(listen(listenfd, 10) == -1){
perror("listen");
return -1;
}
printf("listening...\n");
// 调用accept函数,等待客户端的连接
peerlen = sizeof(cliaddr);
while(1){
if((connfd = accept(listenfd,(struct sockaddr*)&cliaddr, &peerlen)) < 0){
perror("accept");
return -1;
}
bzero(buf, sizeof(buf));
if(recv(connfd, buf, BUFF_SIZE, 0) == -1){
perror("recv");
return -1;
}
printf("Received a message: %s\n", buf);
strcpy(buf, "Welcome to server");
send(connfd, buf, BUFF_SIZE, 0);
close(connfd);
}
close(listenfd);
return 0;
}
#include "net.h"
#define BUFF_SIZE 128
int main(int argc, char* argv[])
{
int sockfd;
char buf[BUFF_SIZE] = "Hello server";
struct sockaddr_in servaddr;
if(argc < 3){
printf("入参有误!\n");
return -1;
}
// 创建socket
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
perror("socket");
return -1;
}
// 设置sockaddr_in结构体中相关参数
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(atoi(argv[2]));
servaddr.sin_addr.s_addr = inet_addr(argv[1]);
// 调用connect函数向服务器发送连接请求
if(connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){
perror("connect");
return -1;
}
// 发送消息给服务端
send(sockfd, buf, sizeof(buf), 0);
if(recv(sockfd, buf, BUFF_SIZE, 0) == -1){
perror("recv");
return -1;
}
printf("recv from server : %s\n", buf);
close(sockfd);
return 0;
}
主要还是对服务器端的优化。
在上述并发服务器中使用多线程能满足多个客户端的连接,但是客户端的数量上不能太多,假设有1000个客户端要连接这个服务器,那服务器就得开1000个线程,而且并不是每个客户端都是活跃的,对于那些不活跃的客户端服务器也不能释放线程资源,直到客户端断开连接,这样对服务器来说压力太大。
所以就有了IO多路复用。
多路复用IO
下一篇详细讲!