相关接口函数:https://www.cnblogs.com/wanghuaijun/p/6021711.html
不自己写了,在这里面找吧!!! 我这里就写事例代码
一、几种服务器 IO 模型简介
1、阻塞型------->通过多线程或者多进程进行网络通信
2、非阻塞型------------->通过轮询方式进行网络通信
3、多路复用型-------->通过 select 函数设置通信属性实现
4、信号(UDP)
说明:以下事例均实现通信功能,即通过服务器转发来自客户端的信息
二、简单说一下个人对网络通信的理解
对于网络通信当中的 UDP 和 TCP 通信,UDP 通信适用于小规模通信,因为如果每个人的消息发送都是由自己完成,那么将占用大量的客户端资源,当然 UDP 相对TCP 来说比较简单。而 TCP 通信最核心的内容就是转发,将发送的任务交给服务器,信息先通过网络发送给服务器,然后再由服务器转发。这样用户只需要和服务器进行通信,而不是直接与客户端通信,那么由服务器提供通信资源,这样客户端资源就没有那么大的消耗。
三、先写多路复用的事例代码(用的数组实现,你可以尝试使用内核链表实现)
内核链表实现以下功能相关源码自行在该页面下载https://download.csdn.net/download/qq_41985711/10680665
3.1、客户端
// 客户端, 消息发送
#include "myhead.h"
void useage(int argc, char **argv)
{
if (argc != 3)
{
printf("use: %s IP PORT\n", argv[0]);
exit(0);
}
}
int main(int argc, char **argv)
{
useage(argc, argv);
// 一、创建套接字,获取描述符 fd
int fd = socket(AF_INET, SOCK_STREAM, 0);
// 二、设置服务器(也就是接收端)的网络属性
struct sockaddr_in seraddr;
socklen_t serlen = sizeof(seraddr);
bzero(&seraddr, serlen);
seraddr.sin_family = AF_INET;
seraddr.sin_addr.s_addr = inet_addr(argv[1]);
seraddr.sin_port = htons(atoi(argv[2]));
// 三、连接服务器
int ret = connect(fd, (struct sockaddr *)&seraddr, serlen);
if (ret == -1)
{
perror("connect filed: \n");
return -1;
}
char msg[100];
// 四、准备 select 参数
fd_set rset;
while (1)
{
FD_ZERO(&rset);
FD_SET(0, &rset);
FD_SET(fd, &rset);
// select 函数查吧!这里告诉功能
// 监控文件描述符的状态改变
ret = select(fd+1, &rset, NULL, NULL, NULL);
// 五、被监控文件描述符发生状态改变
if (ret > 0)
{
if (FD_ISSET(0, &rset))
{
bzero(msg, 100);
scanf("%s", msg);
ret = send(fd, msg, 100, 0);
printf("sent %d bytes\n", ret);
}
else if (FD_ISSET(fd, &rset))
{
bzero(msg, 100);
ret = recv(fd, msg, 100, 0);
if (ret <= 0)
{
if (ret == 0)
printf("server quit!!!\n");
else
printf("recv failed!!!\n");
close(fd);
return 0;
}
printf("rerv msg %d bytes---->%s\n", ret, msg);
}
}
else if (ret == -1)
{
perror("select filed: \n");
return -1;
}
else if(ret == 0)
{
printf("delay time\n");
return -1;
}
}
close(fd);
return 0;
}
3.2、服务器
// tcp select 广播 接收端
#include "myhead.h"
void useage(int argc, char **argv)
{
if (argc != 2)
{
printf("use: %s <PORT>\n", argv[0]);
exit(0);
}
}
int main(int argc, char **argv)
{
useage(argc, argv);
// 一、创建套接字 socket,获取描述符 fd
int fd = socket(AF_INET, SOCK_STREAM, 0);
// 二、设置服务器网络属性(未绑定)-->(端口号,IP)---->struct sockaddr_in
struct sockaddr_in seraddr;
socklen_t len = sizeof (seraddr);
bzero(&seraddr, len);
seraddr.sin_family = AF_INET;
seraddr.sin_addr.s_addr = htonl(INADDR_ANY);
seraddr.sin_port = htons(atoi(argv[1]));
// 三、绑定服务器属性
bind(fd, (struct sockaddr *)&seraddr, len);
// 四、设置监听模式(最多可同时接收来自 N+4 客户端的信息),
listen(fd, 3);
// 五、等待客户端信息,并保存客户端网络属性(不想保存就写 NULL)
int n = 0;
printf("please input how many ports join?\n");
scanf("%d", &n);
struct sockaddr_in cliaddr[n];
socklen_t clilen = sizeof (struct sockaddr_in);
int connfd[n];
bzero(connfd, sizeof (connfd));
// 六、将连接的客户端的套接字保存
fd_set rset;
char msg[100];
int ret;
int i = 0;
int j = 0;
int maxfd = fd;
int k;
while (1)
{
// 七、将需要监控的套接字加入就绪集合
FD_ZERO(&rset);
FD_SET(fd, &rset);
for (j = 0; j < i; j++)
{
if (connfd[j] == -1)
continue;
FD_SET(connfd[j], &rset);
}
ret = select(maxfd+1, &rset, NULL, NULL, NULL);
if (ret > 0)
{
if (FD_ISSET(fd, &rset))
{
connfd[i] = accept(fd, (struct sockaddr *)&cliaddr[i], &clilen);
if (connfd[i] == -1)
{
perror("accept filed: ");
continue;
}
printf("from client ip: %s port: %hu\n",
inet_ntoa(cliaddr[i].sin_addr), ntohs(cliaddr[i].sin_port));
maxfd = connfd[i] < maxfd ? maxfd : connfd[i];
i++;
continue;
}
for (j = 0; j <= i; j++)
{
if (FD_ISSET(connfd[j], &rset))
{
bzero(msg, 100);
ret = recv(connfd[j], msg, 100, 0);
// 判断是数据还是断开连接
if (ret <= 0)
{
if (ret == 0)
printf("socket %d quit!!!\n", connfd[j]);
else
printf("recv failed!!!\n");
close(connfd[j]);
connfd[j] = -1;
continue;
}
else if (ret > 0)
{
for (k = 0; k < i; k++)
{
if (k == j)
continue;
if (connfd[k] == -1)
continue;
send(connfd[k], msg, 100, 0);
}
}
}
}
}
else if (ret == 0)
{
printf("delay time\n");
return -1;
}
else if (ret == -1)
{
perror("select filed: ");
return -1;
}
}
close(fd);
return 0;
}