此例子将利用上一篇介绍的套接字基础概念实现一个最基本的流式套接字客户端/服务器通信程序。在该程序中,客户端和服务器将按照如下步骤交互:
1)客户端向服务器发出日期请求字符串,如%D%Y%A%T等
2)服务器从网络接收到日期时间请求字符串后,根据字符串格式生成对应的日期时间值返回给客户端
为了简化程序美图出套接字变成的关键内容,该实例略去了对请求字符串进行合法的校验的处理。
服务器端程序:
#include<iostream>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<time.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
using namespace std;
#define BUFSIZE 512
int main(int argc, char *argv[])
{
int sockfd; /*服务器套接字*/
int new_fd; /*服务器链接套接字 */
struct sockaddr_in server_addr; /*服务器监听套接字*/
struct sockaddr_in client_addr; /*客户端IP地址*/
socklen_t size;
int portnum;
char reqBuf[BUFSIZE]; /* 应用接收缓存 */
char dtfmt[BUFSIZE]; /*日期-时间结果字符串*/
time_t td;
struct tm tm;
int z;
if(argc != 2)
{
cout<<"Usage:"<<argv[0]<<" portnumber\n";
exit(1);
}
if((portnum = atoi(argv[1])) < 0)
{
cout<<"Usage:"<<argv[0]<<" portnumber\n";
exit(1);
}
/* 创建服务器监听套接字 */
if((sockfd = socket(PF_INET,SOCK_STREAM,0)) == -1)
{
cout<<"Socket error!"<<endl;;
exit(1);
}
/* 为监听套接字准备IP地址和端口 */
memset(&server_addr,0,sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(portnum);
/* 绑定套接字到指定地址和端口 */
if((bind(sockfd,(struct sockaddr *)(&server_addr),sizeof(server_addr))) == -1)
{
cout<<"Bind error"<<endl;
exit(1);
}
/* 监听,backlog默认128 */
if(listen(sockfd,128) == -1)
{
cout<<"Listen Error"<<endl;
exit(1);
}
cout<<"waiting for the client's request...\n";
while(1)
{
size = sizeof(struct sockaddr_in);
/* 接收一个客户端连接并创建服务器连接套接字 */
if((new_fd = accept(sockfd,(struct sockaddr*)(&client_addr),&size)) == -1)
{
cout<<"Accept Error"<<endl;
exit(1);
}
cout<<"The Server got connection from "<<inet_ntoa(client_addr.sin_addr)<<endl;
for(;;)
{
/*读取客户端发来的日期时间请求,若客户端没有发送请求,
*则服务器将阻塞
*/
z = read(new_fd,reqBuf,sizeof(reqBuf));
if(z < 0)
{
cout<<"Read Error"<<endl;
exit(1);
}
/*服务器检查客户端是否关闭了套接字,此时read操作
*返回0(EOF),如果客户端关闭了其套接字,则服务器
*将执行close结束此连接,然后开始接收下一个客户端
*的连接请求。
*/
if(z == 0)
{
close(new_fd);
break;
}
/*
*向请求连接字符串尾添加NULL字符构成完整的请求日期
*时间字符串
*/
reqBuf[z] = 0;
/*
*获得服务器当前日期和时间
*/
time(&td);
tm = *localtime(&td);
/*
*根据请求日期字符串的格式字符串生成应答字符串
*/
strftime(dtfmt,sizeof(dtfmt),reqBuf,&tm);
/*将格式化结果发送给客户端*/
z = write(new_fd,dtfmt,strlen(dtfmt));
if(z < 0)
{
cout<<"Send failure"<<endl;
exit(1);
}
}
}
return 0;
}
客户端程序:
#include<iostream> #include<stdio.h> #include<stdlib.h> #include<string.h> #include<sys/types.h> #include<sys/socket.h> #include<netdb.h> #include<arpa/inet.h> #define BUFSIZE 512 using namespace std; int main(int argc,char *argv[]) { int sockfd; char buf[BUFSIZE]; struct sockaddr_in server_addr; struct hostent *host; int portnumber; int nbytes; int z; char reqBuf[BUFSIZE]; if(argc != 3) { cout<<"Usage: "<<argv[0]<<" hostname portnumber\n"; exit(1); } if((host = gethostbyname(argv[1])) == NULL) { cout<<"Get the Hostname Error\n"; exit(1); } if((portnumber = atoi(argv[2])) < 0) { cout<<"Usage: "<<argv[0]<<" hostname portnumber\n"; exit(1); } /* 创建客户端套接字 */ if((sockfd = socket(PF_INET,SOCK_STREAM,0)) == -1) { cout<<"Socket Error\n"; exit(1); } /* 创建服务器地址 */ memset(&server_addr,0,sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(portnumber); server_addr.sin_addr = *((struct in_addr*) host->h_addr); /*链接服务器*/ if(connect(sockfd,(struct sockaddr *)(&server_addr),sizeof(server_addr)) == -1) { cout<<"Connect Error\n"; exit(1); } cout<<"connect to server "<<inet_ntoa(server_addr.sin_addr)<<endl; /* *客户端主循环,输入“quit” 退出 */ for(;;) { /* 提示输入日期时间请求格式字符串 */ cout<<"Enter format string (^D or 'quit' to exit): "; if(!(cin>>reqBuf)) { break; } /* *为日期时间请求格式字符串添加NULL字符作为结尾, *另外同时去掉末尾的换行符号 */ z = strlen(reqBuf); if(z > 0 && reqBuf[--z] == '\n') reqBuf[z] = 0; if(z == 0) continue; /* *输入‘quit’退出 */ if(!strcasecmp(reqBuf,"QUIT")) { cout<<"press any key to end client.\n"; getchar(); break; } /* *发送日期时间请求字符串到服务器,注意请求信息中去掉了NULL字符 */ z = write(sockfd,reqBuf,strlen(reqBuf)); if(z < 0) { cout<<"Write Error\n"; exit(1); } /* *从客户端套接字中读取服务器发回的应答 */ if((nbytes = read(sockfd,buf,sizeof(buf))) == -1) { cout<<"Read Error\n"; exit(1); } /* *若服务器忧郁某种原因关闭了连接,则客户端需要处理此事件 */ if(nbytes == 0) { cout<<"server has closed the socket.\n"; cout<<"press any key to exit...\n"; exit(1); } buf[nbytes] = '\0'; /* *输出日期时间结果 */ cout<<"result form "<<inet_ntoa(server_addr.sin_addr)<<" port "<<(unsigned)ntohs(server_addr.sin_port)<<" : \n"<<buf<<endl; } close(sockfd); return 0; }
服务器命令: $./srvsingle 9000 waiting for the client's request... The Server got connection from 115.156.202.166 客户端命令: $./ClientSingle 115.156.202.166 9000 Enter format string (^D or 'quit' to exit): %D%Y%A%T result form 115.156.202.166 port 9000 : 04/17/132013Wednesday10:14:56