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);
}