/*
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);
}
}
分享到:
相关推荐
总的来说,这个"socket客户端,可连接多个服务器"的实现涉及了Socket编程的基础,TCP连接的建立与恢复,以及客户端的错误处理和重连策略。理解这些知识点对于开发分布式系统、网络应用或者其他需要网络通信的项目都...
标题“socket客户端_socket_”表明我们将探讨的是如何创建和使用Socket客户端,而描述中的“之前已经上传服务端demo”提示我们已经有了Socket服务端的基础。 在Socket编程中,客户端是发起连接请求的一方,它需要...
Socket客户端是网络编程中的一个重要概念,它允许程序通过TCP或UDP协议与其他运行在网络上的程序进行通信。在本案例中,我们关注的是一个使用C#语言编写的Socket客户端代码,这通常用于实现客户端-服务器(C/S)架构...
Socket客户端在IT行业中是网络编程的一个重要组成部分,主要用于实现不同计算机之间的通信。Socket,也称为套接字,是操作系统提供的一种进程间通信机制,尤其在网络环境中,它为应用程序提供了发送和接收数据的能力...
在C#编程中,Socket是用于网络通信的基本组件,它提供了进程间通信(IPC)的能力,使得客户端和服务器能够相互通信。在这个“C# SOCKET 客户端与主机通讯多线程实现”主题中,我们将深入探讨如何利用C#的Socket类...
本示例中,我们关注的是一个基于C++和MFC(Microsoft Foundation Classes)实现的Socket客户端,该客户端已经过验证,可以与服务端进行有效通信。以下是关于这个主题的详细知识点: 1. **Socket基本概念**: - ...
在IT行业中,网络编程是不可或缺的一部分,而Socket编程则是实现网络通信的基础。本文将深入探讨如何使用Microsoft Visual C++(简称VC)来编制一个Socket客户端。Socket,也被称为套接字,是网络上的两个进程间通信...
SuperSocket写Socket客户端(连接,重连,接收处理数据)
4. 多连接处理:由于一个Socket只能处理一个连接,如果要同时处理多个客户端,可以使用线程或者异步编程模型来并发处理。 5. 服务端关闭:关闭监听Socket,释放资源。 五、队列处理 在高并发场景下,使用队列处理...
Socket客户端是计算机网络编程中的一个重要概念,主要用于实现应用程序间的通信。在本文中,我们将深入探讨Socket客户端的工作原理、如何向服务端发送数据以及接收服务端响应消息的过程。 首先,Socket是一种网络...
在VC++中,我们通常使用Winsock库来实现Socket功能,这个库提供了Windows平台下的Socket API。 ### 二、多线程技术 多线程是操作系统并发执行多个线程的能力。在Socket客户端应用中,多线程可以用于同时处理多个...
这个软件可以任意设置目标IP和端口实现连接相应的主机服务器,编写语言为java,安装时候需要有相应的java环境运行。
`SuperSocket` 是一个轻量级、高度可扩展的C#开发的网络通信框架,它简化了基于TCP协议的客户端和服务器应用程序的开发。这个完整Demo提供了从头开始构建一个简单的客户端和服务端应用程序的基础,特别适合初学者...
C#利用Socket实现客户端之间直接通信 实验功能: 设计程序,分别构建通信的两端:服务器端和客户端应用程序,套接字类型为面向连接的Socket,自己构建双方的应答模式,实现双方的数据的发送和接收(S发给C,C发给S)...
Socket客户端是网络编程中一种常见的工具,主要用于在开发过程中进行通信调试。Socket,又称为套接字,是网络层面上应用程序之间进行数据交换的一种接口。它允许程序通过Internet或局域网进行通信,实现了进程间的...
在Linux操作系统中,Socket编程...总之,Linux下的Socket多进程多客户端通信涉及到了进程创建、并发处理、同步机制、Socket通信以及进程间通信等多个方面。理解并掌握这些知识点,对于构建高效稳定的网络服务至关重要。
3. **命令**: 命令是SuperSocket处理客户端请求的方式,每个命令通常对应一个特定的操作或业务逻辑。 4. **事件驱动**: 超级套接字基于.NET的事件模型,例如`NewConnect`事件会在新的客户端连接建立时触发,`Receive...
本主题将深入探讨“服务器和客户端进程的简单SOCKET通讯”,帮助理解整个连接过程。 1. **Socket基础概念** - **Socket**:Socket在操作系统层面是一个数据接口,用于两个网络应用程序之间进行双向通信。它可以...
在IT领域,网络通信是应用程序之间进行数据交换的关键技术,而C#中的Socket编程则提供了实现这一功能的基础。本文将详细解析标题和描述中提到的"C# Socket客户端服务端封装,支持多连接处理,Tasks多线程,队列处理...
服务器客户端-socket(进程线程),包括套接字,多线程,多进程,单进程,并发,互斥锁,tcp/ip,udp等