文本SOCKET客户端,多进程可解决网速较慢问题,有命令缓冲功能_C/C++_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > C/C++ > 文本SOCKET客户端,多进程可解决网速较慢问题,有命令缓冲功能

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

 2010/11/19 9:18:48  deepfuture  http://deepfuture.javaeye.com  我要评论(0)
  • 摘要:/*starclient.c程序:刘兴(deepfuturelx@gmail.com)[deepfuture.javaeye.com],最后修改时间:2010.10.25功能:文本客户端,多进程可解决网速较慢问题,有命令缓冲功能*/#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<errno.h>#include<linux/types.h>
  • 标签:文本SOCKET客户端 多进程解决网速慢问题 命令缓冲

/*

starclient.c

程序: 刘兴(deepfuturelx@gmail.com)[deepfuture.javaeye.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.javaeye.com

?? ? ? ? ? ? perror("中断错误");

?? ? ? ? ? ? readcn=0; ? ? ? ? ? ?

?? ? ? ? ?}

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

?? ? ? }

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

?

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

?? ? ? buf+=readcn; ??

?? ? ? ?if (*buf==0) break; ? ?//如果读到字符串结尾标志则退出,必须有这句,否则会死循环 ?deepfuture.javaeye.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.javaeye.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.javaeye.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.javaeye.com

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

?? ? ? ? ?address.sin_port=htons(1253);//端口号,s表示16位 deepfuture.javaeye.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.javaeye.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);?

? ? }

}

?

?

发表评论
用户名: 匿名