编程模型图:
SYNOPSIS
#include
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
DESCRIPTION
The htonl() function converts the unsigned integer hostlong from host byte order to network byte order.
The htons() function converts the unsigned short integer hostshort from host byte order to network byte order.
The ntohl() function converts the unsigned integer netlong from network byte order to host byte order.
The ntohs() function converts the unsigned short integer netshort from network byte order to host byte order.
On the i386 the host byte order is Least Significant Byte first, whereas the network byte order, as used on the Internet, is Most Significant
Byte first.
结构体具体参数设置刨析:
INADDR_ANY : 本质是一个宏,可以代表本地的任意IP地址,因为服务器可能存在多个网卡, 每个网卡也可能绑定多个ip 地址, 这样设置可以在所有的ip地址上监听, 直到和某个客户端建立了连接时才确定下来到底用哪个ip地址. (说白了 就是 通配 服务器上的任意可能的ip地址, 避免单机 多ip 造成的 ip 设置错误问题)
return val : 成功返回0, 失败返回 -1.
return val : 成功 返回 0 失败返回 -1 且 传入的 sockfd 变成监听套接字.
监听套接字(listenfd) : 全局唯一一个监听套接字在服务器上, 特别注意, 监听套接字全局唯一一个, 一直保持监听状态, (不像后文中的 connfd : connectfd : 连接套接字)
连接套接字 (connfd) : 每建立了一个连接就会从新产生一个新的 connfd。。。。
生活实例理解监听套接字 和连接套接字, 连接套接字也可叫做服务套接字:
比如说一个高档的酒店, 门口一直有一个接车欢迎的服务员, 这个服务员一直在门口进行监听工作, 来了客人就把人家请进去, 进去之后由单独的另外一个服务员对齐进行服务, 这个酒店是比较高档的, 进去之后服务员是一直对齐服务的, 每来一个人都需要一个新的服务套接字(连接套接字)对齐进行服务,直到离开, 至于门口的监听套接字, 是一直不断的监听哪个客户来了, 哪个要建立连接工作.......
代码模型 :
while (1) {
clientaddr_len = sizeof(clientaddr);
connfd = accept(listenfd, (SA*) &clientaddr, &clientaddr_len);
服务逻辑代码
close(connfd); //服务结束关闭连接..
}
return val : 返回值 正是上述的 connfd, 连接套接字, 每实现一个连接 都会返回一个新的connfd, 所以一般accept函数都是放在一个死循环中不停的接收客户端的连接亲求,并且对齐进行服务....
return val : 成功返回0, 失败返回 -1
辅助函数: 后序 需要使用 字符串ip地址 和 sin_addr之间的相互转换:
代码如下:
[tangyujie@VM-4-9-centos Serve]$ cat server.c
#include
#include
#include
#include
#include
#include
#include
#include
#define SERVE_PORT 12345
#define ERR_EXIT(m) \
do { perror(m); exit(EXIT_FAILURE); } while (0)
typedef struct sockaddr SA;
int listenfd; //设置全局监听套接字, 方便关闭
void handle(int signo) {
fprintf(stdout, "ByeBye!\n");
close(listenfd);
exit(EXIT_SUCCESS);
}
int main() {
signal(SIGINT, handle);
int connfd;
struct sockaddr_in serveAdd, clientAdd;
socklen_t clientAdd_len;
char ipbuff[256];
//创建套接字
if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
ERR_EXIT("socket"); //协议家族 服务类型(套接字类型), 协议弃用(0)
}
//确定服务端地址簇
bzero(&serveAdd, sizeof(serveAdd)); //清0
serveAdd.sin_family = AF_INET;
serveAdd.sin_port = htons(SERVE_PORT);
serveAdd.sin_addr.s_addr = htonl(INADDR_ANY); //注意转网络字节序
//bind 端口 地址信息
if (bind(listenfd, (SA*)&serveAdd, sizeof(serveAdd)) == -1) {
ERR_EXIT("bind");
}
//开始监听..
if (listen(listenfd, 3) == -1) {
ERR_EXIT("listen");
}
printf("Accepting connections..\n");
//循环不断的接收客户的连接请求进行服务
while (1) {
clientAdd_len = sizeof(clientAdd);
if ((connfd = accept(listenfd, (SA*)&clientAdd, &clientAdd_len)) == -1) {
ERR_EXIT("accept");
}
printf("recieve connection from ip is %s and port is %d\n",
inet_ntop(AF_INET, &clientAdd.sin_addr, ipbuff, sizeof(ipbuff)),
ntohs(clientAdd.sin_port));
//服务
while (1) {
char buff[1024] = {0};
int i;
int n = read(connfd, buff, sizeof(buff)); //读数据
if (n == -1) {
ERR_EXIT("read");
}
if (n == 0) { //说明客户端主动断开连接
break;
}
//处理数据, 简单的小写字符转大写
for (i = 0; i < n; ++i) {
buff[i] = toupper(buff[i]);
}
//写回
write(connfd, buff, n);
}
fprintf(stdout, "ip %s and port is %d interrupt connfd\n",
ipbuff, ntohs(clientAdd.sin_port));
close(connfd); //服务结束断开连接
}
return 0;
}
代码如下 :
[tangyujie@VM-4-9-centos Serve]$ cat client.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SERVE_PORT 12345 //端口号
#define ERR_EXIT(m)\
do { perror(m); exit(EXIT_FAILURE); } while (0) //错误处理
typedef struct sockaddr SA;
int sockfd; //设置全局, 方便关闭
void handle(int signo) {
fprintf(stdout, "ByeBye!\n");
close(sockfd);
exit(EXIT_SUCCESS);
}
int main(int argc, char* argv[]) {
if (argc != 2) {
fprintf(stderr, "%s ", argv[0]);
close(EXIT_FAILURE);
}
signal(SIGINT, handle);
struct sockaddr_in serveAdd;
//创建套接字
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
ERR_EXIT("socket"); //协议家族 服务类型(套接字类型), 协议弃用(0)
}
//确定服务端地址簇
bzero(&serveAdd, sizeof(serveAdd)); //清0
serveAdd.sin_family = AF_INET;
serveAdd.sin_port = htons(SERVE_PORT);
//将传入的ip字符串转换为 sin_addr
if (inet_pton(AF_INET, argv[1], &serveAdd.sin_addr) == -1) {
ERR_EXIT("inet_ntop");
}
//不需要绑定端口号 系统随机分配一个临时端口号, 直接连接
if (connect(sockfd, (SA*)&serveAdd, sizeof(serveAdd)) == -1) {
ERR_EXIT("connect");
}
while (1) { //死循环, 使用ctrl c信号关闭连接
char buff[1024];
printf("请说>>");
scanf("%s", buff);
write(sockfd, buff, strlen(buff));
int n = read(sockfd, buff, sizeof(buff));
if (n == -1) {
ERR_EXIT("read");
}
buff[n] = 0;
fprintf(stdout, ">>%s\n", buff);
}
}
后序中篇: 解决并发博客链接:https://blog.csdn.net/weixin_53695360/article/details/122790450?spm=1001.2014.3001.5502
通过上述:
我们主要需要掌握网络 ip地址 mac地址的 对比学习 以及含义,
mac 地址出场设定, 全球唯一.
ip 地址基于网络连接, 不同的网段下,或者同一网段下不同时候的连接 ip地址可能不一样. ip地址由 网络号 + 主机号 构成, 可以唯一标识一台主机.....
port端口号, 由于需要区别同一台 主机上面的 不同的 进程, port端口号主要是标识全网类唯一的一个进程
socket连接服务的本质: 是 全网中 为二两个 不同进程 之间 的 基于网络的相互通信.... 这种通信方式叫做 socket通信, 本质是打破同一主机通信的局限, 实现不同主机之间的通信
然后就是最基础网络通信的 TCP 模型了.....
通过上述模型, 存在一定的问题, 我们发现, 只有一个客户进行连接访问服务端的时候是没有问题的, 但是第二个客户端连接请求过来的时候, 竟然没有办法进行服务?????????
这样是不符合我们生活实际需求的, 也就是没办法并发服务多个客户端...这个问题提出来, 如何解决,大家可以在评论区进行讨论