socket实现简易echo time等功能

13 篇文章 0 订阅
3 篇文章 0 订阅

学了APUE里的套接字,敲了实验楼里的epoll例子。却刚意识到从未用POSIX socket API来自己真正写个C/S端。
例子也就参照老掉牙的echo,time:客户端发送cmd,服务端根据收到的cmd字符串执行相应的获取时间、回显等功能。

C/S编程模型:


先上服务端代码:

#include <sys/socket.h>	//socket
#include <sys/types.h>	//setsockopt
#include <unistd.h>	//close
#include <netinet/in.h>	//sockaddr_in
#include <arpa/inet.h>	//inet_addr
#include <stdio.h>	//perror
#include <stdlib.h>	//perror
#include <string.h>	//strlen,strncmp,strcmp
#include <time.h>	//time


#define SERV_PORT 8888
#define SERV_ADDR "127.0.0.1"
#define ERROR(par) {perror((par));exit(-1);}
#define MAXLEN 128

char recvmesg[MAXLEN];
char sendmesg[MAXLEN];

int main(){
	memset(recvmesg,0,sizeof(recvmesg));
	memset(sendmesg,0,sizeof(sendmesg));
	sockaddr_in skadd_in;
	skadd_in.sin_family=PF_INET;
	skadd_in.sin_port=htons(SERV_PORT);
	skadd_in.sin_addr.s_addr=inet_addr(SERV_ADDR);
	int sockfd=socket(PF_INET,SOCK_STREAM,0);
	if(sockfd<0)ERROR("socket err");
	//set socket option to avoid err
	int on=1;
	if((setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)))<0)ERROR("setsocketopt err");

	if(bind(sockfd,(sockaddr*)&skadd_in,sizeof(skadd_in))<0)ERROR("bind err");
	if(listen(sockfd,5)<0)ERROR("listen err");
	int confd;
	if((confd=accept(sockfd,NULL,NULL)) <0)ERROR("accept err");
	printf("accept ok!!\n");	
	while(1){
			if(recv(confd,recvmesg,sizeof(recvmesg),0)<=0)ERROR("recv err");
			printf("recv ok!!\n");	
			if(0==strncmp(recvmesg,"echo",strlen("echo"))){
				printf("recv cmd: echo\n");
				do{
					memset(recvmesg,0,sizeof(recvmesg));
					if(recv(confd,recvmesg,sizeof(recvmesg),0)<=0)ERROR("recv err");
					printf("echo mode:cur recvmesg is %s!!\n",recvmesg);
					memcpy(sendmesg,recvmesg,sizeof(recvmesg)+1);
					send(confd,sendmesg,strlen(sendmesg),0);
					memset(sendmesg,0,sizeof(sendmesg));
				}while(0!=strncmp(recvmesg,"eof",strlen("eof")));
			}
			else if(0==strncmp(recvmesg,"time",strlen("time"))){
				printf("recv cmd: time\n");
				time_t timenow;
				time(&timenow);
				memcpy(sendmesg,ctime(&timenow),sizeof(ctime(&timenow)+1));
				send(confd,sendmesg,strlen(sendmesg),0);

			}
			else if(0==strncmp(recvmesg,"quit",strlen("quit"))){
				memcpy(sendmesg,"server closed",sizeof("server closed")+1);
				send(confd,sendmesg,strlen(sendmesg),0);
				break;
			}else{
				printf("recv cmd:others\n");
				memcpy(sendmesg,"cmd invaild",sizeof("cmd invaild")+1);
				send(confd,sendmesg,strlen(sendmesg),0);
			}
	}
	close(sockfd);

	return 0;
}

然后是客户端:

#include <sys/socket.h>	//socket
#include <unistd.h>	//close
#include <netinet/in.h>	//sockaddr_in
#include <arpa/inet.h>	//inet_addr
#include <stdio.h> //perror
#include <stdlib.h> //perror
#include <string.h> //strlen,strncmp,strcmp

#define SERVERIP "127.0.0.1"
#define SERVERPORT 8888
#define ERROR(par) {perror((par));exit(-1);}
#define MAXLEN 128

char message[MAXLEN];

int main()
{
	memset(message,0,sizeof(message));
	sockaddr_in servaddr;
	servaddr.sin_family=PF_INET;
	servaddr.sin_port=htons(SERVERPORT);
	servaddr.sin_addr.s_addr=inet_addr(SERVERIP);

	int sockfd=socket(PF_INET,SOCK_STREAM,0);
	if(sockfd<0)ERROR("socket err");
	if(connect(sockfd,(sockaddr*)&servaddr,sizeof(servaddr))<0)ERROR("connect err");
	while(1){
		printf("enter your cmd(echo/time/quit): ");
		memset(message,0,sizeof(message));
		fgets(message,MAXLEN,stdin);
		send(sockfd,message,strlen(message),0);
		if(0==strncmp(message,"echo",strlen("echo"))){
			do{
				memset(message,0,sizeof(message));
				fgets(message,MAXLEN,stdin);
				send(sockfd,message,strlen(message),0);
				memset(message,0,sizeof(message));
				if(recv(sockfd,message,MAXLEN,0)<0)ERROR("recv err");
				printf("%s\n",message);
			}while(0!=strncmp(message,"eof",strlen("eof")));
		}
		else{
			if(recv(sockfd,message,MAXLEN,0)<0)ERROR("recv err");
			printf("%s\n",message);
			if(0==strncmp(message,"server closed",strlen("server closed"))){
				break;
			}

		}
	}
	close(sockfd);
	return 0;
}

注意点:

1.同一机器上实现客户端和服务端,使用回送地址(本机地址)作为服务端IP。

2.使用setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on))设置套接字选项。一般一端口释放后会等待两分钟之后才能再被使用,SO_REUSEADDR让端口释放后立即可用。SO_REUSEADDR用于对TCP套接字处于TIME_WAIT状态下的socket,允许重复绑定使用。server程序总是应该在调用bind()之前设置SO_REUSEADDR套接字选项。

3.“<”运算符优先级高于“=”,对accpet返回值赋值需要加括号。否则recv时会造成错误。

4.使用recv,send进行buf操作后需要对buf清零。memset和bzero皆可。


效果:


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值