实现一个基本的流式套接字客户端/服务器通信程序,客户端和服务器按如下步骤交互:
(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, (
struct
sockaddr
*)&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
;}