实现一个基本的流式套接字客户端/服务器通信程序,客户端和服务器按如下步骤交互:
(1)客户端向服务器发出日期时间请求字符串,如:%D %Y %A %T等。
(2)服务器从网络接收到日期时间请求字符串后,根据字符串格式生成对应的日期时间值返回给客户端。
-
//TCP服务器
/*
用法:./server ip port
说明:该流式套接字服务器程序工作于多线程模式,根据客户端发来的请求格式字符串,服务器回应对应的日期和时间
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <pthread.h>
#define BUFSIZE 1024
#define BACKLOG 128
static void bail(const char *on_what)
{
fputs(strerror(errno),stderr);
fputs(": ",stderr);
fputs(on_what, stderr);
fputc('\n',stderr);
exit(1);
}
time_t td;
struct tm tm;
struct TmpSockt
{
int connfd;
sockaddr_in client;
};
void *start_routine(void *arg)
{
char reqBuf[BUFSIZE];
char dtfmt[BUFSIZE];
struct TmpSockt *temp=(TmpSockt*)arg;
while (1)
{
//读取客户端发来的日期时间请求,若客户端没有发送请求,则服务器将阻塞
long bytes=read(temp->connfd, reqBuf,sizeof(reqBuf));
if(bytes<0)
bail("read()");
//服务器检查客户端是否关闭了套接字,此时read操作返回0(EOF),
//如果客户端关闭了其套接字,则服务器将执行close结束此连接,然后开始接收下一个客户端的连接请求
if(bytes==0)
{
printf("The %ld thread exit...\n",(long)pthread_self());
close(temp->connfd);
delete temp;
pthread_exit(NULL);
break;
}
//向请求字符串尾添加NULL字符构成完整的请求日期时间字符串
reqBuf[bytes]=0;
//获取服务器当前日期和时间
time(&td);
tm=*localtime(&td);
//根据请求日期字符串的格式串生成应答字符串,不清楚的可以查一下这个函数
strftime(dtfmt, sizeof(dtfmt), reqBuf, &tm);
//将格式化结果发送给客户端
bytes=write(temp->connfd, dtfmt,strlen(dtfmt));
if (bytes<0)
bail("write()");
}
return NULL;
}
int main(int argc,char **argv)
{
int sockfd,connectfd; //描述符
pthread_t thread;
TmpSockt *sockt;
int portnumber; //端口号
struct sockaddr_in server;//服务器地址信息
struct sockaddr_in client;//客户端地址信息
socklen_t sin_size;
if ((portnumber=atoi(argv[2]))==-1)
{
fprintf(stderr,"格式错误");
exit(1);
}
if ((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)
{
fprintf(stderr,"socket error %s",strerror(errno));
exit(1);
}
//设置套接字选项为SO_REUSEADDR
int opt=SO_REUSEADDR;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
bzero(&server, sizeof(server));
server.sin_family=AF_INET;
server.sin_port=htons(portnumber);
if (!inet_aton(argv[1], &server.sin_addr))
{
bail("bad address");
}
//绑定套接字到相应地址
if (bind(sockfd, (struct sockaddr*)&server,sizeof(server))==-1)
{
fprintf(stderr,"bind error:%s\a\n",strerror(errno));
exit(1);
}
//监听网络连接
if (listen(sockfd,BACKLOG)==-1)
{
fprintf(stderr,"Listen error:%s\a\n",strerror(errno));
exit(1);
}
sin_size=sizeof(struct sockaddr_in);
printf("waiting for the client's request...\n");
while (1)
{
if ((connectfd=accept(sockfd, (structsockaddr*)&client, &sin_size))==-1)
{
fprintf(stderr,"accept error:%s\a\n",strerror(errno));
exit(1);
}
fprintf(stdout,"Server got connection from %s\n",inet_ntoa(client.sin_addr));
sockt=new TmpSockt;
sockt->connfd=connectfd;
memcpy((void *)&sockt->client, &client,sizeof(client));
//产生线程,并执行线程函数,处理客服端请求
if (pthread_create(&thread,NULL,start_routine, (void*)sockt)==-1)
{
fprintf(stderr,"pthread_creat() error:%s\a\n",strerror(errno));
exit(1);
}
}
close(sockfd); //关闭监听
return 0;
}
客户端:
-
//TCP客户端
/*
用法:./client hostname port
说明:本程序使用TCP连接和TCP服务器通信,当连接建立后,向服务器发送如下格式字符串
格式字符串示例:
(1) %D
(2) %A %D %H:%M:%S
(3) %A
(4) %H:%M:%S
(5)...
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <time.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#define BUFSIZE 1024
#define backlog 128 //等待队列大小
static void bail(const char *on_what)
{
fputs(strerror(errno),stderr);
fputs(": ",stderr);
fputs(on_what, stderr);
fputc('\n',stderr);
exit(1);
}
int main(int argc,char *argv[])
{
int sockfd;//客户端套接字
char buf[BUFSIZE];
struct sockaddr_in server_addr;
int portnumber;
long nbytes;
long z;
char reqBuf[BUFSIZE];
if (argc!=3)
{
printf("输入格式错误\n");
exit(1);
}
if ((portnumber=atoi(argv[2]))<0)
{
fprintf(stderr,"Usage:%s hostname portnumber\a\n",argv[0]);
exit(1);
}
//创建客户端套接字
if ((sockfd=socket(PF_INET,SOCK_STREAM,0))==-1)
{
fprintf(stderr,"Socket error:%s\a\n",strerror(errno));
exit(1);
}
//创建服务器地址
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(portnumber);
if (!inet_aton(argv[1], &server_addr.sin_addr))
{
bail("bad address");
}
//连接服务器
if (connect(sockfd, (struct sockaddr*)(&server_addr),sizeof(server_addr))==-1)
{
fprintf(stderr,"connect error:%s\a\n",strerror(errno));
exit(1);
}
printf("connected to server %s\n",inet_ntoa(server_addr.sin_addr));
//客户端主循环输入 “quit”退出
for (; ; )
{
//提示输入日期请求格式字符串
fputs("\nEnter fotmat string(^D or 'quit' to exit):",stdout);
if (!fgets(reqBuf,sizeof(reqBuf),stdin))
{
printf("\n");
break;
}
//为日期时间请求字符串添加NULL字符作为结尾,另外同时去掉末尾的换行符
z=strlen(reqBuf);
if (z>0 && reqBuf[--z]=='\n')
reqBuf[z]=0;
if (z==0)//客户端仅键入Enter
continue;
//输入‘quit’退出
if(!strcasecmp(reqBuf,"QUIT"))//忽略大小写比较
{
printf("press any key to end client.\n");
getchar();
break;
}
//发送日期时间请求字符串到服务器,注意请求信息中去掉了NULL字符
z=write(sockfd, reqBuf, sizeof(reqBuf));
printf("client has sent '%s' to the sever\n",reqBuf);
if (z<0)
bail("write()");
//从客户端套接字中读取服务器发回的应答
if ((nbytes=read(sockfd,buf,sizeof(buf)))==-1)
{
fprintf(stderr,"read error:%s\n",strerror(errno));
exit(1);
}
//若服务器由于某种原因关闭了连接,则客户端需要处理此事件
if(nbytes==0)
{
printf("server hs closed the socket.\n");
printf("press any key to exit...\n");
getchar();
break;
}
buf[nbytes]='\0';
//输出日期时间结果
printf("result from %s port %u:\n\t'%s'\n",inet_ntoa(server_addr.sin_addr),(unsigned)ntohs(server_addr.sin_port),buf);
}
close(sockfd);
return 0;
}