在网上做兼职美工有哪些网站,接私活 做网站,哪个网站做推销产品,文昌市住房和城乡建设局网站模型局限性:
1 不使用 非阻塞 同步异步 信号驱动 多路复用 select poll epoll 事件驱动 等技术
2 意在展示最原始的TCP模型
3 代码命名规整清晰,注释详尽,不官方,使用GPT4做了详细检查
实验流程:
服务器:socket-bind-listen-accept-recv-send-clo…模型局限性:
1 不使用 非阻塞 同步异步 信号驱动 多路复用 select poll epoll 事件驱动 等技术
2 意在展示最原始的TCP模型
3 代码命名规整清晰,注释详尽,不官方,使用GPT4做了详细检查
实验流程:
服务器:socket-bind-listen-accept-recv-send-close 共7步
客户端:socket-connect-send-recv-close 共5步
预设共识:
只有一个服务器 可以有多个客户端 实际上后面的各种io模型变种都是基于此共识
实验描述及现象:
当客户端连接上服务器后,会输出已连接字样,并打印收到的数据
然后回给客户端一句话 为了模拟大数据处理 启用了5秒计时
客户端会收到这些数据并打印在自己的终端
这样就模拟了一次CS模型的TCP通信,可以反复运行客户端观察结果
观察阻塞现象:将客户端复制几份 多个控制台同时运行,可以观察到同时只有一个控制台在数54321
注意事项:
1 用于通信的ip地址和端口号要进行大小端转换
2 服务器 accept会返回新的socket用于读写,而客户端一直使用同一个socket
3 如果服务器和客户端都使用whlie(recv)的结构会造成类似死锁的现象,所以必须有一端缓冲区足够大
4 客户端进程的结束取决于服务器的处理速度,当服务器send全部数据后会close socket,客户端会收到0个字节后结束进程
5 服务器ip地址及端口号 已经预设到宏中 方便修改 必须同时修改CS两端
6 在更复杂的应用场景中可能需要引入更高级的通信模型和技术以提高效率和响应性。
#define _GNU_SOURCE
#include stdio.h
#include sys/socket.h
#include sys/un.h
#include arpa/inet.h
#include netinet/in.h
#include stdlib.h
#include string.h
#include unistd.h
// 服务器地址
#define SERVER_IP 192.168.142.132
// 服务器端口
#define SERVER_PORT 55555
int main()
{// server_sockfd用于socket的返回值,client_sockfd用于accept的返回值int server_sockfd, client_sockfd;// 不用struct sockaddr的原因是不能直接设置ip和端口,而后都需要强转,属于历史遗留问题// 这个结构体主要存 ip地址 端口号 地址族struct sockaddr_in server_sockaddr, client_sockaddr;memset(server_sockaddr, 0, sizeof(server_sockaddr));memset(client_sockaddr, 0, sizeof(client_sockaddr));// accept要求填入lensocklen_t client_sockaddr_len sizeof(client_sockaddr);// 表示发送接收的字节ssize_t send_bytes, recv_bytes;// 发送用bufchar send_buf[1024] server say : fine.;// 接受用bufchar recv_buf[1024] {0};// 大小端转换用bufchar ipv4_addr_buf[64] {0};// 新建ipv4 tcp类型socketserver_sockfd socket(AF_INET, SOCK_STREAM, 0);if (server_sockfd -1){perror(socket);exit(EXIT_FAILURE);}// 地址族server_sockaddr.sin_family AF_INET;// ip地址大小端转换存入结构体inet_pton(AF_INET, SERVER_IP, server_sockaddr.sin_addr.s_addr);// 端口大小端转换存入结构体server_sockaddr.sin_port htons(SERVER_PORT);// 绑定到socketif (bind(server_sockfd, (struct sockaddr *)server_sockaddr, sizeof(server_sockaddr)) -1){perror(bind);exit(EXIT_FAILURE);}// 使用ss -tuln命令 可以看见此进程正在监听55555if (listen(server_sockfd, 16) -1){perror(listen);exit(EXIT_FAILURE);}printf(server start...\n);while (1){// 有客户端connect,他就会accept// 函数返回后,代表建立了连接,此时client_sockaddr中会有内容// 函数的返回值是一个用于读写的新socketclient_sockfd accept(server_sockfd, (struct sockaddr *)client_sockaddr, client_sockaddr_len);if (client_sockfd -1){perror(accept);continue;}// 尝试打印出client_sockaddr中的内容,当然要进行字节序转换inet_ntop(AF_INET, client_sockaddr.sin_addr, ipv4_addr_buf, sizeof(ipv4_addr_buf));printf(ipv4_addr:%s port:%d connected\n, ipv4_addr_buf, ntohs(client_sockaddr.sin_port));// 服务器收到的数据,假设缓冲区足够大能一次性读取recv_bytes recv(client_sockfd, recv_buf, sizeof(recv_buf), 0);if (recv_bytes -1){perror(recv);exit(EXIT_FAILURE);}else if (recv_bytes 0){printf(closed by peer\n);continue;}// 打印出来看看printf(%s\n, recv_buf);// 假设有大型数据total字节,一次发不完ssize_t total (ssize_t)strlen(send_buf);// 已发送数量ssize_t sent 0;// 当已发送小于总数就继续发while (total sent){// send_buf sent 指针运算,发完的下次指针就往后挪// total - sent剩余数量send_bytes send(client_sockfd, send_buf sent, total - sent, 0);if (send_bytes -1){perror(send);break;}// 计算已发送字节总数sent send_bytes;}// 模拟大数据处理时间char a[5] 54321;for (size_t i 0; i 5; i){send(client_sockfd, a[i], 1, 0);sleep(1);}// 这个客户端的请求就处理完了close(client_sockfd);}close(server_sockfd);return 0;
}#define _GNU_SOURCE
#include stdio.h
#include sys/socket.h
#include sys/un.h
#include arpa/inet.h
#include netinet/in.h
#include stdlib.h
#include string.h
#include unistd.h// 服务器地址
#define SERVER_IP 192.168.142.132
// 服务器端口
#define SERVER_PORT 55555
int main()
{// 客户端用socketint client_sockfd;// 这个结构体主要存 ip地址 端口号 地址族struct sockaddr_in server_sockaddr, client_sockaddr;memset(server_sockaddr, 0, sizeof(server_sockaddr));memset(client_sockaddr, 0, sizeof(client_sockaddr));// getsockname要求填入lensocklen_t client_sockaddr_len sizeof(client_sockaddr);// 表示发送接收的字节ssize_t send_bytes, recv_bytes;// 发送用bufchar send_buf[1024] {0};// 接受用bufchar recv_buf[1024] {0};// 地址转换用bufchar ipv4_addr_buf[64] {0};// 新建ipv4 tcp类型socketclient_sockfd socket(AF_INET, SOCK_STREAM, 0);if (client_sockfd -1){perror(socket);exit(EXIT_FAILURE);}// 对server_sockaddr进行大小端转换及存入服务器地址信息inet_pton(AF_INET, SERVER_IP, server_sockaddr.sin_addr.s_addr);server_sockaddr.sin_port htons(SERVER_PORT);server_sockaddr.sin_family AF_INET;// 连接服务器if ((connect(client_sockfd, (struct sockaddr *)server_sockaddr, sizeof(server_sockaddr))) -1){perror(connect);exit(EXIT_FAILURE);}// 获取本机地址及端口信息,端口是随机分配的getsockname(client_sockfd, (struct sockaddr *)client_sockaddr, client_sockaddr_len);inet_ntop(AF_INET, client_sockaddr.sin_addr, ipv4_addr_buf, sizeof(ipv4_addr_buf));// 向服务器问好,并告诉是谁给他发的消息snprintf(send_buf, sizeof(send_buf), ipv4_addr:%s port:%u say : how are you ?, ipv4_addr_buf, ntohs(client_sockaddr.sin_port));// 假设这句话是一个很大型的数据// 总字节ssize_t total (ssize_t)strlen(send_buf);// 已发送字节ssize_t sent 0;// 已发送小于总字节就继续发while (total sent){// send_buf sent 指针运算,发完的下次指针就往后挪// total - sent剩余数量send_bytes send(client_sockfd, send_buf sent, total - sent, 0);if (send_bytes -1){perror(send);break;}// 计算已发送字节总数sent send_bytes;}// 为什么客户端可以使用一个阻塞式的while(recv())?// 因为服务端发送完毕会close连接,客户端recv_bytes0 跳出循环while ((recv_bytes recv(client_sockfd, recv_buf, sizeof(recv_buf), 0)) 0){printf(%s\n, recv_buf);memset(recv_buf, 0, sizeof(recv_buf));}if (recv_bytes -1){perror(recv);}else if (recv_bytes 0){printf(closed by peer\n);}// 全剧终close(client_sockfd);return 0;
}