本文的聊天工具,理论上来说是没有具体区分服务端和客户端的(只针对此处的点对点聊天工具,例如QQ,微信这种IM工具是有服务器端的,要不然咋会出现服务器登不上去的故障呢?),另外服务端和客户端除了socket连线地方略有不同之外(客户端主动连接服务器,服务端被动接受连接),其他的基本都是一致的,所以服务端和客户端融合就是很有必要的了,废话不多说,具体代码如下:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/select.h>
#include <stdlib.h>
#include <time.h>
#define MAXLINE 4096
#define ELELEN 100
#define SERV_PORT 5000
#define LISTENQ 5
struct im_msg {
char time[ELELEN];
char name[ELELEN];
char data[MAXLINE];
} *imsg;
void get_cur_time(char *time_buf) {
time_t ticks;
char *tmp = NULL;
char time_tmp[100];
size_t len;
memset(time_tmp, 0x00, 100);
ticks = time(NULL);
tmp = ctime(&ticks);
strcat(time_tmp, "[");
strcat(time_tmp, tmp);
len = strlen(time_tmp);
time_tmp[len - 1] = 0;
strcat(time_tmp, "]");
strcpy(time_buf, time_tmp);
}
int main(int argc, char **argv)
{
int listenfd, connfd, maxfd;
struct sockaddr_in cliaddr, servaddr;
socklen_t clilen;
char recvline[MAXLINE], sendline[MAXLINE];
ssize_t nread;
const char *quit_code = "quit\n";
const char *server_name = "server: ";
const char *client_name = "client: ";
char msg_name[20];
fd_set rset;
if (argc == 1) {
printf("#####this is im server#####\n");
strcpy(msg_name, server_name);
} else if (argc == 2) {
printf("#####this is im client#####\n");
strcpy(msg_name, client_name);
} else {
printf("server: ./imtool\n");
printf("client: ./imtool <IPaddress>\n");
return -1;
}
if (argc == 1) {
if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket");
return -1;
}
memset(&servaddr, 0x00, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
perror("bind");
return -1;
}
if (listen(listenfd, LISTENQ) < 0) {
perror("listen");
return -1;
}
clilen = sizeof(cliaddr);
if ((connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen)) < 0) {
perror("accept");
}
maxfd = connfd > fileno(stdin) ? connfd + 1 : fileno(stdin) + 1;
imsg = (struct im_msg *)malloc(sizeof(struct im_msg));
} else {
if ((connfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket");
return -1;
}
memset(&servaddr, 0x00, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERV_PORT);
if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) < 0) {
perror("inet_pton");
return -1;
}
if (connect(connfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
perror("connect");
return -1;
}
}
maxfd = connfd > fileno(stdin) ? connfd + 1 : fileno(stdin) + 1;
imsg = (struct im_msg *)malloc(sizeof(struct im_msg));
for(;;) {
FD_ZERO(&rset);
FD_SET(fileno(stdin), &rset);
FD_SET(connfd, &rset);
memset(imsg, 0x00, sizeof(struct im_msg));
if (select(maxfd, &rset, NULL, NULL, NULL) < 0) {
perror("select");
continue;
}
if (FD_ISSET(connfd, &rset)) {
memset(recvline, 0x00, MAXLINE);
nread = read(connfd, imsg, MAXLINE);
if (nread <= 0) {
perror("read from connfd");
continue;
} else {
if (strcasecmp(imsg->data, quit_code) == 0) {
printf("recv quit, im will exit\n");
break;
} else {
strcat(recvline, imsg->time);
strcat(recvline, imsg->name);
strcat(recvline, imsg->data);
if (write(fileno(stdout), recvline, sizeof(struct im_msg)) < 0) {
perror("write to stdout");
continue;
}
}
}
}
if (FD_ISSET(fileno(stdin), &rset)) {
memset(sendline, 0x00, MAXLINE);
nread = read(fileno(stdin), imsg->data, MAXLINE);
strcpy(imsg->name, msg_name);
get_cur_time(imsg->time);
if (nread < 0) {
perror("read from stdin");
continue;
} else if (nread == 0) {
printf("read EOF, server will exit\n");
break;
} else {
if (strcasecmp(imsg->data, quit_code) == 0) {
printf("input quit, im will exit\n");
break;
} else {
if (write(connfd, imsg, sizeof(struct im_msg)) < 0) {
perror("write to connfd");
continue;
}
}
}
}
}
memset(imsg, 0x00, sizeof(struct im_msg));
get_cur_time(imsg->time);
strcpy(imsg->name, msg_name);
strcpy(imsg->data, quit_code);
write(connfd, imsg, sizeof(struct im_msg));
return 0;
}