Linux下socket异步通讯聊天程序(转)
网络课的project 1能用到的资料,程序结构比较清晰,转来学习一下
什么是异步通讯?
就是通讯任意一方可以任意发送消息,有消息来到时会收到系统提示去接收消息。
这里要用到select函数。使用步骤如下:
1、设置一个集合变量,用来存放所有要判断的句柄(file descriptors:即我们建立的每个socket、用open打开的每个文件等)
2、把需要判断的句柄加入到集合里
3、设置判断时间
4、开始等待,即select
5、如果在设定的时间内有任何句柄状态变化了就马上返回,并把句柄设置到集合里
服务器端源代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <sys/types.h>
?#define MAXBUF 1024
/************关于本文档********************************************
*filename: async-server.c
*purpose: 演示网络异步通讯,这是服务器端程序
*wrote by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
*date time:2007-01-25 21:22
*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
* 但请遵循GPL
*Thanks to: Google.com
*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
* 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!
*********************************************************************/
?int
main(
int
argc,
char
**argv)
{
????
int
sockfd, new_fd;
????
socklen_t len;
????
struct
sockaddr_in my_addr, their_addr;
????
unsigned
int
myport, lisnum;
????
char
buf[MAXBUF + 1];
????
fd_set rfds;
????
struct
timeval tv;
????
int
retval, maxfd = -1;
?????
if
(argv[1])
????????
myport =
atoi
(argv[1]);
????
else
????????
myport = 7838;
?????
if
(argv[2])
????????
lisnum =
atoi
(argv[2]);
????
else
????????
lisnum = 2;
?????
if
((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
????????
perror
(
"socket"
);
????????
exit
(1);
????
}
?????
bzero(&my_addr,
sizeof
(my_addr));
????
my_addr.sin_family = PF_INET;
????
my_addr.sin_port = htons(myport);
????
if
(argv[3])
????????
my_addr.sin_addr.s_addr = inet_addr(argv[3]);
????
else
????????
my_addr.sin_addr.s_addr = INADDR_ANY;
?????
if
(bind(sockfd, (
struct
sockaddr *) &my_addr,
sizeof
(
struct
sockaddr))
????????
== -1) {
????????
perror
(
"bind"
);
????????
exit
(1);
????
}
?????
if
(listen(sockfd, lisnum) == -1) {
????????
perror
(
"listen"
);
????????
exit
(1);
????
}
?????
while
(1) {
????????
printf
????????????
(
"\n----等待新的连接到来开始新一轮聊天……\n"
);
????????
len =
sizeof
(
struct
sockaddr);
????????
if
((new_fd =
?????????????
accept(sockfd, (
struct
sockaddr *) &their_addr,
????????????????????
&len)) == -1) {
????????????
perror
(
"accept"
);
????????????
exit
(
errno
);
????????
}
else
????????????
printf
(
"server: got connection from %s, port %d, socket %d\n"
,
???????????????????
inet_ntoa(their_addr.sin_addr),
???????????????????
ntohs(their_addr.sin_port), new_fd);
?????????
/* 开始处理每个新连接上的数据收发 */
????????
printf
????????????
(
"\n准备就绪,可以开始聊天了……直接输入消息回车即可发信息给对方\n"
);
????????
while
(1) {
????????????
/* 把集合清空 */
????????????
FD_ZERO(&rfds);
????????????
/* 把标准输入句柄0加入到集合中 */
????????????
FD_SET(0, &rfds);
????????????
maxfd = 0;
????????????
/* 把当前连接句柄new_fd加入到集合中 */
????????????
FD_SET(new_fd, &rfds);
????????????
if
(new_fd > maxfd)
????????????????
maxfd = new_fd;
????????????
/* 设置最大等待时间 */
????????????
tv.tv_sec = 1;
????????????
tv.tv_usec = 0;
????????????
/* 开始等待 */
????????????
retval = select(maxfd + 1, &rfds, NULL, NULL, &tv);
????????????
if
(retval == -1) {
????????????????
printf
(
"将退出,select出错! %s"
,
strerror
(
errno
));
????????????????
break
;
????????????
}
else
if
(retval == 0) {
????????????????
/* printf
???????????????????
("没有任何消息到来,用户也没有按键,继续等待……\n"); */
????????????????
continue
;
????????????
}
else
{
????????????????
if
(FD_ISSET(0, &rfds)) {
????????????????????
/* 用户按键了,则读取用户输入的内容发送出去 */
????????????????????
bzero(buf, MAXBUF + 1);
????????????????????
fgets
(buf, MAXBUF, stdin);
????????????????????
if
(!strncasecmp(buf,
"quit"
, 4)) {
????????????????????????
printf
(
"自己请求终止聊天!\n"
);
????????????????????????
break
;
????????????????????
}
????????????????????
len = send(new_fd, buf,
strlen
(buf) - 1, 0);
????????????????????
if
(len > 0)
????????????????????????
printf
????????????????????????????
(
"消息:%s\t发送成功,共发送了%d个字节!\n"
,
?????????????????????????????
buf, len);
????????????????????
else
{
????????????????????????
printf
????????????????????????????
(
"消息'%s'发送失败!错误代码是%d,错误信息是'%s'\n"
,
?????????????????????????????
buf,
errno
,
strerror
(
errno
));
????????????????????????
break
;
????????????????????
}
????????????????
}
????????????????
if
(FD_ISSET(new_fd, &rfds)) {
????????????????????
/* 当前连接的socket上有消息到来则接收对方发过来的消息并显示 */
????????????????????
bzero(buf, MAXBUF + 1);
????????????????????
/* 接收客户端的消息 */
????????????????????
len = recv(new_fd, buf, MAXBUF, 0);
????????????????????
if
(len > 0)
????????????????????????
printf
????????????????????????????
(
"接收消息成功:'%s',共%d个字节的数据\n"
,
?????????????????????????????
buf, len);
????????????????????
else
{
????????????????????????
if
(len < 0)
????????????????????????????
printf
????????????????????????????????
(
"消息接收失败!错误代码是%d,错误信息是'%s'\n"
,
?????????????????????????????????
errno
,
strerror
(
errno
));
????????????????????????
else
????????????????????????????
printf
(
"对方退出了,聊天终止\n"
);
????????????????????????
break
;
????????????????????
}
????????????????
}
????????????
}
????????
}
????????
close(new_fd);
????????
/* 处理每个新连接上的数据收发结束 */
????????
printf
(
"还要和其它连接聊天吗?(no->退出)"
);
????????
fflush
(stdout);
????????
bzero(buf, MAXBUF + 1);
????????
fgets
(buf, MAXBUF, stdin);
????????
if
(!strncasecmp(buf,
"no"
, 2)) {
????????????
printf
(
"终止聊天!\n"
);
????????????
break
;
????????
}
????
}
?????
close(sockfd);
????
return
0;
}
客户端源代码如下:
?001002003004005006007008009010011012013014015016017018019020021022023024025026027028029030031032033034035036037038039040041042043044045046047048049050051052053054055056057058059060061062063064065066067068069070071072073074075076077078079080081082083084085086087088089090091092093094095096097098099100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <resolv.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
?#define MAXBUF 1024
/************关于本文档********************************************
// *filename: ssync-client.c
*purpose: 演示网络异步通讯,这是客户端程序
*wrote by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
*date time:2007-01-25 21:32
*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
* 但请遵循GPL
*Thanks to: Google.com
*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
* 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!
*********************************************************************/
int
main(
int
argc,
char
**argv)
{
????
int
sockfd, len;
????
struct
sockaddr_in dest;
????
char
buffer[MAXBUF + 1];
????
fd_set rfds;
????
struct
timeval tv;
????
int
retval, maxfd = -1;
?????
if
(argc != 3) {
????????
printf
????????????
(
"参数格式错误!正确用法如下:\n\t\t%s IP地址 端口\n\t比如:\t%s 127.0.0.1 80\n此程序用来从某个 IP 地址的服务器某个端口接收最多 MAXBUF 个字节的消息"
,
?????????????
argv[0], argv[0]);
????????
exit
(0);
????
}
????
/* 创建一个 socket 用于 tcp 通信 */
????
if
((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
????????
perror
(
"Socket"
);
????????
exit
(
errno
);
????
}
?????
/* 初始化服务器端(对方)的地址和端口信息 */
????
bzero(&dest,
sizeof
(dest));
????
dest.sin_family = AF_INET;
????
dest.sin_port = htons(
atoi
(argv[2]));
????
if
(inet_aton(argv[1], (
struct
in_addr *) &dest.sin_addr.s_addr) == 0) {
????????
perror
(argv[1]);
????????
exit
(
errno
);
????
}
?????
/* 连接服务器 */
????
if
(connect(sockfd, (
struct
sockaddr *) &dest,
sizeof
(dest)) != 0) {
????????
perror
(
"Connect "
);
????????
exit
(
errno
);
????
}
?????
printf
????????
(
"\n准备就绪,可以开始聊天了……直接输入消息回车即可发信息给对方\n"
);
????
while
(1) {
????????
/* 把集合清空 */
????????
FD_ZERO(&rfds);
????????
/* 把标准输入句柄0加入到集合中 */
????????
FD_SET(0, &rfds);
????????
maxfd = 0;
????????
/* 把当前连接句柄sockfd加入到集合中 */
????????
FD_SET(sockfd, &rfds);
????????
if
(sockfd > maxfd)
????????????
maxfd = sockfd;
????????
/* 设置最大等待时间 */
????????
tv.tv_sec = 1;
????????
tv.tv_usec = 0;
????????
/* 开始等待 */
????????
retval = select(maxfd + 1, &rfds, NULL, NULL, &tv);
????????
if
(retval == -1) {
????????????
printf
(
"将退出,select出错! %s"
,
strerror
(
errno
));
????????????
break
;
????????
}
else
if
(retval == 0) {
????????????
/* printf
???????????????
("没有任何消息到来,用户也没有按键,继续等待……\n"); */
????????????
continue
;
????????
}
else
{
????????????
if
(FD_ISSET(sockfd, &rfds)) {
????????????????
/* 连接的socket上有消息到来则接收对方发过来的消息并显示 */
????????????????
bzero(buffer, MAXBUF + 1);
????????????????
/* 接收对方发过来的消息,最多接收 MAXBUF 个字节 */
????????????????
len = recv(sockfd, buffer, MAXBUF, 0);
????????????????
if
(len > 0)
????????????????????
printf
????????????????????????
(
"接收消息成功:'%s',共%d个字节的数据\n"
,
?????????????????????????
buffer, len);
????????????????
else
{
????????????????????
if
(len < 0)
????????????????????????
printf
????????????????????????????
(
"消息接收失败!错误代码是%d,错误信息是'%s'\n"
,
?????????????????????????????
errno
,
strerror
(
errno
));
????????????????????
else
????????????????????????
printf
(
"对方退出了,聊天终止!\n"
);
????????????????????
break
;
????????????????
}
????????????
}
????????????
if
(FD_ISSET(0, &rfds)) {
????????????????
/* 用户按键了,则读取用户输入的内容发送出去 */
????????????????
bzero(buffer, MAXBUF + 1);
????????????????
fgets
(buffer, MAXBUF, stdin);
????????????????
if
(!strncasecmp(buffer,
"quit"
, 4)) {
????????????????????
printf
(
"自己请求终止聊天!\n"
);
????????????????????
break
;
????????????????
}
????????????????
/* 发消息给服务器 */
????????????????
len = send(sockfd, buffer,
strlen
(buffer) - 1, 0);
????????????????
if
(len < 0) {
????????????????????
printf
????????????????????????
(
"消息'%s'发送失败!错误代码是%d,错误信息是'%s'\n"
,
?????????????????????????
buffer,
errno
,
strerror
(
errno
));
????????????????????
break
;
????????????????
}
else
????????????????????
printf
????????????????????????
(
"消息:%s\t发送成功,共发送了%d个字节!\n"
,
?????????????????????????
buffer, len);
????????????
}
????????
}
????
}
????
/* 关闭连接 */
????
close(sockfd);
????
return
0;
}
编译用如下命令:
gcc -Wall async-server.c -o server
gcc -Wall async-client.c -o client
运行用如下命令:
./server 7838 1
./client 127.0.0.1 7838