【从服务器下载文件的实现】

该代码示例展示了客户端通过套接字连接到本地服务器,并发送命令进行交互。客户端将用户输入的命令进行分割并传递给工作线程,工作线程根据命令类型执行不同操作,如创建管道、 fork 子进程执行命令,通过管道通信。当遇到特定命令(如 'end')时,通信结束。
摘要由CSDN通过智能技术生成

client.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#define OK  "ok#"
char* get_cmd(char *buff,char  *myargv)
{
    if (buff == NULL || myargv == NULL)
    {
      
        return NULL;

    }
    char* s = NULL;
    int i = 0;
    s=strtok(buff, " "); //第一次分割出的是命令
    while (s != NULL)
    {
        myargv[i++] = s;
        s = strtok(NULL, " ");//此处在客户端不需要考虑并发编程,不用线程安全_s。
    }
    return myargv[0];  //返回的是命令
    
}
int main()
{
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    if( sockfd == -1 )
    {
        exit(0);
    }

    struct sockaddr_in saddr;
    memset(&saddr,0,sizeof(saddr));
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(6000);
    saddr.sin_addr.s_addr = inet_addr("127.0.0.1");

    int res = connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
    if ( res == -1 )
    {
        close(sockfd);
        exit(0);
    }

    while( 1 )
    {
        printf("connect>>");
        fflush(stdout);

        char buff[128] = {0};
        fgets(buff,128,stdin);
        buff[strlen[buff] - 1] = { 0 };//去除fgets中带来的\n。strlen 不是计算整个空间的大小而是字符串的长度
        char* cmd_buff[128] = { 0 };
        //strncpy_s(cmd_buff, 127, buff);
        strcpy(cmd_buff, buff);复制一份的作用是buff已经被分割了,发源字符串更方便一些。
        char* myargv[10] = { 0 }; //此指针数组存放的是命令加参数。在get_cmd 函数中已经通过传指针把数据传入。
        char *cmd=get_cmd(buff,myargv);
        if (cmd == NULL)
        {
            continue;
        }
        
        if ( strncmp(cmd,"end",3) == 0 )
        {
            break;
        }
        if (strncmp(cmd, "get", 3) == 0)
        {

        }
        if (strncmp(cmd, "up", 2) == 0)
        {

        }
        else{

        
        send(sockfd,cmd_buff,strlen(cmd_buff),0);
        char read_buff[1024] = { 0 };//这里可不能用buff(128)去读,128不够用。

        int num=recv(sockfd,read_buff,1023,0);//1023是防止访问越界。
          //recv()可以返回值也可以调用函数的方式:recv(sockfd,read_buff,1023,0)
        if (num <= 0)
        {
            printf("ser close or error");
            break;
        }
        if (strncmp(read_buff, OK, 3) != 0) 
        {
            printf("%s\n",read_buff);
            //break;
        }

        printf("buff=%s\n",read_buff+strlen(OK);
        }
    }
    close(sockfd);//能不能加上去

}

thread.c

#include "thread.h"
#define ARG_MAX 10
#define ERROR_CMD "error argv"
#define PIPE_ERROR "error pipe"
#define FORK_ERROR   "fork error"
#define OK  "ok#"

#include<thread.h>
char *   get_cmd(char* buff, char* argv[])
{
    if (buff == NULL || argv == NULL)
    {
        
        return NULL;
    }
    char* p = NULL;
    char *s =strtok_s(buff, " ",&p);
    
    int i = 0;

    while (s != NULL)
    {
        
        argv[i++] = s;
        s =strtok_s(NULL, " ", &p);//多线程模式下必须使用线程安全_s。
        return argv[0];
    }



}

void* work_thread(void* arg)
{
   
    int c = (int)arg;
   
    while (1)
    {
        char buff[128] = { 0 };
        int n = recv(c, buff, 127, 0);

        char* myargv[ARG_MAX];
        
        char* cmd = get_cmd(buff, myargv);
        
        if (cmd == NULL)
        {
            send(c, ERROR_CMD, strlen(ERROR_CMD), 0);
            continue;
        }
        if (strncmd(cmd, "get") == 0)
        {

        }
        if (strncmd(cmd, "up") == 0)
        {

        }
        else
        {// 创建管道
            int pipefd[2];// 这里肯定不能直接调用,比如:pipefd[2];
            if (pipe(pipefd) == -1)
            {
                send(c, PIPE_ERROR, strlen(PIPE_ERROR), 0);
                continue;
            }
            pid_t pid = fork();
            if (pid < 0)
            {
                send(c, FORK_ERROR, strlen(FORK_ERROR), 0);
                continue;
            }
            if (pid == 0)
            {
                close(pipefd[0]);//半双工模式,子进程写时,读端是必须关闭的。
                dup2(pipefd[1], 1); //用管道写端替换此线程的标准输入和标准输出(1和2)
                dup2(pipefd[1], 2);//dup2(oldfd,newfd)
                execvp(cmd, myargv);//execvp(command(命令), myargv(命令+参数);
                //这里为什么选execvp???
               
                perror("exec error");
                //close(pipefd[1]);
                exit(0);

            }
            close(pipefd[1]);//子进程结束,父进程要读了就要把写端关闭。
            wait(NULL);
            char read_buff[1024] = {OK}; //发话的是客户端,回话的一端是服务器端,此端有可能返回的是rm等无消息返回的操作,所以会话要带上标志防止客户端以为出现了问题。
            read(c,read_buff+strlen(OK), 1021);
            //待实现代码:
            //如果1024不够就循环收,等read返回值为零之后再wait() {等数据全输完} //这个没实现
            //if (buff == NULL)
            //{
             //   send(c, PIPE_ERROR, strlen(PIPE_ERROR), 0);
           // }
            close(pipefd[0]);
            send(c, read_buff, strlen(read_buff), 0);
        }
    }
    close(c);
    printf("client close\n");
}





void start_thread(int c)
{
    pthread_t id;
    pthread_create(&id, NULL, work_thread, (void*)c);
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值