Linux下socket異步通訊聊天程序

wuxiude 13年前發布 | 4K 次閱讀 CodeIgniter

<H1 class=entry-title>Linux下socket異步通訊聊天程序(轉)</H1>

<P class=entry-title>original from: http://yangqi.org/linux-socket-asynchronous-im-system/</A></P>

<DIV class=entry-meta>Posted by yangqi @ <SPAN class=entry-date>2010年02月17日 [Wed] 22:37</SPAN></DIV>

<DIV class=entry-content>

網絡課的project 1能用到的資料,程序結構比較清晰,轉來學習一下

什么是異步通訊?
就是通訊任意一方可以任意發送消息,有消息來到時會收到系統提示去接收消息。

這里要用到select函數。使用步驟如下:
1、設置一個集合變量,用來存放所有要判斷的句柄(file descriptors:即我們建立的每個socket、用open打開的每個文件等)
2、把需要判斷的句柄加入到集合里
3、設置判斷時間
4、開始等待,即select
5、如果在設定的時間內有任何句柄狀態變化了就馬上返回,并把句柄設置到集合里

服務器端源代碼如下:

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
#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;
}

客戶端源代碼如下:

 
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#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;
}