IO网编

IO

第一次作业

使用fgets统计给定文件的行数

#include<myhead.h>
#include<sqlite3.h>
int main(int argc, const char *argv[])
{//以只读形式打开文件FILE *fp = NULL;if((fp = fopen("./03fgets记录行数.c","r")) == NULL){perror("fopen error");return -1;}int num = 0; //记录行数char buf[5]="";char *ptr = NULL;while((ptr = fgets(buf,sizeof(buf),fp)) != NULL) //没读完就一直读,将读取到字符串放入buf中{if(buf[strlen(buf)-1] == '\n')//判断数组中倒数第二个字符是否为换行{num++;}}printf("当前文件的行数为:[%d]\n",num);return 0;
}

使用fputs和fgets完成两个文件的拷贝

#include<myhead.h>
#include<sqlite3.h>
int main(int argc, const char *argv[])
{FILE *fp = NULL,*fq = NULL;//以只读形式打开文件if((fp = fopen("./001.txt","r")) == NULL){perror("fopen error");return -1;}//以只写形式打开文件if((fq = fopen("./002.txt","w")) == NULL){perror("fopen error");return -1;}char buf[5]="";char *ptr = NULL;while((ptr = fgets(buf,sizeof(buf),fp)) != NULL)//从fp中读取数据放入buf中{fputs(buf,fq); //将数据从buf中写入到fq}fclose(fp);fclose(fq);return 0;
}

完成注册登录功能

#include<myhead.h>
#include<sqlite3.h>
//展示界面
void interface()
{printf("0退出\n");printf("1注册\n");printf("2登录\n");
}//注册
int zhuce()
{//打开以追加形式文件FILE *fp = NULL;if((fp = fopen("./passworld.txt","a")) == NULL){perror("fopen error");return -1;}char zczh[128] = "";char zcmm[128] = "";//从终端获取注册的账号和密码printf("请输入注册账号:");fscanf(stdin,"%s",zczh);printf("请输入注册密码:");fscanf(stdin,"%s",zcmm);//将账号和密码格式化输入到文件中fprintf(fp,"%s %s\n",zczh,zcmm);return 0;fclose(fp);
}//登录
int denglu()
{//打开以只读形式文件FILE *fp = NULL;if((fp = fopen("./passworld.txt","r")) == NULL){perror("fopen error");return -1;}char dlzh[128] = "";char dlmm[128] = "";char zh[128] = "";char mm[128] = "";//从终端获取注册的账号和密码printf("请输入登录账号:");fscanf(stdin,"%s",dlzh);printf("请输入登录密码:");fscanf(stdin,"%s",dlmm);//从文件中获取账号和密码int res;while((res = fscanf(fp,"%s %s",zh,mm)) > 0){if(strcmp(dlzh,zh) ==0 && strcmp(dlmm,mm) == 0) //判断登录账号和密码,是否与文件中的一致{printf("登录成功!\n");return 0;}}printf("账号或密码输入错误!\n");fclose(fp);return -1;
}
int main(int argc, const char *argv[])
{while(1){interface();int num;printf("请输入选项:");scanf("%d",&num);switch (num){case 0:exit(0);//结束进程case 1:zhuce();//调用注册函数break;case 2:denglu();//调用登录函数break;default:printf("输入的功能选项错误,请重新输入!\n");break;}while(getchar() != '\n');printf("按任意键继续……\n");getchar();system("clear");}return 0;
}

第二次作业

使用fread和fwrite完成两个文件的拷贝

#include<myhead.h> // 引入自定义头文件
//创建一个结构体用于存储文件标识符
typedef struct fp
{FILE *fp1; // 定义文件指针fp1,用于指向源文件FILE *fp2; // 定义文件指针fp2,用于指向目标文件
}fp,*fpptr; // 定义结构体fp和结构体指针fpptr//打开文件
fp fileopen(int argc, const char *argv[])
{fp fd0; // 定义结构体变量fd0,用于存储打开的文件指针//因为函数有返回值,而返回值是一个结构体,这里创建一个新的结构体变量用于返回出错的了情况fp fderr = {NULL,NULL}; // 定义结构体变量fderr,初始化文件指针为NULL,用于错误处理//判断是否输入两个文件if(argc != 3){printf("输入的文件数量不正确!\n"); // 如果输入的文件数量不为2,打印错误信息return fderr; // 返回错误结构体fderr}//以只读形式打开源文件if((fd0.fp1 = fopen(argv[1],"r")) == NULL){perror("fopen error"); // 如果打开源文件失败,打印错误信息return fderr; // 返回错误结构体fderr}//以只写形式打开目标文件if((fd0.fp2 = fopen(argv[2],"w")) == NULL){perror("fopen error"); // 如果打开目标文件失败,打印错误信息return fderr; // 返回错误结构体fderr}return fd0; // 返回成功打开的文件指针结构体
}//关闭文件
void fileclose(fp fd0)
{fclose(fd0.fp1); // 关闭源文件fclose(fd0.fp2); // 关闭目标文件
}//获取文件长度
int fileline(fp fd)
{int line; // 定义变量line,用于存储文件长度//将光标移到文件末尾,获取文件大小line = fseek(fd.fp1,0,SEEK_END); // 将源文件的读取指针移动到文件末尾//因为不关闭文件,所以要将光标位置复原fseek(fd.fp1,0,SEEK_SET); // 将源文件的读取指针重新定位到文件开头return line; // 返回文件长度
}//拷贝文件
void filecopy(fp fd0)
{//定义一个搬运工char buf[128] = ""; // 定义缓冲区buf,用于临时存储读取的数据int res; // 定义变量res,用于存储每次读取的字节数//循环从源文件读取数据,如果文件没读完并且没读到指定大小则继续循环while(!feof(fd0.fp1)) // 判断文件是否读到末尾{memset(buf,0,sizeof(buf)); // 清空缓冲区bufres = fread(buf,1,sizeof(buf),fd0.fp1); // 从源文件中读取数据到缓冲区buf//将数据写入目标文件fwrite(buf,1,res,fd0.fp2); // 将缓冲区buf的数据写入目标文件}//调用【关闭文件】函数fileclose(fd0);  // 关闭文件
}
int main(int argc, const char *argv[])
{fp fd0; // 定义结构体变量fd0,用于存储打开的文件指针//调用【打开文件】函数fd0 = fileopen(argc,argv); // 打开文件,并将文件指针存储到fd0if(fd0.fp1 == NULL){return -1; // 如果源文件打开失败,返回-1}//创建一个变量接收文件长度,调用【获取文件长度】函数int lines = fileline(fd0); // 获取文件长度,并存储到变量lines//调用【拷贝文件】函数filecopy(fd0); // 拷贝文件return 0; // 程序执行成功,返回0
}

使用read、write完成两个文件的拷贝

#include<myhead.h>
//创建一个结构体用于存储文件标识符
typedef struct fd
{int fd1;int fd2;
}fd,*fdptr;//打开文件
fd fileopen(int argc, const char *argv[])
{fd fd0;//因为函数有返回值,而返回值是一个结构体,这里创建一个新的结构体变量用于返回出错的了情况fd fderr = {-1,-1};//判断是否输入两个文件if(argc != 3){printf("输入的文件数量不正确!\n");return fderr;}//以只读形式打开源文件if((fd0.fd1 = open(argv[1],O_RDONLY)) == -1){perror("open error");return fderr;}//以只写形式打开目标文件if((fd0.fd2 = open(argv[2],O_WRONLY|O_CREAT|O_TRUNC,0664)) == -1){perror("open error");return fderr;}return fd0;
}//关闭文件
void fileclose(fd fd0)
{close(fd0.fd1);close(fd0.fd2);
}//获取文件长度
int fileline(int fd)
{int line;//将光标移到文件末尾,获取文件大小line = lseek(fd,0,SEEK_END);//因为不关闭文件,所以要将光标位置复原lseek(fd,0,SEEK_SET);return line;
}//拷贝文件
void filecopy(fd fd0,int stat,int end)
{//定义一个搬运工char buf[128] = "";//帮助判断循环是否继续执行int res = 1;int now_end = 0; //已经读了多少//从什么地方开始拷贝lseek(fd0.fd1,stat,SEEK_SET);lseek(fd0.fd2,stat,SEEK_SET);//循环从源文件读取数据,如果文件没读完并且没读到指定大小则继续循环while((res > 0) && now_end <= end){memset(buf,0,sizeof(buf));res = read(fd0.fd1,buf,sizeof(buf));//将数据写入目标文件write(fd0.fd2,buf,res);//当前光标所在的位置now_end = lseek(fd0.fd1,0,SEEK_CUR);}//调用【关闭文件】函数fileclose(fd0); 
}
int main(int argc, const char *argv[])
{fd fd0;//调用【打开文件】函数fd0 = fileopen(argc,argv);//创建一个变量接收文件长度,调用【获取文件长度】函数int lines = fileline(fd0.fd1);//调用【拷贝文件】函数filecopy(fd0,0,lines);return 0;
}

将时间在文件中跑起来

#include<myhead.h>
#include<sqlite3.h>
//获取文件行号
int filelens()
{//以只读形式打开文件FILE *fp = NULL;if((fp = fopen("./time.txt","r")) == NULL){perror("fopen error");return -1;}int num = 0; //记录行数char buf[5]="";char *ptr = NULL;while((ptr = fgets(buf,sizeof(buf),fp)) != NULL) //没读完就一直读,将读取到字符串放入buf中{if(buf[strlen(buf)-1] == '\n')//判断数组中倒数第二个字符是否为换行{num++;}}fclose(fp);return num;
}//获取系统时间
char *systime()
{static char buf[128] = "";//获取系统时间(秒数)time_t sTime = time(NULL);//将系统时间(秒数)转换成时间结构体struct tm *t = localtime(&sTime);//将时分秒格式化放入字符串数组中,并加上序号snprintf(buf,sizeof(buf),"%d、%02d:%02d:%02d\n",filelens()+1,t->tm_hour,t->tm_min,t->tm_sec);return buf;
}//系统时间写入
int intime()
{FILE *fp = NULL;//以追加的形式打开文件if((fp = fopen ("./time.txt","a")) == NULL){perror("fopen error");return -1;}while(1){//调用【系统时间写入函数】将字符串写入文件fprintf(fp,"%s",systime());fflush(fp);sleep(1);}fclose(fp);return 0;
}
int main(int argc, const char *argv[])
{intime();return 0;
}

第三次作业

使用多进程完成两个文件的拷贝,父进程拷贝前一半,子进程拷贝后一半,父进程回收子进程的资源

#include<myhead.h>
//创建一个结构体用于存储文件标识符
typedef struct fd
{int fd1;int fd2;
}fd,*fdptr;//打开文件
fd fileopen(int argc, const char *argv[])
{fd fd0;//因为函数有返回值,而返回值是一个结构体,这里创建一个新的结构体变量用于返回出错的了情况fd fderr = {-1,-1};//判断是否输入两个文件if(argc != 3){printf("输入的文件数量不正确!\n");return fderr;}//以只读形式打开源文件if((fd0.fd1 = open(argv[1],O_RDONLY)) == -1){perror("open error");return fderr;}//以只写形式打开目标文件if((fd0.fd2 = open(argv[2],O_WRONLY|O_CREAT|O_TRUNC,0664)) == -1){perror("open error");return fderr;}return fd0;
}//关闭文件
void fileclose(fd fd0)
{close(fd0.fd1);close(fd0.fd2);
}//获取文件长度
int fileline(int fd)
{int line;//将光标移到文件末尾,获取文件大小line = lseek(fd,0,SEEK_END);//因为不关闭文件,所以要将光标位置复原lseek(fd,0,SEEK_SET);return line;
}//拷贝文件
void filecopy(fd fd0,int stat,int end)
{//定义一个搬运工char buf[128] = "";//帮助判断循环是否继续执行int res = 1;int now_end = 0; //已经读了多少//从什么地方开始拷贝lseek(fd0.fd1,stat,SEEK_SET);lseek(fd0.fd2,stat,SEEK_SET);//循环从源文件读取数据,如果文件没读完并且没读到指定大小则继续循环while((res > 0) && now_end <= end){memset(buf,0,sizeof(buf));res = read(fd0.fd1,buf,sizeof(buf));//将数据写入目标文件write(fd0.fd2,buf,res);//当前光标所在的位置now_end = lseek(fd0.fd1,0,SEEK_CUR);}//调用【关闭文件】函数fileclose(fd0); 
}
int main(int argc, const char *argv[])
{fd fd0;//调用【打开文件】函数fd0 = fileopen(argc,argv);//创建一个变量接收文件长度,调用【获取文件长度】函数int lines = fileline(fd0.fd1);//printf("%d\n",lines);//创建一个用于接收进程号的变量pid_t pid;//创建子进程if((pid = fork()) < 0){perror("fork error");return -1;}else if(pid > 0){//父进程//调用【拷贝文件】函数和【获取文件长度】函数filecopy(fd0,0,lines/2);//等待回收子进程资源waitpid(pid,NULL,0);}else{//子进程//调用【拷贝文件】函数和【获取文件长度】函数filecopy(fd0,lines/2,lines);//结束进程exit(EXIT_SUCCESS);}return 0;
}

第四次作业

使用多线程完成两个文件的拷贝,第一个线程拷贝前一半,第二个线程拷贝后一半,主线程回收两个线程的资源

#include<myhead.h>
#include<sqlite3.h>
//创建一个结构体用于存储文件标识符
typedef struct fd
{int fd1;int fd2;
}fd,*fdptr;//打开文件
fd fileopen(int argc, const char *argv[])
{fd fd0;//因为函数有返回值,而返回值是一个结构体,这里创建一个新的结构体变量用于返回出错的了情况fd fderr = {-1,-1};//判断是否输入两个文件if(argc != 3){printf("输入的文件数量不正确!\n");return fderr;}//以只读形式打开源文件if((fd0.fd1 = open(argv[1],O_RDONLY)) == -1){perror("open error");return fderr;}//以只写形式打开目标文件if((fd0.fd2 = open(argv[2],O_WRONLY|O_CREAT|O_TRUNC,0664)) == -1){perror("open error");return fderr;}return fd0;
}//关闭文件
void fileclose(fd fd0)
{close(fd0.fd1);close(fd0.fd2);
}//获取文件长度
int fileline(int fd)
{int line;//将光标移到文件末尾,获取文件大小line = lseek(fd,0,SEEK_END);//因为不关闭文件,所以要将光标位置复原lseek(fd,0,SEEK_SET);return line;
}//拷贝文件
void filecopy(fd fd0,int stat,int end)
{//定义一个搬运工char buf[128] = "";//帮助判断循环是否继续执行int res = 1;int now_end = 0; //已经读了多少//从什么地方开始拷贝lseek(fd0.fd1,stat,SEEK_SET);lseek(fd0.fd2,stat,SEEK_SET);//循环从源文件读取数据,如果文件没读完并且没读到指定大小则继续循环while((res > 0) && now_end <= end){memset(buf,0,sizeof(buf));res = read(fd0.fd1,buf,sizeof(buf));//将数据写入目标文件write(fd0.fd2,buf,res);//当前光标所在的位置now_end = lseek(fd0.fd1,0,SEEK_CUR);}
}//定义一个线程体函数
void *task1(void *arg)
{//解压万能指针fd fd0 = *(fdptr)arg;//调用【获取文件长度】函数int lines = fileline(fd0.fd1);//调用【拷贝文件】函数filecopy(fd0,lines/3,lines*2/3);//退出线程pthread_exit(NULL);
}//定义一个线程体函数
void *task2(void *arg)
{//解压万能指针fd fd0 = *(fdptr)arg;//调用【获取文件长度】函数int lines = fileline(fd0.fd1);//调用【拷贝文件】函数filecopy(fd0,lines*2/3,lines);//退出线程pthread_exit(NULL);
}
int main(int argc, const char *argv[])
{//调用【打开文件】函数fd fd0 = fileopen(argc,argv);//定义一个线程号变量pthread_t tid1,tid2;//创建一个线程if(pthread_create(&tid1,NULL,task1,&fd0)){printf("pthread_create error\n");return -1;}//创建一个线程if(pthread_create(&tid2,NULL,task2,&fd0)){printf("pthread_create error\n");return -1;}//调用【获取文件长度】函数int lines = fileline(fd0.fd1);//调用【拷贝文件】函数filecopy(fd0,0,lines/3);//设置线程分离态pthread_detach(tid1);pthread_detach(tid2);printf("输入空格结束拷贝(请等待几秒):");getchar();//调用【关闭文件】函数fileclose(fd0); return 0;
}

第五次作业

使用有名管道完成两个进程的相互通信

read

#include<myhead.h>
#include<sqlite3.h>
int main(int argc, const char *argv[])
{//创建子进程pid_t pid;if((pid = fork()) < 0){ERR_PRIN("fork");return -1;}else if(pid == 0){//这是子进程int fd;if((fd = open("./write",O_RDONLY)) == -1){ERR_PRIN("open");return -1;}//创建搬运工char buf[128] = "";while(1){//清空数组memset(buf,0,sizeof(buf));//从管道文件中读取数据read(fd,buf,sizeof(buf));if(!strcmp(buf,"quit")){break;}printf("write:%s\n",buf);}//关闭文件close(fd);//退出进程exit(EXIT_SUCCESS);}else{//这是父进程int fd;if((fd = open("./read",O_WRONLY)) == -1){ERR_PRIN("open");return -1;}//创建搬运工char buf[128] = "";while(1){//清空数组memset(buf,0,sizeof(buf));//从终端读取数据fgets(buf,sizeof(buf),stdin);buf[strlen(buf)-1] = 0;//向管道中写入数据write(fd,buf,sizeof(buf));if(!strcmp(buf,"quit")){//杀死杀死子进程kill(pid,SIGKILL);break;}}//关闭文件close(fd);//等待回收子进程资源wait(NULL);}return 0;
}

write

#include<myhead.h>
#include<sqlite3.h>
int main(int argc, const char *argv[])
{//创建子进程pid_t pid;if((pid = fork()) < 0){ERR_PRIN("fork");return -1;}else if(pid == 0){//这是子进程//创建管道文件mkfifo("./read",0664);int fd;if((fd = open("./read",O_RDONLY)) == -1){ERR_PRIN("open");return -1;}//创建搬运工char buf[128] = "";while(1){//清空数组memset(buf,0,sizeof(buf));//从管道文件中读取数据read(fd,buf,sizeof(buf));if(!strcmp(buf,"quit")){//杀死杀死父进程kill(getppid(),SIGKILL);break;}printf("read:%s\n",buf);}//关闭文件close(fd);//退出进程exit(EXIT_SUCCESS);}else{//这是父进程//创建管道文件mkfifo("./write",0664);int fd;if((fd = open("./write",O_WRONLY)) == -1){ERR_PRIN("open");return -1;}//创建搬运工char buf[128] = "";while(1){//清空数组memset(buf,0,sizeof(buf));//从终端读取数据fgets(buf,sizeof(buf),stdin);buf[strlen(buf)-1] = 0;//向管道中写入数据write(fd,buf,sizeof(buf));if(!strcmp(buf,"quit")){//杀死杀死子进程kill(pid,SIGKILL);break;}}//关闭文件close(fd);//等待回收子进程资源wait(NULL);}//删除文件system("rm read");system("rm write");return 0;
}

第六次作业

使用消息队列完成两个进程间相互通信

#include<myhead.h>
#include<sqlite3.h>
//创建消息结构体
typedef struct 
{long lei;            //消息类型char data[1024];    //正文
}Msg_ds;
//求取正文大小
#define SIZE sizeof(Msg_ds) - sizeof(long)
int main(int argc, const char *argv[])
{//创建键值key_t key = ftok("./",'b');//判断键值是否创建成功if(key == -1){ERR_PERR("ftok error");return -1;}//创建消息队列int msgid;if((msgid = msgget(key,IPC_CREAT|0664)) == -1){ERR_PERR("msgget error");return -1;}//创建子进程pid_t pid;if((pid = fork()) < 0){ERR_PERR("fork error");return -1;}else if(pid == 0){//这是子进程while(1){Msg_ds msg;//接收消息if(msgrcv(msgid,&msg,SIZE,0,0) == -1){ERR_PERR("msgrcv error");return -1;}if(strcmp(msg.data,"quit") == 0){break;}//向终端输出printf("%d = %s\n",getpid(),msg.data);}//退出子进程exit(EXIT_SUCCESS);}else{//这是父进程while(1){Msg_ds msg = {.lei = 100};//从终端获取要发送的消息fgets(msg.data,sizeof(msg.data),stdin);//将换行符转换成0if(msg.data[strlen(msg.data)-1] == '\n'){msg.data[strlen(msg.data)-1] = 0;}//发送消息if(msgsnd(msgid,&msg,SIZE,0) == -1){ERR_PERR("msgsnd error");return -1;}if(strcmp(msg.data,"quit") == 0){break;}}//等待回收子进程资源waitpid(pid,NULL,0);//销毁消息队列if(msgctl(msgid,IPC_RMID,NULL) == -1){ERR_PERR("msgctl error");return -1;}}return 0;
}

网编

第三次作业

使用select实现tcp的服务器端,poll实现tcp的客户端

select

服务器

#include<myhead.h>
#define PORT 8888   //端口号1024~49151
#define IP "192.168.250.100" //本机IP
int main(int argc, char const *argv[])
{//创建socket套接字int sfd = socket(AF_INET,SOCK_STREAM,0);if(sfd < 0){ERR_PERR("socket error");return -1;}//允许端口快速的被复用int reuse = 1;if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0){ERR_PERR("setsockopt");return -1;}//填充信息结构体struct sockaddr_in sin;sin.sin_family = AF_INET;   sin.sin_port = htons(PORT); sin.sin_addr.s_addr = inet_addr(IP); //绑定if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin)) < 0){ERR_PERR("bind error");return -1;}//设置监听套接字if(listen(sfd,128) < 0){ERR_PERR("listen error");return -1;}int newfd = -1;struct sockaddr_in cins[1024];// 客户端地址数组char buf[128] = "";fd_set readfds,tempfds; //创建读集合,临时集合FD_ZERO(&readfds); // 清空读集合FD_SET(0,&readfds);FD_SET(sfd,&readfds); //将终端和监听套接字的文件描述符放进读集合中int maxfd = sfd; // 给maxfd赋值while (1){tempfds = readfds; //拷贝一份读集合int res = select(maxfd+1,&tempfds,NULL,NULL,NULL); //阻塞等待集合中的事件发生if (res == -1){ERR_PERR("select");return -1;}else if (res == 0){ERR_PRIN("超出时间");return -1;}for (int i = 0; i <= maxfd; i++){if (!FD_ISSET(i,&tempfds)){continue;}if (i == 0){// 键盘输入事件scanf("%s",buf);printf("键盘输入事件:%s\n",buf);}else if (i == sfd){// 客户端链接事件struct sockaddr_in cin;// 客户端地址socklen_t len = sizeof(cin); //信息结构体大小if ((newfd = accept(i,(struct sockaddr*)&cin,&len)) == -1){ERR_PERR("accept");return -1;}cins[newfd] = cin;// 将新地址放入数组printf("newfd = %d, [%s:%d]链接成功!\n",newfd,inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));FD_SET(newfd,&readfds); // 将客户端文件描述符加入读集合里maxfd = newfd > maxfd ? newfd : maxfd;// 更新最大文件描述符}else{if (FD_ISSET(i,&tempfds)){memset(buf,0,sizeof(buf));int res = -1;if ((res = recv(i,buf,sizeof(buf),0)) == -1) //读取客户端传来的消息{ERR_PERR("recv");return -1;}else if (res == 0){printf("i = %d 客户端下线!\n",i);close(i);maxfd = i < maxfd ? maxfd : maxfd-1;continue;}printf("i = %d [%s:%d]%s\n",i,inet_ntoa(cins[i].sin_addr),ntohs(cins[i].sin_port),buf);strcat(buf,"已阅");send(i,buf,sizeof(buf),0);}   }   }}close(sfd);return 0;
}

客户端

#include<myhead.h>
#define PORT 8888   //端口号1024~49151
#define IP "192.168.250.100" //本机IP
int main(int argc, char const *argv[])
{int sfd = socket(AF_INET,SOCK_STREAM,0); //创建套接字if (sfd == -1){ERR_PERR("socket");return -1;}//填充信息结构体struct sockaddr_in sin;sin.sin_family = AF_INET;   sin.sin_port =     htons(PORT);   sin.sin_addr.s_addr = inet_addr(IP);  if(connect(sfd, (struct sockaddr*)&sin, sizeof(sin))==-1) //链接服务器{perror("connect error");return -1;}fd_set readfds,tempfds;//创建读集合,临时集合FD_ZERO(&readfds); // 清空读集合FD_SET(0,&readfds);FD_SET(sfd,&readfds); //将终端和服务器套接字的文件描述符放进读集合中char buf[128] = "";while (1){tempfds = readfds;int res = select(sfd+1,&tempfds,NULL,NULL,NULL);//阻塞等待集合中的事件发生if (res == -1){ERR_PERR("select");return -1;}else if (res == 0){ERR_PRIN("超出时间");return -1;}if (FD_ISSET(0,&tempfds)) //从终端获取{memset(buf,0,sizeof(buf));scanf("%s",buf);send(sfd,buf,strlen(buf),0);}if (FD_ISSET(sfd,&tempfds)) //接收服务器消息{memset(buf,0,sizeof(buf));int res = recv(sfd,buf,sizeof(buf),0);if (res == 0){break;}printf("%s\n",buf);}}close(sfd);return 0;
}
poll

服务器

#include<myhead.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){ERR_PERR("socket");return -1;}int reuse = 1;if (setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse)) == -1) //端口快速复用{ ERR_PERR("setsockopt");return -1;}struct sockaddr_in sin; //填充服务器地址信息结构体sin.sin_family = AF_INET;sin.sin_port = htons(PORT);sin.sin_addr.s_addr = inet_addr(IP);if (bind(sfd,(struct sockaddr*)&sin,sizeof(sin)) == -1) //绑定服务器地址信息{ERR_PERR("bind");return -1;}if (listen(sfd,128) == -1) //设置监听套接字{ERR_PERR("listen");return -1;}char buf[128] = "";int maxfd = sfd; //设置最大文件描述符struct pollfd fds[1024]; //创建poll监测集合for (int i = 0; i < 1024; i++){fds[i].fd = -1;}fds[0].fd = 0;fds[0].events = POLLIN;fds[sfd].fd = sfd;fds[sfd].events = POLLIN; //将0和sfd文件描述符设置监测读事件int newfd = -1; //用于接收链接的客户端struct sockaddr_in cins[1024]; //客户端地址结构体数组while (1){int ren;if((ren = poll(fds,1024,-1)) == -1){ERR_PERR("poll");return -1;}else if (ren == 0){ERR_PRIN("超时\n");return -1;}for (int i = 0; i <= maxfd; i++){//链接事件if (fds[i].revents == POLLIN && i == sfd) //如果真的发生了读事件{struct sockaddr_in cin; //客户端地质结构体socklen_t len = sizeof(cin); //结构体大小if ((newfd = accept(sfd,(struct sockaddr*)&cin,&len)) == -1) //链接客户端{ERR_PERR("accept");return -1;}printf("i = %d [%s:%d]链接成功!\n",i,inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));cins[newfd] = cin; //放入数组中fds[newfd].fd = newfd;    fds[newfd].events = POLLIN; //设置监测读事件maxfd = newfd > maxfd ? newfd : maxfd; //更新最大描述符printf("newfd = %d maxfd = %d\n",newfd,maxfd);}//键盘事件else if (fds[i].revents == POLLIN && i == 0) //如果真的发生了读事件{scanf("%s",buf);printf("i = %d\n",i);}//交互事件else if (fds[i].revents == POLLIN && i > 3) //如果真的发生了读事件{memset(buf,0,sizeof(buf));int res = -1;if ((res = recv(i,buf,sizeof(buf),0)) == -1) //读取客户端传来的消息{ERR_PERR("recv");return -1;}else if (res == 0){printf("i = %d 客户端下线!\n",i);close(i);fds[i].fd = -1;maxfd = i < maxfd ? maxfd : maxfd-1;continue;}printf("i = %d [%s:%d]%s\n",i,inet_ntoa(cins[i].sin_addr),ntohs(cins[i].sin_port),buf);strcat(buf,"已阅");send(i,buf,sizeof(buf),0);}}}close(sfd);return 0;
}

客户端

#include<myhead.h>
#define IP "192.168.250.100"      
#define PORT 8888               
int main(int argc, const char *argv[])
{//创建socket套接字int cfd = socket(AF_INET, SOCK_STREAM, 0);if(cfd == -1){perror("socket error");return -1;}//填充信息结构体struct sockaddr_in sin;sin.sin_family = AF_INET;   sin.sin_port =     htons(PORT);   sin.sin_addr.s_addr = inet_addr(IP);   if(connect(cfd, (struct sockaddr*)&sin, sizeof(sin))==-1)//链接服务器{perror("connect error");return -1;}char buf[128] = "";while(1){int res = poll(pfd, 2, -1);   //阻塞检测集合中是否有事件产生if(res == -1){perror("poll error");return -1;}else if(poll == 0){printf("time out\n");return -1;}if(pfd[0].revents == POLLIN)              {//清空数据memset(buf,0,sizeof(buf));fgets(buf, sizeof(buf), stdin);   //从终端输入buf[strlen(buf)-1] = 0;send(cfd, buf, strlen(buf), 0);printf("发送成功\n");if(strcmp(buf, "quit") == 0){break;}}if(pfd[1].revents == POLLIN)        {//清空数据memset(buf,0,sizeof(buf));recv(cfd, buf, sizeof(buf), 0); printf("收到消息为:%s\n", buf);}}close(cfd);return 0;
}

TCP移动机械臂 

#include<myhead.h> // 引入自定义的头文件
#include <ncurses.h> // 引入ncurses库
#define IP "192.168.125.116" // 定义服务器IP地址
#define PORT 8888 // 定义服务器端口号
int main(int argc,const char *argv[]) // 主函数
{//创建套接字int sfd = socket(AF_INET,SOCK_STREAM,0); // 创建TCP套接字if (sfd == -1) // 如果套接字创建失败{ERR_PERR("socket"); // 打印错误信息return -1; // 返回-1}//填充IP地址和端口号struct sockaddr_in cin; // 定义服务器地址结构体cin.sin_family = AF_INET; // 设置地址族为IPv4cin.sin_port = htons(PORT); // 设置端口号cin.sin_addr.s_addr = inet_addr(IP); // 设置IP地址//链接服务器if (connect(sfd,(struct sockaddr *)&cin,sizeof(cin)) == -1) // 连接服务器{ERR_PERR("bind"); // 打印错误信息return -1; // 返回-1}//定义一个标志位int num = 0; // 定义标志位//初始化机械臂位置char r[5] = {0xff,0x02,0x00,0x00,0xff}; // 初始化红色机械臂位置unsigned char b[5] = {0xff,0x02,0x01,0x00,0xff}; // 初始化蓝色机械臂位置sendto(sfd,r,sizeof(r),0,(struct sockaddr*)&cin,sizeof(cin)); // 发送红色机械臂位置信息sleep(1); // 延时1秒,防止沾包sendto(sfd,b,sizeof(b),0,(struct sockaddr*)&cin,sizeof(cin)); // 发送蓝色机械臂位置信息initscr(); // 初始化 ncurses// cbreak(); // 禁用行缓冲// noecho(); // 禁用回显while (1) // 进入循环{char buf = 0; // 定义接收操作的变量buf = getchar(); // 获取用户输入switch (buf) // 开始switch语句{case 'w': // 如果输入为wcase 'W': // 或者大写Wr[3] += 5; // 红色机械臂位置增加5if (r[3] >= 90) // 如果位置超过90度{r[3] = 90; // 将位置设置为90度}else if (r[3] <=-90) // 如果位置小于-90度{r[3] = -90; // 将位置设置为-90度}sendto(sfd,r,sizeof(r),0,(struct sockaddr*)&cin,sizeof(cin)); // 发送红色机械臂位置信息break; // 结束当前casecase 's': // 如果输入为scase 'S': // 或者大写Sr[3] -= 5; // 红色机械臂位置减少5if (r[3] >= 90) // 如果位置超过90度{r[3] = 90; // 将位置设置为90度}else if (r[3] <=-90) // 如果位置小于-90度{r[3] = -90; // 将位置设置为-90度}sendto(sfd,r,sizeof(r),0,(struct sockaddr*)&cin,sizeof(cin)); // 发送红色机械臂位置信息break; // 结束当前casecase 'a': // 如果输入为acase 'A': // 或者大写Ab[3] -= 5; // 蓝色机械臂位置减少5if (b[3] >= 0) // 如果位置大于等于0度{b[3] = 0; // 将位置设置为0度}sendto(sfd,b,sizeof(b),0,(struct sockaddr*)&cin,sizeof(cin)); // 发送蓝色机械臂位置信息break; // 结束当前casecase 'd': // 如果输入为dcase 'D': // 或者大写Db[3] += 5; // 蓝色机械臂位置增加5if (b[3] >= 180) // 如果位置超过180度{b[3] = 180; // 将位置设置为180度}sendto(sfd,b,sizeof(b),0,(struct sockaddr*)&cin,sizeof(cin)); // 发送蓝色机械臂位置信息break; // 结束当前casecase 'q': // 如果输入为qcase 'Q': // 或者大写Qnum = 1; // 设置标志位为1default: // 默认情况continue; // 继续下一次循环break; // 结束当前case}if (num == 1) // 如果标志位为1{break; // 退出循环}}endwin(); // 结束 ncurses//关闭套接字close(sfd); // 关闭套接字return 0; // 返回0
}

上传下载 

#include<myhead.h> // 包含自定义头文件myhead.h
#include <ncurses.h> // 引入ncurses库
#define IP "192.168.125.135" // 定义IP地址为"192.168.125.135"
#define PORT 69 // 定义端口号为69
int down(int cfd,struct sockaddr_in sin); // 声明down函数,参数为cfd和sin
int up(int cfd,struct sockaddr_in sin);
void jiemian(void); // 声明jiemian函数,无参数
int main(int argc, char *argv[]) // 主函数,参数为argc和argv
{int cfd = socket(AF_INET,SOCK_DGRAM,0); // 创建UDP套接字cfdif (cfd == -1) // 如果cfd为-1{ERR_PERR("socket"); // 打印错误信息return -1; // 返回-1}struct sockaddr_in sin; // 定义sockaddr_in结构体sinsin.sin_family = AF_INET; // 设置sin的地址族为AF_INETsin.sin_port = htons(PORT); // 设置sin的端口号为PORTsin.sin_addr.s_addr = inet_addr(IP); // 设置sin的IP地址为IP// down(cfd,sin);while (1){system("clear");jiemian(); // 调用jiemian函数printf("请输入选项>>>");char num = getchar();while(getchar() != 10); // 清空输入缓冲区switch (num){case '1':down(cfd,sin); // 调用down函数,参数为cfd和sinbreak;case '2':up(cfd,sin); // 调用up函数,参数为cfd和sinbreak;case '3':close(cfd); // 关闭套接字cfdsystem("clear");return 0; // 返回0break;default:continue;break;}printf("按任意键继续……");getchar();}
}
void jiemian(void) // 定义jiemian函数,无参数
{printf("*************************************\n\
*************************************\n\
*************1、下载*****************\n\
*************2、上传*****************\n\
*************3、退出*****************\n\
*************************************\n\
*************************************\n"); // 打印菜单信息
}
int down(int cfd,struct sockaddr_in sin) // 定义down函数,参数为cfd和sin
{char buf[516] = ""; // 定义长度为516的字符数组bufchar filename[20] = ""; // 定义长度为20的字符数组filenameprintf("请输入文件名:"); // 打印提示信息scanf("%s",filename); // 读取用户输入的文件名while(getchar() != 10); // 清空输入缓冲区int fd = open(filename,O_WRONLY|O_CREAT,0664); // 打开文件,可写且创建,权限为0664if (fd == -1) // 如果打开文件失败{ERR_PERR("open"); // 打印错误信息return -1; // 返回-1}int size = sprintf(buf,"%c%c%s%c%s%c",0,1,filename,0,"octet",0); // 格式化字符串到buf中if (sendto(cfd,buf,size,0,(struct sockaddr*)&sin,sizeof(sin)) < 0) // 发送数据到服务器{ERR_PERR("sendto"); // 打印错误信息return -1; // 返回-1}unsigned short code = 0; // 定义short类型变量num和codewhile (1) // 进入循环{memset(buf,0,sizeof(buf)); // 将buf清零struct sockaddr_in cin;socklen_t len = sizeof(cin); // 定义socklen_t类型变量lenssize_t res = recvfrom(cfd,buf,sizeof(buf),0,(struct sockaddr*)&cin,&len); // 从服务器接收数据if (res == -1) // 如果接收数据失败{ERR_PERR("recvform"); // 打印错误信息return -1; // 返回-1}code = buf[1]; // 获取数据包中的codeunsigned short *p = (unsigned short *)buf; // 定义short指针p,指向bufif (code == 3) // 如果code为3{*p = htons(4); // 转换网络字节序if (sendto(cfd,buf,4,0,(struct sockaddr*)&cin,sizeof(cin)) < 0) // 发送数据到服务器{ERR_PERR("sendto"); // 打印错误信息return -1; // 返回-1}if (res < 516) // 如果接收到的数据小于516{if(write(fd,p+2,strlen(buf+4)) == -1) // 写入文件{ERR_PERR("write");return -1;}break; // 跳出循环}else if (res == 516) // 如果接收到的数据等于516{if(write(fd,p+2,512) == -1) // 写入文件{ERR_PERR("write");return -1;}continue; // 继续循环}}else if (code == 5) // 如果code为5{printf("download error! errno = %d\n",buf[3]); // 打印下载错误信息break; // 跳出循环}}close(fd); // 关闭文件
}
int up(int cfd,struct sockaddr_in sin)
{char buf[516] = ""; // 定义长度为516的字符数组bufchar filename[20] = ""; // 定义长度为20的字符数组filenameprintf("请输入文件名:"); // 打印提示信息scanf("%s",filename); // 读取用户输入的文件名while(getchar() != 10); // 清空输入缓冲区int fd = open(filename,O_RDONLY); // 打开文件,可写且创建,权限为0664if (fd == -1) // 如果打开文件失败{ERR_PERR("open"); // 打印错误信息return -1; // 返回-1}int size = sprintf(buf,"%c%c%s%c%s%c",0,2,filename,0,"octet",0); // 格式化字符串到buf中if (sendto(cfd,buf,size,0,(struct sockaddr*)&sin,sizeof(sin)) < 0) // 发送数据到服务器{ERR_PERR("sendto"); // 打印错误信息return -1; // 返回-1}while (1){memset(buf,0,sizeof(buf)); // 将buf清零struct sockaddr_in cin;socklen_t len = sizeof(cin); // 定义socklen_t类型变量lenssize_t res = recvfrom(cfd,buf,sizeof(buf),0,(struct sockaddr*)&cin,&len); // 从服务器接收数据if (res == -1) // 如果接收数据失败{ERR_PERR("recvform"); // 打印错误信息return -1; // 返回-1}if (buf[1] == 4) // 如果收到的数据包类型为4{unsigned short num = ((buf[2] << 8)|buf[3]) + 1; // 计算下一个块编号memset(buf,0,512); // 将buf清零buf[1] = 3; // 设置数据包类型为3*(unsigned short *)(buf + 2) = htons(num); // 设置块编号ssize_t rec = read(fd,buf+4,512); // 从文件中读取数据到buf中if (rec == 0) // 如果读取到的数据长度为0{break; // 跳出循环}sendto(cfd,buf,rec+4,0,(struct sockaddr *)&cin,sizeof(cin)); // 发送数据包到服务器}}close(fd); // 关闭文件
}

网络聊天室 

服务器

fun.h

#ifndef __FUN_H__
#define __FUN_H__
#include<myhead.h> // 引入自定义的头文件
#define PORT 8888   //端口号1024~49151
#define IP "192.168.250.100" //本机IP
//消息结构体
typedef struct data
{char type;//要进行的选项char username[20];//用户名称char data[1024];//消息内容
}data,*dataptr;
//链表节点
typedef struct fun
{struct sockaddr_in cin; //客户端地址char username[20];//用户名称int fd;   //文件描述符int len;   //记录链表长度struct fun *next;      //记录后继节点的地址
}fun,*funptr;
struct cli
{int newfd; // 客户端套接字描述符struct sockaddr_in cin; // 客户端地址信息结构体funptr list;//链表
};
funptr list_create();// 链表的创建
int list_kong(funptr list);// 链表判空
funptr list_new(char *e,struct sockaddr_in cin,int fd);// 申请节点封装数据
int list_head_create(funptr list,char *e,struct sockaddr_in cin,int fd);// 头插
int list_ls(funptr list);// 遍历
funptr list_pos_ptr(funptr list,int pos);// 按位置查找返回节点地址
int list_pos_del(funptr list,int pos);// 任意位置删除
int list_data_ptr(funptr list,char *e);//按值查找返回节点位置
int list_lol(funptr list);// 链表翻转
int list_end_del(funptr list); //尾删
int list_del(funptr *list);// 链表的销毁
void *task(void *arg);// 线程函数
void *task1(void *arg); // 线程函数
#endif

fun.c

#include"fun.h"
// 链表的创建
funptr list_create()
{//从堆区申请空间funptr list_head = (funptr)malloc(sizeof(fun));if(list_head == NULL){ERR_PRIN("list_ptr error");return NULL;}//初始化节点strcpy(list_head->username,"");list_head->len = 0;list_head->fd = -1;list_head->next = NULL;return list_head;
}
// 链表翻转
int list_lol(funptr list)
{if(list_kong(list) || list->next == NULL){ERR_PRIN("链表反转失败!\n");return -1;}funptr L = list->next;list->next = NULL;funptr q;while(L != NULL){q = L;L = L->next;q->next = list->next;list->next = q;}printf("链表翻转成功!\n");return 0;
}
// 申请节点
funptr list_new(char *e,struct sockaddr_in cin,int fd)
{//从堆区申请空间funptr list_head = (funptr)malloc(sizeof(fun));if(list_head == NULL){ERR_PRIN("list_ptr error");return NULL;}//初始化节点list_head->fd = fd;list_head->cin = cin;strcpy(list_head->username,e);list_head->len = 0;list_head->next = NULL;return list_head;
}
// 链表判空
int list_kong(funptr list)
{if(list == NULL){printf("链表不存在!\n");return 0;}if(list->len == 0 && list->next == NULL)return 1;else return 0;
}
// 头插
int list_head_create(funptr list,char *e,struct sockaddr_in cin,int fd)
{if(list == NULL){printf("链表不存在!\n");return -1;}//创建一个中间变量,调用【申请节点】funptr p = list_new(e,cin,fd);p->next = list->next;list->next = p;list->len++;return 0;
}
// 遍历
int list_ls(funptr list)
{if(list_kong(list) || list->next == NULL){ERR_PRIN("遍历失败!\n");return -1;}funptr p = list->next;//循环链表while(p != NULL){printf("%d ",p->fd);p = p->next;}printf("\n");return 0;
}
// 按位置查找返回节点地址
funptr list_pos_ptr(funptr list,int pos)
{if(list_kong(list) || list->next == NULL || pos > list->len){ERR_PRIN("查找失败!\n");return NULL;}funptr p = list;for(int i = 0;i < pos;i++){p = p->next;}return p;
}
// 任意位置删除
int list_pos_del(funptr list,int pos)
{if(list_kong(list) || list->next == NULL || pos > list->len){ERR_PRIN("删除失败!\n");return -1;}//找到前一个结点funptr p = list_pos_ptr(list,pos-1);funptr q = p->next;p->next = q->next;free(q);q = NULL;list->len--;return 0;
}
// 尾删
int list_end_del(funptr list)
{if(list_kong(list) || list->next == NULL){ERR_PRIN("删除失败!\n");return -1;}funptr p = list;//循环找到倒数第二个节点while(p->next->next != NULL){p = p->next;}//释放最后一个节点free(p->next);p->next = NULL;list->len--;printf("删除成功!\n");return 0;
}
// 链表的销毁
int list_del(funptr *list)
{if(*list == NULL){printf("链表不存在!\n");return -1;}while(!list_kong(*list)){list_end_del(*list);}free(*list);*list = NULL;printf("销毁成功!\n");return 0;
}
//按值查找返回位置
int list_data_ptr(funptr list,char *e)
{if(list_kong(list) || list->next == NULL){printf("查找失败!\n");return -1;}funptr p = list->next;int index = 1;while (p != NULL){if (strcmp(p->username,e) == 0){return index;}p = p->next;index++;}return 0;
}
// 线程函数
void *task(void *arg) 
{struct cli info = *(struct cli*)arg; // 获取客户端信息ssize_t res = 0; // 接收数据长度data cli_data;//定义消息结构体变量char buf[20] = "";while(1) // 进入循环{//字符串清空memset(buf,0,sizeof(buf));memset(cli_data.data,0,sizeof(cli_data.data)); // 清空缓冲区memset(cli_data.username,0,sizeof(cli_data.username)); // 清空缓冲区//读取消息if((res = recv(info.newfd,&cli_data,sizeof(cli_data),0)) == -1) // 接收消息{ERR_PERR("recv error"); // 打印错误信息return NULL; // 返回空}strcpy(buf,cli_data.username);if (cli_data.type == 'C'){if (strcmp(cli_data.data,"quit") == 0){funptr p = info.list->next; //接收链表while (p != NULL){if (p->fd == info.newfd){printf("%s [%s:%d]客户端下线!\n",p->username,inet_ntoa(info.cin.sin_addr),ntohs(info.cin.sin_port)); // 打印客户端下线信息close(p->fd);}if (p->fd != info.newfd) //判断是否是同一个用户{// printf("p->fd = %d\n",p->fd);cli_data.type = 'L';memset(cli_data.data,0,sizeof(cli_data.data)); //清空消息sprintf(cli_data.data,"-----%s已下线-----",cli_data.username);if(send(p->fd,&cli_data,sizeof(cli_data),0) == -1) // 发送消息{ERR_PERR("send error"); // 打印错误信息return NULL; // 返回空}}p = p->next;}// printf("username = %s\n",cli_data.username);// printf("pos = %d\n",list_data_ptr(info.list,cli_data.username));list_pos_del(info.list,list_data_ptr(info.list,cli_data.username));// list_ls(info.list);break; // 退出循环}funptr p = info.list->next; //接收链表while (p != NULL){if (p->fd != info.newfd) //判断是否是同一个用户{// printf("p->fd = %d\n",p->fd);if(send(p->fd,&cli_data,sizeof(cli_data),0) == -1) // 发送消息{ERR_PERR("send error"); // 打印错误信息return NULL; // 返回空}}p = p->next;}printf("%s [%s:%d] %s\n",cli_data.username,inet_ntoa(info.cin.sin_addr),ntohs(info.cin.sin_port),cli_data.data); // 打印客户端发送的消息}else if(res == 0) // 如果接收到的数据长度为0{funptr p = info.list->next; //接收链表while (p != NULL){if (p->fd == info.newfd){printf("%s [%s:%d]客户端下线!\n",p->username,inet_ntoa(info.cin.sin_addr),ntohs(info.cin.sin_port)); // 打印客户端下线信息}if (p->fd != info.newfd) //判断是否是同一个用户{cli_data.type = 'L';memset(cli_data.data,0,sizeof(cli_data.data));sprintf(cli_data.data,"-----%s已下线-----",buf);if(send(p->fd,&cli_data,sizeof(cli_data),0) == -1) // 发送消息{ERR_PERR("send error"); // 打印错误信息return NULL; // 返回空}close(p->fd);}p = p->next;}list_pos_del(info.list,list_data_ptr(info.list,cli_data.username));break; // 退出循环}}close(info.newfd); // 关闭客户端套接字pthread_exit(NULL); // 退出线程
}
// 线程函数
void *task1(void *arg) 
{struct cli info1 = *(struct cli *)arg;data cli_data = {.type = 'C',.data = "",.username = "大王"};while(1){memset(cli_data.data,0,sizeof(cli_data.data)); //清空字符数组scanf("%s",cli_data.data); //从终端获取数据if (strcmp(cli_data.data,"quit") == 0 && info1.list->next == NULL){exit(EXIT_SUCCESS);break;}funptr p = info1.list->next; //接收链表while (p != NULL){if(send(p->fd,&cli_data,sizeof(cli_data),0) == -1) // 发送消息{ERR_PERR("send error"); // 打印错误信息return NULL; // 返回空}p = p->next;}}
}

main.c

#include"fun.h"
int main(int argc, const char *argv[]) // 主函数
{//创建socket套接字int sfd = socket(AF_INET,SOCK_STREAM,0); // 创建套接字if(sfd < 0) // 如果创建套接字失败{ERR_PERR("socket error"); // 打印错误信息return -1; // 返回-1}//允许端口快速的被复用xint reuse = 1; // 设置端口复用if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) // 设置套接字选项{ERR_PERR("setsockopt"); // 打印错误信息return -1; // 返回-1}//填充信息结构体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)) < 0) // 绑定地址{ERR_PERR("bind error"); // 打印错误信息return -1; // 返回-1}//设置监听套接字if(listen(sfd,128) < 0) // 监听套接字{ERR_PERR("listen error"); // 打印错误信息return -1; // 返回-1}//获取新套接字int newfd = -1; // 新的套接字描述符struct sockaddr_in cin; // 客户端地址信息结构体socklen_t len = sizeof(cin); // 地址信息结构体长度funptr list = list_create();//创建链表data cli_data;//定义消息结构体变量struct cli info1 = {.list = list};pthread_t tid1;pthread_create(&tid1,NULL,task1,&info1); // 创建线程pthread_detach(tid1); // 分离线程while(1) // 进入循环{if((newfd = accept(sfd,(struct sockaddr*)&cin,&len)) < 0) // 接受新的连接{ERR_PERR("accept error"); // 打印错误信息return -1; // 返回-1}if (recv(newfd,&cli_data,sizeof(cli_data),0) == -1) // 读取消息{ERR_PERR("recv");return -1;}if (cli_data.type == 'L'){if (list_head_create(list,cli_data.username,cin,newfd) == -1)// 插入链表{ERR_PRIN("list_head_create");return -1;}printf("%s [%s:%d]客户端连接成功\n",cli_data.username,inet_ntoa(cin.sin_addr),ntohs(cin.sin_port)); // 打印客户端连接成功信息memset(cli_data.data,0,sizeof(cli_data.data)); //清空消息// list_ls(list);funptr p = list->next; //接收链表while (p != NULL){if (p->fd != newfd) //判断是否是同一个用户{// printf("newfd = %d\n",newfd);// printf("p->fd = %d\n",p->fd);sprintf(cli_data.data,"-----%s已上线-----",cli_data.username);if(send(p->fd,&cli_data,sizeof(cli_data),0) == -1) // 发送消息{ERR_PERR("send error"); // 打印错误信息return -1; // 返回空}}p = p->next;}pthread_t tid; // 线程IDstruct cli info = {newfd,cin,list}; // 客户端信息pthread_create(&tid,NULL,task,(void *)&info); // 创建线程pthread_detach(tid); // 分离线程}}list_del(&list);// 销毁链表close(sfd); // 关闭服务器套接字close(newfd); // 关闭客户端套接字return 0; // 返回0
}

客户端 

fun.h

#ifndef __FUN_H__
#define __FUN_H__
#include<myhead.h> // 引入自定义的头文件
#define PORT 8888   //端口号1024~49151
#define IP "192.168.250.100" //本机IP
//消息结构体
typedef struct data
{char type;//要进行的选项char username[20];//用户名称char data[1024];//消息内容
}data,*dataptr;
//链表节点
typedef struct fun
{struct sockaddr_in cin; //客户端地址char username[20];//用户名称int fd;   //文件描述符int len;   //记录链表长度struct fun *next;      //记录后继节点的地址
}fun,*funptr;
struct cli
{int newfd; // 客户端套接字描述符struct sockaddr_in cin; // 客户端地址信息结构体funptr list;//链表
};
void *task(void *arg);// 线程函数
#endif

fun.c

#include"fun.h"
// 线程函数
void *task(void *arg) 
{int sfd = *(int *)arg;ssize_t res = 0; // 接收数据长度data cli_data;//定义消息结构体变量while(1) // 进入循环{//字符串清空memset(cli_data.data,0,sizeof(cli_data.data)); // 清空缓冲区memset(cli_data.username,0,sizeof(cli_data.username)); // 清空缓冲区//读取消息if((res = recv(sfd,&cli_data,sizeof(cli_data),0)) == -1) // 接收消息{ERR_PERR("recv error"); // 打印错误信息return NULL; // 返回空}if (cli_data.type == 'L'){printf("%s\n",cli_data.data);}if (cli_data.type == 'C'){printf("%s:%s\n",cli_data.username,cli_data.data); // 打印客户端发送的消息}}pthread_exit(NULL); // 退出线程
}

main.c

#include"fun.h"
int main(int argc, const char *argv[])
{//创建socket套接字int sfd = socket(AF_INET,SOCK_STREAM,0);if(sfd < 0){ERR_PERR("socket error");return -1;}//填充信息结构体struct sockaddr_in sin;sin.sin_family = AF_INET;   //IPv4sin.sin_port = htons(PORT); //端口号转换成网络sin.sin_addr.s_addr = inet_addr(IP); //本机IP转换成网络//连接服务器if(connect(sfd,(struct sockaddr *)&sin,sizeof(sin)) < 0){ERR_PERR("connect error");return -1;}char buf[20] = ""; //存储用户名称data cli_data = {.type = 'L'};//定义消息结构体变量memset(cli_data.username,0,sizeof(cli_data.username)); // 清空缓冲区printf("请输入名称>>>");scanf("%s",buf);strcpy(cli_data.username,buf);//将用户名称输入结构体//发送消息if(send(sfd,&cli_data,sizeof(cli_data),0) < 0){ERR_PERR("send error");return -1;}ssize_t res = 0;pthread_t tid; // 线程IDpthread_create(&tid,NULL,task,&sfd); // 创建线程pthread_detach(tid); // 分离线程while(1){//字符串清空memset(cli_data.data,0,sizeof(cli_data.data)); // 清空缓冲区//从终端获取消息scanf("%s",cli_data.data);//发送消息cli_data.type = 'C';//切换传输模式if(send(sfd,&cli_data,sizeof(cli_data),0) < 0){ERR_PERR("send error");return -1;}if(!strcmp(cli_data.data,"quit")){if(send(sfd,&cli_data,sizeof(cli_data),0) < 0){ERR_PERR("send error");return -1;}break;}}//关闭套接字close(sfd);return 0;
}

【有道云笔记】模拟面试(自己)
https://note.youdao.com/s/DnuFAmvS

【有道云笔记】无标题笔记
https://note.youdao.com/s/8HIuYadl

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/737549.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

倍增法/st表 算法解析+例题

倍增法 / st 表 基本 倍增法及 LCA 倍增&#xff0c;顾名思义就是成倍的增加。主要思想就是将问题的大区间分成 log ⁡ n \log n logn 个小块&#xff0c;每个块的长度为一个尽可能大的二的整数次幂&#xff0c;对于每个块用类似动态规划的方法 O ( n log ⁡ n ) O(n\log n…

kibana新增查看更新删除es中的数据

登录kibana&#xff0c;打开开发工具 写入数据 PUT test20240311/person/1 {"name": "张三","mobile":"13011111111" } 查询数据 GET /test20240311/person/_search {"query": {"term": {"mobile": {…

docker安装各种组件

一 docker基本命令 镜像操作 ① 列举镜像 docker images ② 搜索镜像&#xff08;以jdk为例&#xff09; docker search jdk ③ 下载镜像 docker pull java ④ 查看所有镜像 docker images ⑤ 启动镜像&#xff08;以jdk8为例&#xff09; docker run -it --name jdk…

Android AMS

Android进阶&#xff1a;一口气读完《Android进阶解密》 - 掘金 Android AMS&#xff08;Activity Manager Service&#xff09;实现的功能 **管理应用程序的生命周期&#xff1a;**启动、停止、暂停、恢复和销毁应用程序。 **控制应用程序的启动顺序&#xff1a;**确保应用程…

鸿蒙实战开发学习:【HiView插件开发】

概述 Hiview是一个跨平台的终端设备维测服务集&#xff0c;其中是由插件管理平台和插件实现的各自功能构成整套系统。 本文描述了hiview插件开发的全部流程。 插件的概念 整节部分包括了插件的概念&#xff0c;事件源的概念&#xff0c;流水线的概念等基本概念 插件的定义 …

低功耗高端蓝牙智能跳绳解决方案

一、方案概述 跳绳运动作为轻量、燃脂、便捷的运动之一&#xff0c;拥有庞大的人群基础。在这样的趋势下&#xff0c;智能跳绳的智能化及精细化也就顺理成章。 芯联深入智能运动健康器材市场&#xff0c;最新开发了蓝牙智能跳绳方案&#xff0c;采用双霍尔高精准计数方案&…

Nodejs 18.19.0 基本环境搭建(无版本管理)

简介 记录一次安装Nodejs的过程, 及设置install 安装路径等… 基本安装及设置步骤 1. 下载Nodejs并安装 NodeJs 18.19.0 LTS Windows安装msi, 下载后安装在自己想要的路径, 例如 D:\Softwares\Dev_Tools\nodejs cmd 中输入 nodejs version 可以查看版本 2. 设置 npm config ls…

Java17 --- SpringCloud初始项目创建

目录 一、cloud项目创建 1.1、项目编码规范 1.2、注解生效激活 1.3、导入父工程maven的pom依赖 二、创建子工程并导入相关pom依赖 2.1、相关配置文件 2.1.1、数据库配置文件内容 2.1.2、自动生成文件配置内容 三、创建微服务8001子工程 3.1、导入相关pom依赖 3.…

蓝桥杯PythonB组练习——字符串对比

一、题目 问题描述 给定两个仅由大写字母或小写字母组成的字符串&#xff08;长度介于1到10之间&#xff09;&#xff0c;它们之间的关系是以下4种情况之一&#xff1a; 1&#xff1a;两个字符串长度不等。比如Beijing和Hebei 2&#xff1a;两个字符串不仅长度相等&#xf…

全面认识计算机操作系统(二)

目录 一、操作系统的诞生 相关概念&#xff1a; 1. 手工操作阶段 2. 脱机输入 / 输出阶段 &#xff08;1&#xff09;脱机输入技术 &#xff08;2&#xff09;脱机输出技术 3. 单道批处理阶段 4. 多道批处理阶段 5. 分时技术产生 6. 实时系统产生 二、现代操作系统的…

植物病害识别:YOLO水稻病害识别数据集(3000多张,3个类别,yolo标注)

YOLO水稻病害识别数据集&#xff0c;包含细菌性枯萎病&#xff0c;水稻瘟疫&#xff0c;褐斑病3个常见病害类别&#xff0c;共3000多张图像&#xff0c;yolo标注完整&#xff0c;可直接训练。 适用于CV项目&#xff0c;毕设&#xff0c;科研&#xff0c;实验等 需要此数据集或…

隐私与创新的交汇点:Partisia Blockchain 重绘技术蓝图

正当我们在这个信息泛滥的时代寻找稳固的信任锚点时&#xff0c;区块链技术应运而生&#xff0c;然而&#xff0c;正如任何科技革命都会遇到的挑战&#xff0c;一个重要的问题摆在了我们面前&#xff1a;如何在不牺牲个人隐私的前提下&#xff0c;享受区块链技术带来的好处&…

ChatGLM:CPU版本如何安装和部署使用

前段时间想自己部署一个ChatGLM来训练相关的物料当做chatgpt使用&#xff0c;但是奈何没有gpu机器&#xff0c;只能使用cpu服务器尝试使用看看效果 我部署的 Chinese-LangChain 这个项目&#xff0c;使用的是LLM&#xff08;ChatGLM&#xff09;embedding(GanymedeNil/text2vec…

深克隆和浅克隆

1.克隆 克隆是指创建对象的一个副本&#xff0c;使副本具有与原始对象相同的属性和状态。 而克隆又分深克隆和浅克隆。 2.浅克隆 浅克隆&#xff1a;只会把原对象中类型为值的属性复制一份&#xff0c;然后引用数据类型就把它们在内存中的地址复制过去 比如一个对象有 public …

Rust:性能优异的异步并发库 Tokio

一、Tokio 简介 Tokio是一个开源的异步运行时库&#xff0c;专为编写异步IO应用而设计。它提供了一种简单而安全的方式来编写异步代码&#xff0c;允许开发者无缝切换不同类型的异步运行时&#xff0c;并提供了统一的接口用于实现不同的功能&#xff0c;如网络IO、文件IO、数据…

我终于解决MathPage.wll文件找不到问题|(最新版Word上亲测)运行时错误,53’: 文件未找到:athPage.WLL

1、问题症状&#xff1a; 运行时错误&#xff0c;53’: 文件未找到:athPage.WLL 2、 解决方案 第一步 首先我们要先找到MathType安装目录下MathPage.wll文件&#xff0c;直接在此电脑中搜索MathPage.wll&#xff0c;找到文件所在位置。 第二步 打开Word文件&#xff0c…

Matplotlib进阶学习:基于Matplotlib实现设置Times New Roman字体

Matplotlib进阶学习&#xff1a;基于Matplotlib实现设置Times New Roman字体 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程&#x1f4…

求区间的最大值(RMQ、线段树)

题目描述&#xff1a; 有n个数&#xff0c;给你m个询问&#xff0c;每次询问给你A&#xff0c;B个数&#xff0c;要求你编写程序求出[A,B]这个区间中的最大值。 输入格式&#xff1a; 第一行一个整数 N表示数字的个数。 接下来一行为 N个数&#xff0c;表示数字序列。 第三…

Git指令reset的参数soft、mixed与hard三者之间的区别

主要内容 reset默认不写参数&#xff0c;与使用mixed参数含义一样 为了描述简洁&#xff0c;使用下图说明&#xff1a; #mermaid-svg-LtChquRXlEV1j6og {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-LtChquRXlEV1j…

实现简易可视化拖拽生成页面

1.背景 由于一些简单、重复的页面&#xff0c;需要开发&#xff0c;会耗费大量人力和物力。因此一般会利用低代码平台&#xff0c;进行拖拽生成&#xff0c;下面就是实现简易可视化拖拽生成页面 2 简易可视化拖拽生成页面具体实现 2.1 整体页面布局 整个页面分为顶栏、内容区…