近期有些空閑竭讳,正好趁著這段時(shí)間做些練手的項(xiàng)目鞏固一些Linux下C編程:)
技術(shù)
- TCP Socket編程
- SYS V消息隊(duì)列
- curses lib
- pthread lib
設(shè)計(jì)
幾乎沒有設(shè)計(jì)梭冠。空执。簡(jiǎn)單的服務(wù)端/客戶端設(shè)計(jì),使用TCP socket傳輸數(shù)據(jù)婴梧,消息隊(duì)列做進(jìn)程間通信下梢。
Server/Client
server端
- 主進(jìn)程監(jiān)聽,子進(jìn)程處理每個(gè)客戶端
- 子進(jìn)程的子進(jìn)程收其他進(jìn)程消息發(fā)送給當(dāng)前客戶端
client端
兩個(gè)進(jìn)程塞蹭,一個(gè)進(jìn)程獲得終端輸入孽江,一個(gè)進(jìn)程收socket數(shù)據(jù)輸出- 派生線程在輸出窗口輸出服務(wù)端信息
結(jié)構(gòu)定義
消息隊(duì)列結(jié)構(gòu)
typedef struct mqmesg{
long mtype;
long mlen;
char mdata[MAXLEN];
}message;
用戶登錄信息結(jié)構(gòu)
typedef struct lginfo{
struct tm *login_time; // 登錄時(shí)間結(jié)構(gòu)
struct sockaddr_in *cliaddr; // 客戶端IPV4結(jié)構(gòu)
char login_name[50+1]; // 用戶登錄名
}loginfo;
客戶端線程參數(shù)結(jié)構(gòu)
typedef struct thr_arg{
WINDOW *wnd;
int socket;
char *servip;
}thrarg;
模塊
-
通用模塊 util.c
- ssize_t readn(int, void *, size_t); // 循環(huán)read函數(shù),確保收取n字節(jié)
- ssize_t writen(int, void *, size_t); // 循環(huán)write函數(shù)番电,確保發(fā)送n字節(jié)
- int sendMsg(int, void *, int); // 發(fā)送socket封裝函數(shù)
- int recvMsg(int, void *, int *); // 接收socket封裝函數(shù)
- int mqMsgSTInit(message *, char *, long, long); // 消息隊(duì)列結(jié)構(gòu)賦值函數(shù)
- ssize_t sendMq(int, message *); // 發(fā)送消息隊(duì)列封裝函數(shù)
- ssize_t recvMq(int, message *); // 接收消息隊(duì)列封裝函數(shù)
- int tm2DateTimeStr(struct tm *, char *); // linux時(shí)間tm結(jié)構(gòu)轉(zhuǎn)YYYY-MM-DD HH:MM:SS字符串
- int getCurTimeStr(char *); // 獲得當(dāng)前時(shí)間串
-
服務(wù)端功能模塊 servfunc.c
- int getClientCount(int); // 讀取type 1消息代表的客戶端數(shù)量
- int putClientCount(int, int); // 向消息隊(duì)列寫入客戶端數(shù)量
- int login_serv(int, loginfo *); // 服務(wù)端登入處理函數(shù)
- 客戶端功能模塊 clifunc.c
- int login_cli(int); // 客戶端登入處理函數(shù)
- void *thr_fn(thrarg *); // 客戶端處理輸出線程函數(shù)
實(shí)現(xiàn)
server 端
#include "chatroom.h"
int main(int argc, char *argv[])
{
struct sockaddr_in servaddr, cliaddr;
socklen_t cliaddr_len;
char buf[MAXLEN+1];
char buf2[MAXLEN] = {0};
char addr[INET_ADDRSTRLEN];
int listenfd,connfd;
int i,n,len;
int pid;
int client_count = 0;
int fpid = getpid();
char tt[19+1] = {0};
listenfd = socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERVPORT);
bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
listen(listenfd, 20);
printf("Accepting connect...\n");
int mq_fd = msgget(IPC_PRIVATE, SVMSG_MODE | IPC_CREAT);
printf("msgid:%d\n",mq_fd);
putClientCount(mq_fd, client_count);
message *msg = (message *)malloc(sizeof(message));
while(1){
cliaddr_len = sizeof(cliaddr);
connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);
client_count = getClientCount(mq_fd);
client_count++;
putClientCount(mq_fd, client_count);
pid = fork();
if(pid<0) printf("fork err\n");
else if(pid>0){
continue;
}else{
// child
printf("this is child process[%d]\n", getpid());
inet_ntop(AF_INET, &cliaddr.sin_addr, addr, sizeof(addr));
printf("Recieved connection form [%s] at PORT [%d]\n", addr, ntohs(cliaddr.sin_port));
client_count = getClientCount(mq_fd);
int cliNo = client_count;
putClientCount(mq_fd, client_count);
loginfo *cli_log_info = (loginfo *)malloc(sizeof(loginfo));
cli_log_info->cliaddr = &cliaddr;
login_serv(connfd, cli_log_info); // client login
getCurTimeStr(tt);
sprintf(buf2, "(%s) %s join the chatroom", tt, cli_log_info->login_name);
client_count = getClientCount(mq_fd);
for(i=1;i<=client_count;i++){
if(i==cliNo) continue;
mqMsgSTInit(msg, buf2, strlen(buf2), 10000+i);
sendMq(mq_fd, msg);
}
putClientCount(mq_fd, client_count);
char welcome[100] = {0};
sprintf(welcome, "%s%d%s", "-----welcome to chat room, current user no: ", client_count, "------");
sendMsg(connfd, welcome, strlen(welcome));
int pid2 = fork();
if(pid2<0){ printf("fork err\n"); continue;}
else if(pid2>0){
while(1){
memset(buf, 0x00, sizeof(buf));
if(recvMsg(connfd, buf, &len)<0){
printf("The client [%d] closed the connection.\n", getpid());
getCurTimeStr(tt);
sprintf(buf2, "(%s) %s quit the chatroom", tt, cli_log_info->login_name);
client_count = getClientCount(mq_fd);
for(i=1;i<=client_count;i++){
if(i==cliNo) continue;
mqMsgSTInit(msg, buf2, strlen(buf2), 10000+i);
sendMq(mq_fd, msg);
}
putClientCount(mq_fd, client_count);
kill(pid2, SIGKILL);
break;
}
printf("CLIENT[%s]:PID[%d]:LOGIN_NAME[%s]:LEN[%d]:MSG[%s]\n", addr, getpid(), cli_log_info->login_name, len, buf);
getCurTimeStr(tt);
sprintf(buf2, "(%s) %s: %s", tt, cli_log_info->login_name, buf);
// strcat(buf, "[B]");
// printf("DEBUG: [%d], buf[%s]\n", strlen(buf), buf);
client_count = getClientCount(mq_fd);
for(i=1;i<=client_count;i++){
//if(i==cliNo) continue;
mqMsgSTInit(msg, buf2, strlen(buf2), 10000+i);
sendMq(mq_fd, msg);
}
putClientCount(mq_fd, client_count);
}
}else{
while(1){
mqMsgSTInit(msg, NULL, 0, 10000+cliNo);
if(recvMq(mq_fd, msg)<=0) continue;
else{
sendMsg(connfd, msg->mdata, msg->mlen);
}
}
}
client_count = getClientCount(mq_fd);
client_count--;
putClientCount(mq_fd, client_count);
close(connfd);
break;
}
}
close(listenfd);
free(msg);
if(getpid() == fpid){
msgctl(mq_fd, IPC_RMID, NULL);
}
return 0;
}
client 端
#include "chatroom.h"
int nrows, ncols;
pthread_t ntid;
int main(int argc, char *argv[])
{
struct sockaddr_in servaddr;
char buf[MAXLEN], buf2[MAXLEN];
int sockfd;
int n,len,flag;
int pid;
char servip[15+1];
int ret;
if(argc == 2)
{
strcpy(servip, argv[1]);
}else{
printf("USAGE: client [serverip]\n");
exit(0);
}
WINDOW *wnd = initscr();
getmaxyx(wnd, nrows, ncols);
WINDOW *logwin = newwin(0,0,0,0);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
inet_pton(AF_INET, (const char *)servip, &servaddr.sin_addr);
servaddr.sin_port = htons(SERVPORT);
ret = connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
if(!ret){
wprintw(logwin,"Connect succeed!\n");
wrefresh(logwin);
}else{
wprintw(logwin,"Cant connect to the server:%s\n", servip);
wrefresh(logwin);
exit(1);
}
// login
login_cli_cgi(sockfd, logwin);
werase(logwin);
delwin(logwin);
WINDOW *winin, *winout;
winin = newwin(0, 0, nrows-1, 0);
winout = newwin(nrows-2, 0, 0, 0);
scrollok(winout, 1);
thrarg ta = {winout, sockfd, servip};
ret = pthread_create(&ntid, NULL, (void *)thr_fn, &ta);
if(ret != 0){
wprintw(winout, "cant create thread\n");
exit(ret);
}
wprintw(winin, "> ");
wrefresh(winin);
while(!wgetnstr(winin, buf, MAXLEN)){
sendMsg(sockfd, buf, strlen(buf));
wclrtoeol(winin);
wprintw(winin, "> ");
wrefresh(winin);
}
close(sockfd);
delwin(winin);
delwin(winout);
endwin();
return 0;
}
其他
- 目前已知的幾個(gè)缺陷:
客戶端終端輸入輸出在一起岗屏,有時(shí)候輸入時(shí)會(huì)有輸出冒出來- Ctrl-C kill掉服務(wù)端后,建立的消息隊(duì)列沒有刪除