文本SOCKET客户端,多进程可解决网速较慢问题,有命令缓冲功能

/*

starclient.c

程序: 刘兴(deepfuturelx@gmail.com)[deepfuture.iteye.com],

最后修改时间:2010.10.25

功能:文本客户端,多进程可解决网速较慢问题,有命令缓冲功能

*/

 

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <errno.h> 

#include <linux/types.h>

#include <linux/shm.h>

#include <linux/sem.h>

#include <linux/ipc.h>

#include <sys/socket.h>

#include <netinet/in.h>

 

 

 

 

#define MAXS  500//命令缓冲区的大小(以字节为单位),这里测试用所以得比较小,最好设置为1024+1,请尽可能大点,因为太小可能导致后来的命令把前面没有执行的命令覆盖

#define BUFFERSIZE  40//接收键盘输入缓冲区

#define SEMID 251//信号标志

#define FILENAME "abc.txt"

#define SHMKEY 241//共享内存标志

#define SHMSIZE MAXS//共享内存大小

#define CMDMAX 20//命令的最长

 

 

//程序完成父进程接收键盘输入,子进程向服务器遄输出。

ssize_t readn(int fd,void *ptr,size_t maxcn){//读取n个字符,maxc为读取的数目

    size_t noreadcn,readcn;

    char *buf=ptr;

 

 

    noreadcn=maxcn;

    while(noreadcn>0){

       if ( (readcn=read(fd,buf,noreadcn))<0){//读数据

 

          if (errno==EINTR) {//数据读取前,操作被信号中断 deepfuture.iteye.com

             perror("中断错误");

             readcn=0;            

          }

          else {return -1;}//无法修复错误,返回读取失败

       }

       else if(readcn==0) break;//EOF deepfuture.iteye.com

 

       noreadcn-=readcn;//读取成功,但是如果读取的字符数小于maxc,则继续读,因为可能数据还会继续通过网络送过来    

       buf+=readcn;   

        if (*buf==0) break;    //如果读到字符串结尾标志则退出,必须有这句,否则会死循环  deepfuture.iteye.com

       }   

 

    return (maxcn-noreadcn);

}

 

ssize_t writen(int fd,void *ptr,size_t maxcn){//写入n个字符

    size_t nowritecn,writecn;

    char *buf=ptr;

 

    nowritecn=maxcn;

    while(nowritecn>0){

       if((writecn=write(fd,buf,nowritecn))<=0){//写数据

          if (errno==EINTR) {//数据写前,操作被信号中断

             perror("中断错误");

             writecn=0;             

          }

          else {return -1;}//无法修复错误,返回读取失败

       }

 

       nowritecn-=writecn;

       buf+=writecn; 

 

       }  

       return (maxcn-nowritecn);

}

 

 

int main(void){

    char strbuf[MAXS];

    char buf[BUFFERSIZE];

    int sem_id;

    int shm_id;

    int pid;

    int rc,res;

    int fd;

    int addresslen;

    struct sembuf sem_op;//信号集结构

    union semun sem_val;//信号量数值

    struct sockaddr_in address;//地址信息结构 deepfuture.iteye.com

    char *inputcur;

    char *outputcur;

    FILE *myfile;

    char *shm_addr;

    char mybuf[100]; 

 

 

 

 

    //建立信号量集,其中只有一个信号量 

    sem_id=semget(SEMID,1,IPC_CREAT|0600);//SEMID为为正整数,则为公共的;1为信号集的数量;

    if (sem_id==-1){

        printf("create sem error!\n");

        exit(1);    

    }

    //信号量初始化

    sem_val.val=0;

    rc=semctl(sem_id,0,SETVAL,sem_val);//设置信号量

    if (rc==-1){

        printf("initlize sem error!\n");

        exit(1);    

    }

    //建立共享内存

    shm_id=shmget(SHMKEY,SHMSIZE,IPC_CREAT|0600);//参数为:标志,大小,权限

    if (shm_id==-1){

        printf("create shm error!\n");

        exit(1);    

    }     

    //attach共享内存。连接共享内存 deepfuture.iteye.com 

    shm_addr=(char *)shmat(shm_id,NULL,0);//返回共享内存地址 

    if (!shm_addr){

        printf("shmat error!\n");

        exit(1);    

    } 

    //初始化数据

    memset(shm_addr,'\0',MAXS);  

    inputcur=shm_addr;//输入当前字符起始地址

    outputcur=shm_addr;//输出当前字符起始地址

    //创建进程

    pid=fork();

    if (pid==-1){

        printf("fork error!\n");

        exit(1);           

    }

    else if(pid==0){//子进程,接受键盘输入,往共享内存中写字符行

         int isend=0;//是否结束输入

  printf("\n#welcome to StarSea mud game (http://starsea.bigbbs.cn/)\n"); //自定义键盘输入时使用的SHELL外观   

  printf("\n#StarSea$"); //自定义键盘输入时使用的SHELL外观 

         while((!isend)&&fgets(buf,BUFFERSIZE,stdin)!=NULL){//从shell中读入一行  

         

             if (buf[0]=='Q'&&strlen(buf)<=2){//单个字符Q表示退出输入

                 isend++;//退出输入

                 printf("\n退出StarSea....\n");

                 *inputcur=-1; 

             }

             else if (buf[0]=='\n'&strlen(buf)<2){ //空命令

                 printf("\n请输入命令\n");

    printf("\n#StarSea$"); //自定义键盘输入时使用的SHELL外观 

                 continue;

             }

             else if (strlen(buf)>(CMDMAX-2)){

                       printf("\n命令长度不能超过%d\n",CMDMAX-2);

          printf("\n#StarSea$"); //自定义键盘输入时使用的SHELL外观 

                       continue;

             }

             else

             {//如果不是退出命令 

         printf("\n#StarSea$"); //自定义键盘输入时使用的SHELL外观

                  fflush(stdout); 

                    if ((MAXS-(inputcur-shm_addr))<=CMDMAX){//缓冲区不够,清空,cur指针复位

 

                      inputcur=shm_addr;//当前字符起始地址    

 

                   }

         //写共享内存           

           memcpy(inputcur,buf,strlen(buf));     

         inputcur+=strlen(buf);

 

         }  

         //写入一行,增加信号

               sem_op.sem_num=0;

               sem_op.sem_op=1;

               sem_op.sem_flg=0;

               semop(sem_id,&sem_op,1);//操作信号量,每次+1 

 

       *inputcur=-1; 

     exit(0);   

    }

    else{//父进程,从共享内存中读字符行 ,并写入文件 

 

          address.sin_family=AF_INET;//IPV4协议,AF_INET6是IPV6 deepfuture.iteye.com

          address.sin_addr.s_addr=inet_addr("127.0.0.1");//l表示32位,htonl能保证在不同CPU的相同字节序

          address.sin_port=htons(1253);//端口号,s表示16位 deepfuture.iteye.com

         addresslen=sizeof(address);      

          while(1)

          {

             int rc;

 

            //读出一行,减少信号

              sem_op.sem_num=0;

              sem_op.sem_op=-1;

              sem_op.sem_flg=0;

              semop(sem_id,&sem_op,1);//操作信号量,每次-1   

 

          // 读共享内存中一行

         if ((*outputcur)==-1) {//输入结束

                     break;

                 }

                      int i;

              for (i=0;*outputcur!='\n';outputcur++,i++){              

                    buf[i]=*outputcur;          

              }   

          outputcur++;              

            buf[i]='\n';

          buf[++i]=0;

 

                  if ((MAXS-(outputcur-shm_addr))<=CMDMAX){//缓冲区不够,清空,cur指针复位

                      outputcur=shm_addr;//当前字符起始地址   

                  }

                  while(1){  //连接

             fd=socket(AF_INET,SOCK_STREAM,0);//建立socket

                      if (fd==-1){//错误,类型从errno获得

                          perror("error");//perror先输出参数,后跟":"加空格,然后是errno值对应的错误信息(不是错误代码),最后是一个换行符。    

                       }

                 rc=connect(fd,(struct sockaddr *)&address,addresslen);//连接服务器 deepfuture.iteye.com

           if (rc==-1){//rc=0成功,rc=-1失败 

                             close(fd);

               perror("服务器断开,20秒后重新连接....(ctrl+c退出)");

                      printf("\n#StarSea$"); //自定义键盘输入时使用的SHELL外观

                             fflush(stdout); 

                             sleep(20);

                         } 

                         else{//成功

                             break;

                         }    

                  }

                  //发送数据 

                  writen(fd,(void *)buf,strlen(buf)+1);

                  bzero(mybuf,100);  

                  readn(fd,(void *)mybuf,100); 

                  printf("\n#%s",mybuf); 

         printf("\n#StarSea$"); //自定义键盘输入时使用的SHELL外观

                  fflush(stdout); 

 

 

                  close(fd);

 

                      

          }

 

                  

    

     wait(&pid);//等待子进程结束,即用户输入完毕

                  memset(shm_addr,'\0',MAXS); 

     //分离共享进程

     if (shmdt(shm_addr)==-1){

              printf("shmdt error!\n");        

     }

     //撤销共享内存,任何进程只要有权限,都可以撤销共享内存,不一定非要创建它的进程

     struct shmid_ds shm_desc;

     if (shmctl(shm_id,IPC_RMID,&shm_desc)==-1){

         printf("shmctl error!\n");

     }

     exit(0); 

    }

}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值