本文章介绍一种基于Linux使用C语言实现简单的局域网聊天室程序的方法,支持消息群发,历史数据查询,好友列表查看,好友上线下线提醒等功能。聊天界面如下图所示:
下面将按步骤介绍该系统的设计实现,首先在linux下创建一个目录,在目录下按顺序创建几个文件,clientlist.c common.h sock.c main.c Makefile,如下图所示,创建完各文件之后,依次在文件里面新增相关的代码内容。
1.在Makefile文件里面添加如下内容:
CC=gcc
CPROG= chat
BIN = $(CPROG)
OBJS = main.o sock.o clientlist.o
LDFLAGS += -lpthread
CFLAGS = -I.
all: $(BIN)
install:$(BIN)
@true
uninstall:
rm -f $(OBJS) $(BIN)
clean:
rm -f $(OBJS) $(BIN)
$(BIN): $(OBJS)
$(CC) $(OBJS) $(CFLAGS) $(LDFLAGS) $(CFLAGS_EXTRA) -o $(BIN)
2.在main.c文件里面添加如下内容:
#include <stdio.h>
#include <string.h>
#include "common.h"
void main(int argc, char *argv[])
{
if(argc<2) {
LOG("input fmt err!\n");
return ;
}
if(!strcmp(argv[1], "s")) {
svr_start(SERVER_PORT);
}
else if(!strcmp(argv[1], "c")) {
cli_start(argv);
}
}
3.在common.h文件添加如下内容:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <pthread.h>
#include <netinet/tcp.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/queue.h>
#include <sys/ioctl.h>
#include <linux/if.h>
#include <semaphore.h>
#include <arpa/inet.h>
#include <net/if_arp.h>
#include <fcntl.h>
#define MAXCLI 10
#define SERVER_PORT 8080
#define USRNAME_LENGTH 32
#define RGST_INIT 0
#define RGST_OK 1
#define PKG_HEAD 'm'
#define MSG_MODE_LOGIN '0'
#define MSG_MODE_LOGIN_ACK '1'
#define MSG_MODE_SEND '2'
#define MSG_MODE_GET_LIST '3'
#define MSG_MODE_GET_LIST_ACK '4'
#define MSG_MODE_ONLINE '5'
#define MSG_MODE_OFFLINE '6'
#define HISTROY_FILE "h.txt"
struct client_chn_s {
int skfd;
char usrname[USRNAME_LENGTH];
CIRCLEQ_ENTRY(client_chn_s) link;
};
typedef struct client_chn_s client_chn_t;
typedef struct svr {
int listener;
unsigned short port;
}svr_t;
typedef struct cli {
int sockfd;
unsigned char stat;
unsigned char history_enable;
time_t login_timeout;
time_t reconn_timeout;
char usrname[USRNAME_LENGTH];
char ip[48];
unsigned short port;
}cli_t;
typedef struct msg_s {
unsigned char msgtyp;
unsigned char *payload;
int payloadlen;
unsigned char *username;
int username_len;
}msg_t;
4.在sock.c添加如下内容:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include <stdarg.h>
#include "common.h"
cli_t cli;
void LOG(const char *format,...)
{
va_list argptr;
char buffer[2048];
va_start(argptr,format);
vsprintf(buffer,format,argptr);
va_end(argptr);
//printf("%s", buffer);
}
/*
Hex打印
*/
void dump_buf(char *label, unsigned char *in, int len)
{
int i=0;
LOG("%s:\n", label);
for(i=0;i<len;i++) {
LOG("%02x ", in[i]&0xff);
if(0==(i+1)%16) {
LOG("\n");
}
}
LOG("\n");
}
void get_cur_time(char *timestamp)
{
time_t timep;
struct tm *p, pp;
time(&timep);
localtime_r(&timep, &pp);
p = &pp;
sprintf(timestamp,"<%04d-%02d-%02d %02d:%02d:%02d>",p->tm_year + 1900, p->tm_mon + 1, p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec);
}
void sk_history_record(char *msg)
{
FILE *fp = fopen(HISTROY_FILE, "ab+");
if(!fp) {
LOG("[%s]open %s fail", __func__, HISTROY_FILE);
return 0;
}
fprintf(fp, "%s\r\n", msg);
fclose(fp);
}
int sk_tcp_svr_init(int port)
{
int yes = 1;
struct sockaddr_in svr_addr;
int listenfd = -1;
if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
LOG("create socket() fail\n");
return -1;
}
if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
{
LOG("setsockopt() fail!\n");
exit(0);
return -1;
}
svr_addr.sin_family = AF_INET;
svr_addr.sin_addr.s_addr = INADDR_ANY;
svr_addr.sin_port = htons(port);
memset(&(svr_addr.sin_zero), '\0', 8);
if(bind(listenfd, (struct sockaddr *)&svr_addr, sizeof(svr_addr)) == -1)
{
LOG("bind fail!\n");
return -1;
}
if(listen(listenfd, MAXCLI) == -1)
{
LOG("listen fail\n");
return -1;
}
LOG("listen success!!!\n");
return listenfd;
}
int sk_start_connect(int *sockfd,char *ip,unsigned short port)
{
int i;
int iRet;
int status;
int times=3;
struct sockaddr_in host;
int err;
int errlen;
struct timeval timeout={3,0};
fd_set mask;
iRet=0;
status=-1;
*sockfd=-1;
if ((0==port) || 0==strlen(ip))
{
LOG("invalid param");
return 0;
}
host.sin_family=AF_INET;
host.sin_port=htons(port);
LOG("connect %s:%u\n",ip,port);
inet_aton(ip,&host.sin_addr);
if ( (*sockfd=socket(AF_INET,SOCK_STREAM,0))<0 )
{
LOG("create sock err");
iRet=0;
goto err_out;
}
iRet=connect(*sockfd,(struct sockaddr *)&host,sizeof(host));
if(iRet==0) {
LOG("connect succ\n");
if(fcntl(*sockfd,F_SETFL,O_NDELAY) < 0 )
{
LOG("set sock non_block error");
iRet=0;
LOG("fcntl err");
goto err_out;
}
return 1;
}
LOG("iRet:%d\n", iRet);
err_out:
return (iRet);
}
int sk_tcp_connect_init(char *svr, int port)
{
int iRet, sockfd = -1;
iRet=sk_start_connect(&sockfd,svr, port);
if (iRet == 0)
return -1;
return sockfd;
}
int sk_msg_pack(unsigned char *buf, msg_t *m)
{
int len = 0;
unsigned char msgtype = m->msgtyp;
buf[len++] = PKG_HEAD;
buf[len++] = msgtype;
switch(msgtype)
{
case MSG_MODE_LOGIN:
{
LOG("[%s][%d]\n",__FUNCTION__,__LINE__);
if(m->username_len<=USRNAME_LENGTH) {
buf[len++]=m->username_len/256;
buf[len++]=m->username_len;
memcpy(buf+len, m->username, m->username_len);
len+=m->username_len;
}
LOG("[%s][%d]\n",__FUNCTION__,__LINE__);
break;
}
case MSG_MODE_SEND:
{
char timestamp[48]="";
get_cur_time(timestamp);
LOG("[%s][%d]\n",__FUNCTION__,__LINE__);
unsigned char msgbuf[1024]="";
int msglen = sprintf(msgbuf,"%s%s:%s", m->username, timestamp,m->payload);
buf[len++]=msglen/256;
buf[len++]=msglen;
memcpy(buf+len, msgbuf, msglen);
len+=msglen;
break;
}
case MSG_MODE_GET_LIST_ACK:
case MSG_MODE_OFFLINE:
case MSG_MODE_ONLINE:
{
buf[len++]=m->payloadlen/256;
buf[len++]=m->payloadlen;
memcpy(buf+len, m->payload, m->payloadlen);
len+=m->payloadlen;
break;
}
}
return len;
}
void svr_msg_hander(int fd, unsigned char *in, int len)
{
unsigned char msgtyp = in[1];
switch(msgtyp)
{
case MSG_MODE_LOGIN:
{
char usr[USRNAME_LENGTH]="";
strcpy(usr, &in[4]);
client_chn_add_usr(fd, usr);
msg_t m;
m.msgtyp = MSG_MODE_LOGIN_ACK;
unsigned char ackbuf[64]="";
int acklen = sk_msg_pack(ackbuf, &m);
LOG("Send RGST Ack!\n");
send(fd, ackbuf, acklen, 0);
svr_online_offline_notify(fd,1);
break;
}
case MSG_MODE_SEND:
{
client_chn_msg_proc(0,in, len);
break;
}
case MSG_MODE_GET_LIST:
{
char usrlist[1024]="";
int ret=client_chn_get_usr_list(usrlist);
msg_t m;
m.msgtyp = MSG_MODE_GET_LIST_ACK;
m.payload = usrlist;
m.payloadlen = ret;
unsigned char ackbuf[1450]="";
int acklen = sk_msg_pack(ackbuf, &m);
dump_buf("Send friend list resuest Ack", ackbuf,acklen);
send(fd, ackbuf, acklen, 0);
break;
}
}
}
void svr_online_offline_notify(int fd, int isonline)
{
client_chn_t *chn = client_chn_get_elm(fd);
msg_t m;
if(isonline==1) {
m.msgtyp = MSG_MODE_ONLINE;
}
else {
m.msgtyp = MSG_MODE_OFFLINE;
}
char payload[128]="";
char timestamp[48]="";
get_cur_time(timestamp);
if(isonline==1) {
sprintf(payload, "%s%s Online!", timestamp, chn->usrname);
}
else {
sprintf(payload, "%s%s Quit!", timestamp, chn->usrname);
}
m.payload = payload;
m.payloadlen = strlen(payload);
unsigned char buf[1450]="";
int len = sk_msg_pack(buf, &m);
dump_buf("notify offline", buf,len);
client_chn_msg_proc(fd,buf, len);
}
int svr_recv_proc(int fd)
{
unsigned char recvbuf[1450]="";
int ret =recv(fd, recvbuf, sizeof(recvbuf), 0);
if(ret<=0) {
int close_flag = 0;
if(ret == 0)
{
/* connection closed */
LOG("socket %d close\n", fd);
close_flag = 1;
}
else {
if(errno!=EAGAIN) {
LOG("recv error!\n");
close_flag =1;
}
}
if(close_flag) {
svr_online_offline_notify(fd, 0);
return -1;
}
}
else {
LOG("[clisk:%d]recv %d bytes\n", fd, ret);
dump_buf("recv", recvbuf, ret);
//client_chn_msg_proc(fd, recvbuf, ret);
svr_msg_hander(fd, recvbuf, ret);
return ret;
}
return 0;
}
void svr_accept(svr_t *svr)
{
int newfd;
struct sockaddr_in clientaddr;
int addrlen = sizeof(clientaddr);
if((newfd = accept(svr->listener, (struct sockaddr *)&clientaddr, (socklen_t *)&addrlen)) == -1)
{
LOG("accept() error !\n");
}
else
{
LOG(" New connection from %s on socket %d\n", inet_ntoa(clientaddr.sin_addr), newfd);
client_chn_t *elm = (client_chn_t *)malloc(sizeof(client_chn_t));
elm->skfd = newfd;
client_chn_insert(elm);
}
}
int svr_add_fds(svr_t *svr, fd_set *master)
{
int i;
int fdmax = 0;
if(svr->listener>0) {
FD_SET(svr->listener, master);
if (svr->listener > fdmax) {
fdmax = svr->listener;
}
}
int max = client_chn_add_fds(master);
if(fdmax<max) fdmax = max;
return fdmax;
}
void svr_select_fd_proc(svr_t *svr, int fd)
{
if(svr->listener==fd) {
svr_accept(svr);
return;
}
else {
if(svr_recv_proc(fd)<0) {
client_chn_del(fd);
close(fd);
}
}
}
void svr_start(int port)
{
svr_t svr;
svr.port=port;
svr.listener=sk_tcp_svr_init(port);
struct timeval tv, timeo;
fd_set master;
client_chn_init();
while(1) {
tv.tv_sec=0;
tv.tv_usec=20000;
FD_ZERO(&master);
int fdmax = svr_add_fds(&svr, &master);
if(select(fdmax+1, &master, NULL, NULL, &tv)<0)
{
LOG("select err\n");
}
int i;
for(i = 0; i <= fdmax; i++){
if(FD_ISSET(i, &master)) {
LOG("[%s][%d]\n",__FUNCTION__,__LINE__);
svr_select_fd_proc(&svr,i);
}
}
}
}
void *cli_input_hander(void *arg)
{
while(1) {
char msgbuf[1024]="";
//scanf("%s", msgbuf);
gets(msgbuf);
if((cli.sockfd>0)&&(cli.stat==RGST_OK)) {
if((strlen(msgbuf)==2)&&(msgbuf[0]=='-')) {
switch(msgbuf[1]) {
case 'h':
{
show_help();
break;
}
case 'H':
{
printf("<---------History----------\n");
char cmd[64]="";
sprintf(cmd, "cat %s", HISTROY_FILE);
system(cmd);
printf("--------------------------->\n");
break;
}
case 'q':
{
printf("Bye!\n");
exit(0);
break;
}
case 'g':
{
char sendbuf[1450]="";
char username[32]="";
msg_t m;
m.msgtyp = MSG_MODE_GET_LIST;
int sendlen = sk_msg_pack(sendbuf, &m);
dump_buf("send", sendbuf, sendlen);
send(cli.sockfd, sendbuf, sendlen, 0);
break;
}
}
}
else {
char sendbuf[1450]="";
char username[32]="";
msg_t m;
m.payload = msgbuf;
m.payloadlen=strlen(msgbuf);
strcpy(username, cli.usrname);
m.username = username;
m.username_len = strlen(cli.usrname);
m.msgtyp = MSG_MODE_SEND;
int sendlen = sk_msg_pack(sendbuf, &m);
dump_buf("send", sendbuf, sendlen);
send(cli.sockfd, sendbuf, sendlen, 0);
}
}
}
}
void cli_input_start()
{
pthread_t thread_id;
int ret = pthread_create(&thread_id, NULL, cli_input_hander, NULL);
if (ret != 0) {
LOG("[%s] create phread fail,%s.\n", __func__, strerror(ret));
exit(1);
}
}
void cli_login_send(cli_t *c)
{
unsigned char logibuf[512]="";
unsigned char usrname[32]="";
msg_t m;
m.msgtyp = MSG_MODE_LOGIN;
sprintf(usrname,"%s",c->usrname);
m.username = usrname;
m.username_len = strlen(c->usrname);
LOG("[%s][%d]\n",__FUNCTION__,__LINE__);
int len = sk_msg_pack(logibuf, &m);
if(c->sockfd>0) {
send(c->sockfd, logibuf, len, 0);
}
}
void cli_login_check(cli_t *c)
{
if(c->stat==RGST_INIT) {
LOG("[%s][%d]\n",__FUNCTION__,__LINE__);
cli_login_send(c);
}
}
void cli_reconnet_check(cli_t *c)
{
if(c->sockfd<0) {
LOG("[%s][%d]\n",__FUNCTION__,__LINE__);
cli.sockfd = sk_tcp_connect_init(cli.ip,cli.port);
if(cli.sockfd>0)
cli_login_send(c);
}
}
void cli_recv_msg_hander(cli_t *c, unsigned char *msg, int len)
{
unsigned char msgtype=msg[1];
if(msgtype==MSG_MODE_LOGIN_ACK) {
c->stat= RGST_OK;
LOG("RGST_OK!\n");
}
else if(msgtype==MSG_MODE_GET_LIST_ACK) {
unsigned char data[1024]="";
int datalen = msg[2];
datalen=datalen*256+msg[3];
memcpy(data, msg+4, datalen);
printf("<---------Friends list----------\n");
printf("%s", data);
printf("-------------------------------->\n");
}
else if(msgtype==MSG_MODE_SEND||msgtype==MSG_MODE_ONLINE
||msgtype==MSG_MODE_OFFLINE||msgtype==MSG_MODE_ONLINE) {
unsigned char data[1024]="";
int datalen = msg[2];
datalen=datalen*256+msg[3];
memcpy(data, msg+4, datalen);
LOG("recv<==%s\n", data);
printf("%s\n", data);
if(c->history_enable) {
sk_history_record(data);
}
}
}
int cli_recv_proc(cli_t *c)
{
int fd = c->sockfd;
unsigned char recvbuf[1450]="";
int ret =recv(fd, recvbuf, sizeof(recvbuf), 0);
if(ret<=0) {
int close_flag = 0;
if(ret == 0)
{
/* connection closed */
LOG("socket %d close\n", fd);
close_flag = 1;
}
else {
if(errno!=EAGAIN) {
LOG("recv error!\n");
close_flag =1;
}
}
if(close_flag) {
return -1;
}
}
else {
LOG("[clisk:%d]recv %d bytes, %s\n", fd, ret, recvbuf);
cli_recv_msg_hander(c, recvbuf, ret);
return ret;
}
return 0;
}
void cli_start(char *argv[])
{
strcpy(cli.ip,"127.0.0.1");
cli.port=SERVER_PORT;
cli.sockfd = sk_tcp_connect_init(cli.ip,cli.port);
cli.stat = RGST_INIT;
cli.history_enable = 1;
strcpy(cli.usrname, argv[2]);
LOG("[%s][%d]cli.usrname:%s\n",__FUNCTION__,__LINE__, cli.usrname);
struct timeval tv, timeo;
fd_set master;
time_t timenow;
client_chn_init();
cli_input_start();
while(1) {
tv.tv_sec=0;
tv.tv_usec=20000;
FD_ZERO(&master);
time(&timenow);
int fdmax=0;
if(cli.sockfd>0) {
FD_SET(cli.sockfd, &master);
if (cli.sockfd> fdmax) {
fdmax = cli.sockfd;
}
}
if(select(fdmax+1, &master, NULL, NULL, &tv)<0)
{
LOG("select err\n");
}
if(FD_ISSET(cli.sockfd, &master)) {
LOG("[%s][%d]\n",__FUNCTION__,__LINE__);
if(cli_recv_proc(&cli)<0) {
close(cli.sockfd);
cli.sockfd = -1;
cli.stat=RGST_INIT;
}
}
if(abs(timenow-cli.login_timeout)>=10) {
LOG("[%s][%d]\n",__FUNCTION__,__LINE__);
cli.login_timeout = timenow;
cli_login_check(&cli);
}
if(abs(timenow-cli.reconn_timeout)>=30) {
LOG("[%s][%d]\n",__FUNCTION__,__LINE__);
cli.reconn_timeout = timenow;
cli_reconnet_check(&cli);
}
}
}
/*
服务端程序入口
*/
void svr_start(int port)
{
svr_t svr;
svr.port=port;
svr.listener=sk_tcp_svr_init(port);
struct timeval tv, timeo;
fd_set master;
client_chn_init();
while(1) {
tv.tv_sec=0;
tv.tv_usec=20000;
FD_ZERO(&master);
int fdmax = svr_add_fds(&svr, &master);
if(select(fdmax+1, &master, NULL, NULL, &tv)<0)
{
LOG("select err\n");
}
int i;
for(i = 0; i <= fdmax; i++){
if(FD_ISSET(i, &master)) {
LOG("[%s][%d]\n",__FUNCTION__,__LINE__);
svr_select_fd_proc(&svr,i);
}
}
}
}
void *cli_input_hander(void *arg)
{
while(1) {
char msgbuf[1024]="";
scanf("%s", msgbuf);
if((cli.sockfd>0)&&(cli.stat==RGST_OK)) {
char sendbuf[1450]="";
char username[32]="";
msg_t m;
m.payload = msgbuf;
m.payloadlen=strlen(msgbuf);
strcpy(username, cli.usrname);
m.username = username;
m.username_len = strlen(cli.usrname);
m.msgtyp = MSG_MODE_SEND;
int sendlen = sk_msg_pack(sendbuf, &m);
dump_buf("send", sendbuf, sendlen);
send(cli.sockfd, sendbuf, sendlen, 0);
}
}
}
void cli_input_start()
{
pthread_t thread_id;
int ret = pthread_create(&thread_id, NULL, cli_input_hander, NULL);
if (ret != 0) {
LOG("[%s] create phread fail,%s.\n", __func__, strerror(ret));
exit(1);
}
}
void cli_login_send(cli_t *c)
{
unsigned char logibuf[512]="";
unsigned char usrname[32]="";
msg_t m;
m.msgtyp = MSG_MODE_LOGIN;
sprintf(usrname,"%s",c->usrname);
m.username = usrname;
m.username_len = strlen(c->usrname);
LOG("[%s][%d]\n",__FUNCTION__,__LINE__);
int len = sk_msg_pack(logibuf, &m);
if(c->sockfd>0) {
send(c->sockfd, logibuf, len, 0);
}
}
void cli_login_check(cli_t *c)
{
if(c->stat==RGST_INIT) {
LOG("[%s][%d]\n",__FUNCTION__,__LINE__);
cli_login_send(c);
}
}
void cli_reconnet_check(cli_t *c)
{
if(c->sockfd<0) {
LOG("[%s][%d]\n",__FUNCTION__,__LINE__);
cli.sockfd = sk_tcp_connect_init(cli.ip,cli.port);
if(cli.sockfd>0)
cli_login_send(c);
}
}
void cli_recv_msg_hander(cli_t *c, unsigned char *msg, int len)
{
unsigned char msgtype=msg[1];
if(msgtype==MSG_MODE_LOGIN_ACK) {
c->stat= RGST_OK;
LOG("RGST_OK!\n");
}
else if(msgtype==MSG_MODE_SEND) {
unsigned char data[1024]="";
int datalen = msg[2];
datalen=datalen*256+msg[3];
memcpy(data, msg+4, datalen);
LOG("recv<==%s\n", data);
printf("<==%s\n", data);
}
}
/*
客户端数据接收
*/
int cli_recv_proc(cli_t *c)
{
int fd = c->sockfd;
unsigned char recvbuf[1450]="";
int ret =recv(fd, recvbuf, sizeof(recvbuf), 0);
if(ret<=0) {
int close_flag = 0;
if(ret == 0)
{
/* connection closed */
LOG("socket %d close\n", fd);
close_flag = 1;
}
else {
if(errno!=EAGAIN) {
LOG("recv error!\n");
close_flag =1;
}
}
if(close_flag) {
return -1;
}
}
else {
LOG("[clisk:%d]recv %d bytes, %s\n", fd, ret, recvbuf);
cli_recv_msg_hander(c, recvbuf, ret);
return ret;
}
return 0;
}
/*
客户端程序入口
*/
void cli_start(char *argv[])
{
strcpy(cli.ip,"127.0.0.1");
cli.port=SERVER_PORT;
cli.sockfd = sk_tcp_connect_init(cli.ip,cli.port);
cli.stat = RGST_INIT;
strcpy(cli.usrname, argv[2]);
LOG("[%s][%d]cli.usrname:%s\n",__FUNCTION__,__LINE__, cli.usrname);
struct timeval tv, timeo;
fd_set master;
time_t timenow;
client_chn_init();
cli_input_start();
while(1) {
tv.tv_sec=0;
tv.tv_usec=20000;
FD_ZERO(&master);
time(&timenow);
int fdmax=0;
if(cli.sockfd>0) {
FD_SET(cli.sockfd, &master);
if (cli.sockfd> fdmax) {
fdmax = cli.sockfd;
}
}
if(select(fdmax+1, &master, NULL, NULL, &tv)<0)
{
LOG("select err\n");
}
if(FD_ISSET(cli.sockfd, &master)) {
if(cli_recv_proc(&cli)<0) {
close(cli.sockfd);
cli.sockfd = -1;
cli.stat=RGST_INIT;
}
}
if(abs(timenow-cli.login_timeout)>=10) {
//定时检查登录状态
cli.login_timeout = timenow;
cli_login_check(&cli);
}
if(abs(timenow-cli.reconn_timeout)>=30) {
//定时检查连接状态
cli.reconn_timeout = timenow;
cli_reconnet_check(&cli);
}
}
}
5.在clientlist.c添加如下内容:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <pthread.h>
#include <netinet/tcp.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/queue.h>
#include <sys/ioctl.h>
#include <linux/if.h>
#include <semaphore.h>
#include <arpa/inet.h>
#include <net/if_arp.h>
#include <fcntl.h>
#include "common.h"
static CIRCLEQ_HEAD(client_Head, client_chn_s) client_head;
void client_chn_init(void)
{
CIRCLEQ_INIT(&client_head);
}
void client_chn_insert(client_chn_t *elm)
{
CIRCLEQ_INSERT_HEAD(&client_head, elm, link);
}
void client_chn_del(int skfd)
{
struct client_chn_s *chn;
for (chn = client_head.cqh_first; chn != (void *)&client_head;
chn = chn->link.cqe_next) {
if (chn->skfd== skfd) {
CIRCLEQ_REMOVE(&client_head, chn, link);
free(chn);
break;
}
}
}
int client_chn_add_usr(int skfd, char *usr)
{
struct client_chn_s *chn;
int ret=0;
for (chn = client_head.cqh_first; chn != (void *)&client_head;
chn = chn->link.cqe_next) {
if(chn->skfd==skfd) {
sprintf(chn->usrname, "%s\n",usr);
LOG("[%s](%d)add user:%s\n", __func__, __LINE__, chn->usrname);
}
}
return ret;
}
int client_chn_add_fds(fd_set *master)
{
int i;
int fdmax = 0;
struct client_chn_s *chn;
for (chn = client_head.cqh_first; chn != (void *)&client_head;
chn = chn->link.cqe_next) {
if(chn->skfd>fdmax) fdmax = chn->skfd;
FD_SET(chn->skfd, master);
}
return fdmax;
}
void client_chn_print()
{
struct client_chn_s *chn;
for (chn = client_head.cqh_first; chn != (void *)&client_head;
chn = chn->link.cqe_next) {
LOG("[%s](%d)skfd:%d\n", __func__, __LINE__, chn->skfd);
}
}
client_chn_t *client_chn_get_elm(int fd)
{
struct client_chn_s *chn;
for (chn = client_head.cqh_first; chn != (void *)&client_head;
chn = chn->link.cqe_next) {
if(chn->skfd==fd) {
return chn;
}
}
return NULL;
}
int client_chn_msg_proc(int fd, unsigned char *in, int len)
{
struct client_chn_s *chn;
for (chn = client_head.cqh_first; chn != (void *)&client_head;
chn = chn->link.cqe_next) {
if(fd>0) {
if(chn->skfd!=fd) {
send(chn->skfd, in, len, 0);
}
}
else {
send(chn->skfd, in, len, 0);
}
}
}
int client_chn_get_usr_list(char *out)
{
struct client_chn_s *chn;
int ret=0;
int cnt=0;
for (chn = client_head.cqh_first; chn != (void *)&client_head;
chn = chn->link.cqe_next) {
cnt++;
ret+=sprintf(out+ret, "%d:%s", cnt, chn->usrname);
LOG("[%s](%d)pack user:%s\n", __func__, __LINE__, chn->usrname);
}
return ret;
}
6.代码编译:
make clean
make
最后生成“chat”可执行文件,用户便可以在Linux环境下启用聊天室了。
7.启动聊天室步骤(这里以三个用户为例子)
7.1启用后台服务端
7.2依次启动三个客户端
至此,所有接入该聊天室的用户都可以开始使用聊天室的基本功能了。
总结:该程序使用了大家熟悉的TCP协议,通过客户端/服务端的方式,在Linux系统下使用C语言实现简单的局域网聊天室的功能,该功能的实现过程中可以让初学者熟悉Linux下socket编程以及不同设备之间的以太网通信机制,为以后做其它的项目打下基础并积累一些经验。