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)); // 清空缓冲区buf
        res = 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); // 打开文件,并将文件指针存储到fd0
    if(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);
            //将换行符转换成0
            if(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; // 设置地址族为IPv4
    cin.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': // 如果输入为w
        case 'W': // 或者大写W
            r[3] += 5; // 红色机械臂位置增加5
            if (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; // 结束当前case
        case 's': // 如果输入为s
        case 'S': // 或者大写S
            r[3] -= 5; // 红色机械臂位置减少5
            if (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; // 结束当前case
        case 'a': // 如果输入为a
        case 'A': // 或者大写A
            b[3] -= 5; // 蓝色机械臂位置减少5
            if (b[3] >= 0) // 如果位置大于等于0度
            {
                b[3] = 0; // 将位置设置为0度
            }
            sendto(sfd,b,sizeof(b),0,(struct sockaddr*)&cin,sizeof(cin)); // 发送蓝色机械臂位置信息
            break; // 结束当前case
        case 'd': // 如果输入为d
        case 'D': // 或者大写D
            b[3] += 5; // 蓝色机械臂位置增加5
            if (b[3] >= 180) // 如果位置超过180度
            {
                b[3] = 180; // 将位置设置为180度
            }
            sendto(sfd,b,sizeof(b),0,(struct sockaddr*)&cin,sizeof(cin)); // 发送蓝色机械臂位置信息
            break; // 结束当前case
        case 'q': // 如果输入为q
        case 'Q': // 或者大写Q
            num = 1; // 设置标志位为1
        default: // 默认情况
            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套接字cfd
    if (cfd == -1) // 如果cfd为-1
    {
        ERR_PERR("socket"); // 打印错误信息
        return -1; // 返回-1
    }
    struct sockaddr_in sin; // 定义sockaddr_in结构体sin
    sin.sin_family = AF_INET; // 设置sin的地址族为AF_INET
    sin.sin_port = htons(PORT); // 设置sin的端口号为PORT
    sin.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和sin
            break;
        case '2':
            up(cfd,sin); // 调用up函数,参数为cfd和sin
            break;
        case '3':
            close(cfd); // 关闭套接字cfd
            system("clear");
            return 0; // 返回0
            break;
        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的字符数组buf
    char filename[20] = ""; // 定义长度为20的字符数组filename
    printf("请输入文件名:"); // 打印提示信息
	scanf("%s",filename); // 读取用户输入的文件名
    while(getchar() != 10); // 清空输入缓冲区
    int fd = open(filename,O_WRONLY|O_CREAT,0664); // 打开文件,可写且创建,权限为0664
    if (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和code
    while (1) // 进入循环
    {
        memset(buf,0,sizeof(buf)); // 将buf清零
        struct sockaddr_in cin;
        socklen_t len = sizeof(cin); // 定义socklen_t类型变量len
        ssize_t res = recvfrom(cfd,buf,sizeof(buf),0,(struct sockaddr*)&cin,&len); // 从服务器接收数据
        if (res == -1) // 如果接收数据失败
        {
            ERR_PERR("recvform"); // 打印错误信息
            return -1; // 返回-1
        }
        code = buf[1]; // 获取数据包中的code
        unsigned short *p = (unsigned short *)buf; // 定义short指针p,指向buf
        if (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的字符数组buf
    char filename[20] = ""; // 定义长度为20的字符数组filename
    printf("请输入文件名:"); // 打印提示信息
	scanf("%s",filename); // 读取用户输入的文件名
    while(getchar() != 10); // 清空输入缓冲区
    int fd = open(filename,O_RDONLY); // 打开文件,可写且创建,权限为0664
    if (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类型变量len
        ssize_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
    }
    //允许端口快速的被复用x
    int 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;   //IPv4
    sin.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; // 线程ID
            struct 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;   //IPv4
    sin.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; // 线程ID
    pthread_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

  • 9
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值