实现功能:
项目要求:
登录注册功能,不能重复登录,重复注册
单词查询功能
历史记录功能,存储单词,意思,以及查询时间
基于TCP,支持多客户端连接
采用数据库保存用户信息与历史记录
将dict.txt的数据导入到数据库中保存。
按下ctrl+c退出客户端后,注销该客户端的登录信息
格式要求:
main函数只跑逻辑,不允许跑功能代码
功能代码封装成函数
简略框架图:
效果展示:
1.登入界面
1.1普通登入
1.2重复登入
1.3错误登入
2.注册界面
2.1普通注册
2.2重复注册
3.单词查询界面
4.数据库
4.1dict单词数据库
4.2用户账号密码数据库
4.3 历史记录数据库 (表对应每个用户)
服务器:
Serfun.h:
#ifndef __TEST_H__
#define __TEST_H__
#include <signal.h>
#include <stdio.h>
#include <sqlite3.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <pthread.h>
#include <time.h>
typedef struct Node
{
union
{
int len; //存储头节点数据域,表示结点个数
struct sockaddr_in cin; //数据域,普通结点要存放的值
};
char id[20];
struct Node *next; //指针域,记录下个结点的地址
}Linklist;
#define ERR_MSG(msg) do{\
fprintf(stderr, " __%d__ ", __LINE__);\
perror(msg);\
}while(0)
//需要传入线程的数据
struct msg
{
Linklist* L;
int newfd;
struct sockaddr_in cin;
};
//用户信息结构体
struct user_msg
{
char type;
char id[20];
char password[20];
};
//查询单词的结构体
struct W_ord
{
char word[20];
char mean[50];
};
//循环打印已有客户端的线程
void* loop_printf(void* arg);
//将didt单词表添加到 dict.db 数据库中;
int add_dict();
//判断登入注册
int logreg_Interface(sqlite3* userdb,struct user_msg usermsg,int newfd,Linklist* L);
//用户登入函数int user_login(sqlite3* userdb,struct user_msg usermsg,int newfd,Linklist* l);
int user_login(sqlite3* userdb,struct user_msg usermsg,int newfd,Linklist* L);
//用户注册函数
int user_register(sqlite3* userdb,struct user_msg usermsg,int newfd);
//查询对应的单词和解释
int search_word(struct W_ord words,int newfd,struct user_msg usermsg);
//分支线程用户与客户端交互
void* rcv_cli_msg(void* arg); //void* arg = (void*)&cliInfo
//创建链表
Linklist *list_create();
//头插
int list_intsrt_head(Linklist*L, struct sockaddr_in cin,char id[20]);
//任意删
int list_delete_pos(Linklist *L, char id[20]);
//链表的销毁
void list_free(Linklist *L);
//头删
int list_delete_head(Linklist *L);
//判空
int list_empty(Linklist *L);
//遍历循环打印已有客户端
void list_show(Linklist *L);
//查找用户
int list_search_value(Linklist *L, char id[20]);
#endif
serfun.c :
#include "serfun.h"
//循环打印已有客户端的线程
void* loop_printf(void* arg)
{
Linklist* L = arg;
pthread_detach(pthread_self());
while(1)
{
system("clear");
list_show(L);
sleep(3);
}
pthread_exit(NULL);
}
//线程处理函数
void* rcv_cli_msg(void* arg) //void* arg = (void*)&cliInfo
{
//分离分支线程
pthread_detach(pthread_self());
int newfd = (*(struct msg*)arg).newfd;
struct sockaddr_in cin = (*(struct msg*)arg).cin;
Linklist* L = (*(struct msg*)arg).L;
//定义结构体:接收客户端发来的用户信息
struct user_msg usermsg;
//创建用户数据库
sqlite3* userdb = NULL;
if(sqlite3_open("./user.db", &userdb) != SQLITE_OK)
{
fprintf(stderr, "__%d__ sqlite3_open failed\n", __LINE__);
return NULL;
}
//创建用户数据库账号密码表
char* sql = "create table if not exists userMsg (id char primary key, password char);" ;
char* errmsg = NULL;
if(sqlite3_exec(userdb, sql, NULL, NULL, &errmsg) != SQLITE_OK)
{
fprintf(stderr, "__%d__ sqlite3_exec:%s\n", __LINE__, errmsg);
return NULL;
}
while(1)
{
//接收登入、注册信息
ssize_t res;
res = recv(newfd, &usermsg, sizeof(usermsg), 0);
if(res < 0)
{
ERR_MSG("recv");
return NULL;
}
else if(0 == res)
{
printf("[%s : %d] newfd = %d 客户端掉线\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port),newfd);
list_delete_pos(L,usermsg.id);
close(newfd);
pthread_exit(NULL);
break;
}
if( logreg_Interface(userdb,usermsg,newfd,L) == 1 ) //1函数成功 -2函数重新需要参数执行
{
break;
}
}
//将网络结构体插入链表
list_intsrt_head(L,cin,usermsg.id);
struct W_ord words;
char buf[128] = "";
ssize_t res = 0;
while(1)
{
bzero(&words,sizeof(words));
bzero(buf, sizeof(buf));
//循环接收
res = recv(newfd, &words, sizeof(words), 0);
if(res < 0)
{
ERR_MSG("recv");
break;
}
else if(0 == res)
{
printf("[%s : %d] newfd = %d 客户端掉线\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port),newfd);
list_delete_pos(L,usermsg.id);
break;
}
//查询对应的单词和解释
search_word(words,newfd,usermsg);
}
close(newfd);
pthread_exit(NULL);
}
//判断用户登入注册
int logreg_Interface(sqlite3* userdb,struct user_msg usermsg,int newfd,Linklist* L)
{
if(usermsg.type == 'L')
{
return user_login(userdb,usermsg,newfd,L); //1函数成功 -2函数重新需要参数执行
}
else if(usermsg.type == 'R')
{
return user_register(userdb,usermsg,newfd); //2为函数运行成功
}
return 0;
}
//用户登入函数
int user_login(sqlite3* userdb,struct user_msg usermsg,int newfd,Linklist* L)
{
//查询账号密码是否在数据库中
char cmp[128] = "";
sprintf(cmp,"SELECT * FROM userMsg WHERE id like \"%s\" AND password like \"%s\"",usermsg.id,usermsg.password);
char ** pres = NULL;
int row,column; //查询结果的行列数
char * errmsg = NULL;
if(sqlite3_get_table(userdb, cmp, &pres, &row, &column, &errmsg) != SQLITE_OK)
{
fprintf(stderr, "__%d__ sqlite3_get_table:%s\n", __LINE__, errmsg);
return -1;
}
//发送给客户端确认信息, 1为成功,-1为账号密码错误,-2为已经登入
int flag = 0;
if(list_search_value(L,usermsg.id) != 0)
{
flag = -2;
if(send(newfd, &flag, sizeof(flag), 0) < 0)
{
ERR_MSG("send");
return -1;
}
return -2;
}
if(row == 1) //账号密码在数据库
{
flag = 1;
if(send(newfd, &flag, sizeof(flag), 0) < 0)
{
ERR_MSG("send");
return -1;
}
printf("%s用户登入成功\n",usermsg.id);
return 1;
}
else
{
flag = -1;
if(send(newfd, &flag, sizeof(flag), 0) < 0)
{
ERR_MSG("send");
return -1;
}
return -2;
}
//释放获取到的空间
sqlite3_free_table(pres);
pres = NULL;
return 0;
}
//用户注册函数
int user_register(sqlite3* userdb,struct user_msg usermsg,int newfd)
{
int flag = 1 ; //注册成功为1,注册失败为-1;
//调用插入数据库
char* errmsg = NULL;
char msg[128] = "";
sprintf(msg, "insert into userMsg values(\"%s\",\"%s\")", usermsg.id,usermsg.password);
if(sqlite3_exec(userdb, msg, NULL, NULL, &errmsg) != SQLITE_OK)
{
flag = -1; //注册失败,发送-1给客户端
fprintf(stderr, "__%d__ sqlite3_exec:%s\n", __LINE__,errmsg);
if(send(newfd, &flag, sizeof(flag), 0) < 0)
{
ERR_MSG("send");
return -2;
}
return -2;
}
//注册成功,发送1给客户端
if(send(newfd, &flag, sizeof(flag), 0) < 0)
{
ERR_MSG("send");
return -1;
}
printf("%s用户注册成功\n",usermsg.id);
return 2;
}
//查询对应的单词和解释,并发送给客户端,并加入历史记录
int search_word(struct W_ord words,int newfd,struct user_msg usermsg)
{
//打开单词数据库
sqlite3* dictdb = NULL;
if(sqlite3_open("./dict.db", &dictdb) != SQLITE_OK)
{
fprintf(stderr, "__%d__ sqlite3_open:%s\n", __LINE__, sqlite3_errmsg(dictdb));
return -1;
}
//打开所有用户的历史记录数据库
sqlite3* histroy = NULL;
if(sqlite3_open("./histroy.db", &histroy) != SQLITE_OK)
{
fprintf(stderr, "__%d__ sqlite3_open failed\n", __LINE__);
return -1;
}
//创建对应用户的历史记录表格
char sql[128] = "";
sprintf(sql,"create table if not exists %s (word char, mean char,time char);",usermsg.id);
char* errmsg = NULL;
if(sqlite3_exec(histroy, sql, NULL, NULL, &errmsg) != SQLITE_OK)
{
fprintf(stderr, "__%d__ sqlite3_exec:%s\n", __LINE__, errmsg);
return -1;
}
//查询单词和解释是否在数据库中
char cmp[128] = "";
//将命令存入cmp, word像xxx或者意思像xxx的
sprintf(cmp,"SELECT * FROM dict WHERE word like \"%s\" OR mean like \" %s \"",words.word,words.mean);
char ** pres = NULL;
int row,column; //查询结果的行列数
if(sqlite3_get_table(dictdb, cmp, &pres, &row, &column, &errmsg) != SQLITE_OK)
{
fprintf(stderr, "__%d__ sqlite3_get_table:%s\n", __LINE__, errmsg);
return -1;
}
//判断是否查找到
if(row == 0)
{
strcpy(words.mean,"未找到此单词");
}
else
{
//将查到的数据存入单词结构体
bzero(&words,sizeof(words));
strcpy(words.word,pres[2]);
strcpy(words.mean,pres[3]);
//获取查询时间
time_t t;
struct tm *info = NULL;
char timenow[128] = "";
t = time(NULL);
info = localtime(&t);
sprintf(timenow, "%d-%02d-%02d %02d:%02d:%02d",\
info->tm_year+1900, info->tm_mon+1, info->tm_mday,\
info->tm_hour, info->tm_min, info->tm_sec);
//将单词存入历史记录表
bzero(sql,sizeof(sql));
sprintf(sql, "insert into %s values(\"%s\",\"%s\",\"%s\");", usermsg.id,pres[2],pres[3],timenow);
if(sqlite3_exec(histroy, sql, NULL, NULL, &errmsg) != SQLITE_OK)
{
fprintf(stderr, "__%d__ sqlite3_exec:%s\n", __LINE__, errmsg);
return -1;
}
}
//将结构体发送给客户端
if(send(newfd, &words, sizeof(words), 0) < 0)
{
ERR_MSG("send");
return -1;
}
//关闭数据库
sqlite3_close(dictdb);
return 0;
}
//将didt单词表添加到 dict.db 数据库中;
int add_dict()
{
//打开数据库
sqlite3* dictdb = NULL;
if(sqlite3_open("./dict.db", &dictdb) != SQLITE_OK)
{
fprintf(stderr, "__%d__ sqlite3_open:%s\n", __LINE__, sqlite3_errmsg(dictdb));
return -1;
}
char sql[400] = "create table if not exists dict (word char, mean char)";
char* errmsg = NULL;
//创建表格
if(sqlite3_exec(dictdb, sql, NULL, NULL, &errmsg) != SQLITE_OK)
{
fprintf(stderr, "__%d__ sqlite3_exec:%s\n", __LINE__,errmsg);
return -1;
}
//打开文件
FILE* fp = fopen("./dict.txt", "r");
if(NULL == fp)
{
perror("fopen");
return -1;
}
//循环读取一行,直到读取完毕为止
char buf[300] = "";
char word[50]="", mean[300]="";
int i = 0;
while(fgets(buf, sizeof(buf), fp) != NULL)
{
buf[strlen(buf)-1] = 0;
//分离单词和意思
for(i=0; i<strlen(buf)-2; i++)
{
if(buf[i]!=' ' && buf[i+1]==' ' && buf[i+2]==' ')
{
strncpy(word, buf, i+1);
// printf("wrod = %s\n", word);
}
else if(buf[i]==' '&& buf[i+1]==' ' && buf[i+2]!=' ')
{
strcpy(mean, (buf+i+2));
break;
}
}
//插入到数据库中
sprintf(sql, "insert into dict values(\"%s\", \"%s\")", word, mean);
// printf("%s\n", sql);
if(sqlite3_exec(dictdb, sql, NULL, NULL, &errmsg) != SQLITE_OK)
{
fprintf(stderr, "__%d__ sqlite3_exec:%s\n", __LINE__,errmsg);
return -1;
}
bzero(word, sizeof(word));
bzero(mean, sizeof(mean));
}
printf("导入单词库成功\n");
//关闭文件
fclose(fp);
//关闭数据库
sqlite3_close(dictdb);
}
//创建链表
Linklist *list_create()
{
Linklist* L = (Linklist*)malloc(sizeof(Linklist));
if(NULL == L)
{
printf("创建失败\n");
return NULL;
}
//初始化
L->next = NULL;
L->len = 0;
return L;
}
//头插
int list_intsrt_head(Linklist*L, struct sockaddr_in cin,char id[20])
{
//申请节点
Linklist *p = (Linklist*)malloc(sizeof(Linklist));
if(NULL==p)
{
printf("节点申请失败\n");
return -1;
}
//将要插入的数据放入节点
p->cin = cin;
p->next = NULL;
strcpy(p->id,id);
//完成头插
p->next = L->next;
L->next = p;
//表的变化
L->len++;
printf("头插成功\n");
return 0;
}
//任意删
int list_delete_pos(Linklist *L, char id[20])
{
//判断逻辑
if(NULL==L || list_empty(L) )
{
return -1;
}
Linklist *q = L;
if(strcmp(q->next->id,id))
{
q = q->next;
}
//删除逻辑
Linklist *p = q->next;
q->next = p->next;
free(p);
p = NULL;
//表的变化
L->len --;
return 0;
}
//链表的销毁
void list_free(Linklist *L)
{
if(NULL == L)
{
return;
}
//不断调用头删,将结点进行删除
while(L->next != NULL)
{
list_delete_head(L);
}
//将头结点释放
free(L);
L = NULL;
printf("释放表完成\n");
return ;
}
//头删
int list_delete_head(Linklist *L)
{
//判断逻辑
if(NULL==L || list_empty(L))
{
printf("删除失败\n");
return -1;
}
//头删
Linklist *p = L->next; //标记
L->next = p->next; //孤立
free(p); //踢开
p=NULL;
//表的变化
L->len--;
return 0;
}
//判空
int list_empty(Linklist *L)
{
//1表示空 0表示非空
if(NULL==L)
{
printf("表不合法\n");
return -1;
}
return NULL==L->next ? 1:0;
}
//遍历循环打印已有客户端
void list_show(Linklist *L)
{
//判断逻辑
if( NULL==L || list_empty(L))
{
printf("还未有客户端用户登入(3s更新一次)\n");
return;
}
//遍历逻辑
printf("在线的客户端数量为%d,用户分别为(3s更新一次):\n",L->len);
Linklist *q = L->next;
while(q != NULL)
{
printf("地址:[%s : %d]\t用户名:[%s]\n", inet_ntoa(q->cin.sin_addr), ntohs(q->cin.sin_port),q->id);
q = q->next;
}
printf("\n");
}
//查找用户
int list_search_value(Linklist *L, char id[20])
{
//查找逻辑
Linklist *q = L->next;
for(int i=1; i<=L->len; i++)
{
if(strcmp(q->id,id))
{
q = q ->next;
}
else
{
return i;
}
}
return 0;
}
main.c:
#include "serfun.h"
#define PORT 8888 //1024~49151
#define IP "192.168.31.188" //本机IP,用ifconfig查看
int main(int argc, const char *argv[])
{
//创建套接字
int sfd = socket(AF_INET, SOCK_STREAM, 0);
if(sfd < 0)
{
ERR_MSG("socket");
return -1;
}
printf("create socket success\n");
//允许端口快速重用
int reuse = 1;
if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
{
ERR_MSG("setsockopt");
return -1;
}
//填充地址信息结构体,真实的地址信息结构体与协议族相关
//AF_INET,所以详情请看man 7 ip
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT); //网络字节序的端口号
sin.sin_addr.s_addr = inet_addr(IP); //网络字节序的IP地址
//将地址信息结构体绑定到套接字上
if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) < 0)
{
ERR_MSG("bind");
return -1;
}
printf("bind success\n");
//将套接字设置为被动监听状态,让内核去监听是否有客户端连接;
if(listen(sfd, 10) < 0)
{
ERR_MSG("listen");
return -1;
}
printf("listen success\n");
system("clear");
//创建存储客户端网络结构体链表
Linklist *L = list_create();
if(NULL == L)
{
return -1;
}
//定义存储客户端网络结构体
struct sockaddr_in cin;
socklen_t addrlen = sizeof(cin);
//将didt单词表添加到 dict.db 数据库中;
add_dict();
int newfd = 0;
pthread_t tid;
struct msg cliInfo;
//从已完成连接的队列头中,取出一个客户端的信息,创建生成一个新的套接字文件描述符,
//该文件描述符才是与客户端通信的文件描述符!!!
//循环打印已有客户端的线程
if(pthread_create(&tid, NULL, loop_printf, (void*)L) != 0)
{
ERR_MSG("pthread_create");
return -1;
}
while(1)
{
//主线程主要负责连接
newfd = accept(sfd, (struct sockaddr*)&cin, &addrlen);
if(newfd < 0)
{
perror("accept");
return -1;
}
//网络字节序的IP-->点分十进制 网络字节序的port--->本机字节序
printf("[%s : %d] newfd = %d 客户端连入\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port),newfd);
cliInfo.newfd = newfd;
cliInfo.cin = cin;
cliInfo.L = L;
//一旦连接成功后,创建一个分支线程用户与客户端交互;
if(pthread_create(&tid, NULL, rcv_cli_msg, (void*)&cliInfo) != 0)
{
ERR_MSG("pthread_create");
return -1;
}
}
close(sfd);
list_free(L);
return 0;
}
客户端:
clifun.h:
#ifndef __CLIFUN_H__
#define __CLIFUN_H__
#include <stdio.h>
#include <sqlite3.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <pthread.h>
#define ERR_MSG(msg) do{\
fprintf(stderr, " __%d__ ", __LINE__);\
perror(msg);\
}while(0)
//需要传入线程的数据
struct msg
{
int newfd;
struct sockaddr_in cin;
};
//用户信息结构体
struct user_msg
{
char type;
char id[20];
char password[20];
};
//查询单词的结构体
struct W_ord
{
char word[20];
char mean[50];
};
struct PRES
{
char ** topres;
};
//将didt单词表添加到 dict.db 数据库中;
int add_dict(sqlite3*);
//登入注册界面
int logreg_Interface(int sfd);
//向服务器发送用户登入申请
int user_login(int sfd);
//向服务器发送用户注册申请
int user_register(int sfd);
//分支线程用户与客户端交互
void* rcv_cli_msg(void* arg); //void* arg = (void*)&cliInfo
#endif
clifun.c:
#include "clifun.h"
//登入注册界面
int logreg_Interface(int sfd)
{
while(1)
{
printf("-----------洪氏宝典----------\n");
printf("-----------1.登入------------\n");
printf("-----------2.注册------------\n");
printf("-----------3.退出------------\n");
int num; //存储选择的序号
printf("请选择:");
scanf("%d",&num);
if(num == 1)
{
if(user_login(sfd) == -1)
{
return -1;
}
else
{
break;
}
}
else if(num == 2)
{
if(user_register(sfd) == -1)
{
return -1;
}
if(user_login(sfd) == -1)
{
return -1;
}
break;
}
else if(num == 3)
{
return -2;
}
else
{
printf("选择无效,请重新选择\n");
}
}
return 0;
}
//向服务器发送用户登入申请
int user_login(int sfd)
{
while(1)
{
printf("------登入------\n");
//填入账号密码数据
struct user_msg usermsg;
usermsg.type = 'L';
printf("请输入账号:");
scanf("%s",usermsg.id);
getchar();
printf("请输入密码:");
scanf("%s",usermsg.password);
getchar();
//发送给服务器
if(send(sfd, &usermsg , sizeof(usermsg), 0) < 0)
{
ERR_MSG("send");
return -1;
}
//接受服务器发来的确认信息
int flag;
ssize_t res;
res = recv(sfd, &flag, sizeof(flag), 0);
if(res < 0)
{
ERR_MSG("recv");
return -1;
}
else if(0 == res)
{
return -1;
}
//判断服务器发来的确认信息 1为成功,-1为失败(数据错误)
if(flag == 1)
{
printf("登入成功\n");
break;
}
else if(flag == -1)
{
printf("用户账号密码错误\n");
}
else if(flag == -2)
{
printf("该用户已经登入\n");
}
}
return 0;
}
//向服务器发送用户注册申请
int user_register(int sfd)
{
printf("------注册------\n");
while(1)
{
//填入要注册账号密码数据
struct user_msg usermsg;
usermsg.type = 'R';
printf("请输入注册账号(不能纯数字):");
scanf("%s",usermsg.id);
getchar();
printf("请输入注册密码:");
scanf("%s",usermsg.password);
getchar();
//发送给服务器
if(send(sfd, &usermsg , sizeof(usermsg), 0) < 0)
{
ERR_MSG("send");
return -1;
}
//接受服务器发来的确认信息
int flag;
ssize_t res;
res = recv(sfd, &flag, sizeof(flag), 0);
if(res < 0)
{
ERR_MSG("recv");
return -1;
}
else if(0 == res)
{
printf("server is off-line\n");
return -1;
}
//判断服务器发来的确认信息 1为成功,-1为失败(数据错误)
if(flag == 1)
{
printf("注册成功\n");
break;
}
else if(flag < 0)
{
printf("注册失败,重复注册\n");
}
}
return 0;
}
cli.c:
#define PORT 8888
#define IP "192.168.31.188"
int main(int argc, const char *argv[])
{
//创建流式套接字
int sfd = socket(AF_INET, SOCK_STREAM, 0);
if(sfd < 0)
{
ERR_MSG("socket");
return -1;
}
printf("create socket success\n");
//绑定客户端的地址信息结构体--》非必须绑定
//填充要连接的服务器的地址信息结构体
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT);
sin.sin_addr.s_addr = inet_addr(IP);
//连接服务器 connect
if(connect(sfd, (struct sockaddr*)&sin, sizeof(sin)) < 0)
{
ERR_MSG("connect");
return -1;
}
int reslog;
reslog = logreg_Interface(sfd);
if(reslog== 0)
{
}
else if(reslog == -1)
{
printf("服务器掉线\n");
return -1;
}
else
{
return -1;
}
system("clear");
//查询单词操作
char buf[128] = "";
ssize_t res = 0;
struct W_ord words = {"",""};
struct PRES sinpres;
while(1)
{
bzero(&words,sizeof(words));
bzero(buf, sizeof(buf));
//发送
printf("请输入要查询的单词>>>");
fgets(buf, sizeof(buf), stdin);
buf[strlen(buf)-1] = 0;
//判断输入的是单词还是解释
if(buf[0] > 0) //如果第一个是字母的话
{
strcpy(words.word,buf); //将输入存到word里面
}
else
{
strcpy(words.mean,buf);//如果不是将输入存到explain里面
}
if(send(sfd, &words, sizeof(words), 0) < 0)
{
ERR_MSG("send");
return -1;
}
//接收
bzero(&words, sizeof(words));
res = recv(sfd, &words, sizeof(words), 0);
if(res < 0)
{
ERR_MSG("recv");
return -1;
}
else if(0 == res)
{
printf("服务器已掉线\n");
break;
}
printf("%s\t%s\n", words.word, words.mean);
}
//关闭套接字
close(sfd);
return 0;
}