io多路复用(select)代码
#include<myhead.h>
#include <sys/select.h>
#define PORT 8888
#define IP "192.168.250.100"
int main(int argc, char const *argv[])
{ //创建套接字int sfd = socket(AF_INET, SOCK_STREAM, 0);if(sfd == -1){perror("socket error");return -1;}//端口重用int reuse = 1;if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1){perror("setsockopt error");return -1;}printf("设置端口快速重用成功 _%d_ %s_ %s_\n", __LINE__, __FILE__, __func__);struct sockaddr_in sin;sin.sin_family = AF_INET; //表明是ipv4sin.sin_port = htons(PORT); //端口号sin.sin_addr.s_addr = inet_addr(IP); //IP地址//绑定端口if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin))==-1){perror("bind error");return -1;}printf("bind success _%d_ %s_ %s_\n", __LINE__, __FILE__, __func__);//监听模式if(listen(sfd, 128) == -1){perror("listen error");return -1;}printf("listen success _%d_ %s_ %s_\n", __LINE__, __FILE__, __func__);struct sockaddr_in cin; //客户端地址信息结构体cin.sin_family = AF_INET;socklen_t socklen = sizeof(cin); //客户端地址信息的大小//定义文件描述符集合fd_set readfds, tempfds;//清空readfdsFD_ZERO(&readfds);//将套接字和标准输入放入集合FD_SET(sfd,&readfds);FD_SET(0,&readfds);//定义信息容器char buf[128] = "";int res = 0; //接收select的返回值int newfd = -1; //存放用于最新连接客户端的套接字int maxfd = sfd; //定义控制select函数中最大文件描述符struct sockaddr_in saveCin[1024]; //用于存放客户端地址信息结构体
while (1){将集合内容复制一份tempfds = readfds;使用select阻塞等待集合中的文件描述符有事件产生res = select(maxfd+1, &tempfds, NULL, NULL, NULL);if(res == -1){perror("select error");return -1;}else if(res == 0){printf("time out\n");return -1;}for(int i=0; i<=maxfd; i++){if(!FD_ISSET(i, &tempfds)){continue;}if( i == sfd){newfd = accept(sfd, (struct sockaddr*)&cin, &socklen);if(newfd == -1){perror("accept error");return -1;}printf("accept success _%d_ %s_ %s_\n", __LINE__, __FILE__, __func__);//将新的套接字放入select集合 FD_SET(newfd , &readfds);//更新maxfdif(newfd > maxfd){maxfd = newfd;}saveCin[newfd] = cin;}else if(i == 0 ){char buf1[128] = "";bzero(buf, sizeof(buf));fgets(buf, sizeof(buf), stdin); buf[strlen(buf)-1]='\0';printf("终端输入:%s\n", buf);sprintf(buf1, "%s%s", "系统消息:", buf);//将数据发送给所有客户端,群发功能相当于将整个套接字集中的每一可客户端都发一遍for(int j=4; j<=maxfd; j++){send(j, buf1,sizeof(buf1), 0);}}else{//收发数据使用newfd完成通信char buf[128] = "";//清空字符串bzero(buf, sizeof(buf));int ret = recv(i, buf, sizeof(buf), 0); //从套接字中读取客户端发来的消息//判断收到的结果if(ret == 0){printf("客户端已经下线\n"); close(i); //关闭通信的套接字将当前的文件描述符从集合中删除FD_CLR(i, &readfds);更新maxfdfor(int j=maxfd; j>=0; j--){//判断当前的j是否在集合中,如果在,则为maxfdif(FD_ISSET(j, &readfds)){maxfd = j;break;}}continue; //继续判断下一个}else if(ret < 0){perror("recv error");return -1;}printf("[%s:%d]:%s\n", inet_ntoa(saveCin[i].sin_addr), ntohs(saveCin[i].sin_port), buf);send(i, buf, sizeof(buf), 0); } }}return 0;
}
io多路复用(select)结果图
进程代码
#include<myhead.h>
#define PORT 8888
#define IP "192.168.250.100"
//处理客户端请求
int cli_msg(int newfd,struct sockaddr_in cin)
{char buf[128]="";while (1){bzero(buf, sizeof(buf));int res = recv(newfd, buf, sizeof(buf), 0);if (res==0){printf("客户端已经下线\n");break;}else if(res<0){perror("recv error");return -1;}printf("[%s:%d]:%s\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), buf);send(newfd, buf, sizeof(buf), 0); }close(newfd); return 0;}
//回收僵尸进程
void handler(int signo)
{if(signo == SIGCHLD){while(waitpid(-1, NULL, WNOHANG) > 0); }
}int main(int argc, char const *argv[])
{//创建套接字int sfd = socket(AF_INET, SOCK_STREAM, 0);if(sfd == -1){perror("socket error");return -1;}//设置端口重用int reuse = 1;if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1){perror("setsockopt error");return -1;}printf("设置端口快速重用成功 _%d_ %s_ %s_\n", __LINE__, __FILE__, __func__);//设定地址信息结构体struct sockaddr_in sin;sin.sin_family = AF_INET; //表明是ipv4sin.sin_port = htons(PORT); //端口号sin.sin_addr.s_addr = inet_addr(IP); //IP地址//绑定地址if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin))==-1){perror("bind error");return -1;}printf("bind success _%d_ %s_ %s_\n", __LINE__, __FILE__, __func__);//开启监听if(listen(sfd, 128) == -1){perror("listen error");return -1;}printf("listen success _%d_ %s_ %s_\n", __LINE__, __FILE__, __func__);struct sockaddr_in cin; //客户端地址信息结构体cin.sin_family = AF_INET;socklen_t socklen = sizeof(cin); //客户端地址信息的大小pid_t pid;//绑定进程的信号,信号触发就执行handler函数if(signal(SIGCHLD, handler) == SIG_ERR){perror("signal error");return -1;}while (1){//阻塞接收客户端请求int newfd = accept(sfd, (struct sockaddr*)&cin, &socklen);if(newfd == -1){perror("accept error");return -1;}//创建子进程pid = fork();//父进程负责连接if(pid > 0){//父进程不用newfd,回收newfdclose(newfd);}//子进程负责请求响应else if(pid==0){//子进程不用sfd,就回收sfdclose(sfd);cli_msg(newfd, cin);//回收子进程exit(EXIT_SUCCESS);}else{perror("fork error");return -1;}}//关闭所有套接字并关闭监听close(sfd);return 0;
}
进程的结果图
线程代码
#include<myhead.h>
#define PORT 8888
#define IP "192.168.250.100"
//定义存储客户端地址信息结构体和连接请求套接字的结构体
struct msg_info{int newfd;struct sockaddr_in cin;
};//定义线程体
void *task1(void *arg)
{//将外部值传入int newfd=((struct msg_info*)arg)->newfd;struct sockaddr_in cin=((struct msg_info*)arg)->cin;//定义收发容器char buf[128]="";while (1){//响应客户端请求bzero(buf,sizeof(buf));int res=recv(newfd,buf,sizeof(buf),0);if(res==0){printf("客户端已经下线");break; }else if(res>0){printf("[%s:%d]:%s\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), buf);send(newfd,buf,sizeof(buf),0);}else{perror("recv error:");return NULL;}}//关闭连接套接字close(newfd);//退出线程pthread_exit(NULL);
}
int main(int argc, char const *argv[])
{pthread_t tid;int sfd=socket(AF_INET,SOCK_STREAM,0);int reuse = 1;if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1){perror("setsockopt error");return -1;}printf("设置端口快速重用成功 _%d_ %s_ %s_\n", __LINE__, __FILE__, __func__);//绑定的地址信息结构体struct sockaddr_in sin;sin.sin_family = AF_INET; //表明是ipv4sin.sin_port = htons(PORT); //端口号sin.sin_addr.s_addr = inet_addr(IP); //IP地址if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin))==-1){perror("bind error");return -1;}printf("bind success _%d_ %s_ %s_\n", __LINE__, __FILE__, __func__);//将套接字设置成监听状态if(listen(sfd, 128) == -1){perror("listen error");return -1;}printf("listen success _%d_ %s_ %s_\n", __LINE__, __FILE__, __func__);struct sockaddr_in cin; cin.sin_family = AF_INET;socklen_t socklen = sizeof(cin); while(1){//阻塞接收客户端的链接请求,并且获取客户端的地址信息int newfd = accept(sfd, (struct sockaddr*)&cin, &socklen);if(newfd == -1){perror("accept error");return -1;}printf("accept success _%d_ %s_ %s_\n", __LINE__, __FILE__, __func__);//定义用于向线程体传参的结构体变量struct msg_info info = {newfd, cin};//线程创建,并向线程体传参的结构体pthread_create(&tid,NULL,task1,&info);//线程分离if(pthread_detach(tid) != 0){printf("分离失败\n");return -1;}}close(sfd);return 0;
}
线程结果图