基于UDP的网络聊天室
ser
#include "head.h"
#define ERR_MSG(msg) do{\
fprintf(stderr,"line:%d\n",__LINE__);\
perror(msg);\
}while(0)
#define IP "192.168.0.103 " //本机IP ifconfig
#define PORT 2048 //1024-49151
//定义结点结构体
typedef struct Node
{
//数据域:
union{
int len; //头结点的数据域
struct sockaddr_in addr;
};
//指针域:下一个结点的地址
struct Node *next;
}*Linklist;
struct msg
{
char type; //'L'登录 'C'chat 'Q'下线
char name[20];
char text[128];
};
/*
* function: 在堆区创建头结点
* @param [ in] 无参数
* @param [out]
* @return 成功返回头结点地址,失败返回NULL
*/
Linklist Create_head()
{
Linklist L = (Linklist)malloc(sizeof(struct Node));
if(L == NULL)
return NULL;
L->len = 0;
L->next = NULL;
return L;
}
/*
* function: 创建其他结点
* @param [ in] 无参数
* @param [out]
* @return 成功返回首地址,失败返回NULL
*/
Linklist Create_node()
{
Linklist p = (Linklist)malloc(sizeof(struct Node));
if(p == NULL)
return NULL;
//p->addr = NULL;
p->next = NULL;
return p;
}
/*
* function: 头插 永远在头结点后面
* @param [ in] 头结点 插入的数据元素
* @param [out]
* @return 成功返回0,失败返回-1
*/
int Insert_head(Linklist L,struct sockaddr_in e)
{
//判断头结点是否存在
if(L == NULL)
{
printf("头插入失败。\n");
return -1;
}
//在堆区申请其他结点空间
Linklist p = Create_node();
//p的数据赋值为e
p->addr = e;
//p的指针域
p->next = L->next;
L->next = p;
//链表长度自增
L->len++;
return 0;
}
#if 1
/*
* function: 链表的遍历
* @param [ in] 链表
* @param [out]
* @return 无返回值函数
*/
void Output(Linklist L)
{
//1.判断头结点是否存在
//2.判断链表是否为空
if(L == NULL || L->len == 0)
printf("输出失败!\n");
while(L->next)
{
L = L->next;
// printf("%s\t",L->addr);
}
printf("\n");
}
/*
* function: 按元素查找
* @param [ in] 链表 元素
* @param [out]
* @return 成功返回位置,失败返回-1
*/
int Search_by_data(Linklist L,struct sockaddr_in e)
{
//判断头结点是否存在
//判断链表是否为空
if(L == NULL || L->len == 0)
{
printf("失败。\n");
return -1;
}
Linklist p = L;
for(int i=0;i<L->len;i++)
{
p = p->next;
if(p->addr.sin_port == e.sin_port)
return i+1;
}
return -1;
}
/*
* function: 按位置删除
* @param [ in] 链表 删除的位置
* @param [out]
* @return 成功返回0,失败返回-1
*/
int Delete_by_pos(Linklist L,int pos)
{
//判断头结点是否存在
//判断位置是否合法
if(L == NULL || L->len == 0 || pos<1 || pos>L->len)
{
printf("失败。\n");
return -1;
}
//找到pos-1的位置,起名字为p
Linklist p = L;
for(int i=0;i<pos-1;i++)
p = p->next;
Linklist s = p->next;
p->next = s->next;
free(s);
s = NULL;
L->len--;
//Delete_head(p);
return 0;
}
/*
* function: 按元素删除
* @param [ in] 链表 元素
* @param [out]
* @return 成功返回0,失败返回-1
*/
int Delete_by_data(Linklist L,struct sockaddr_in e)
{
//先找e位置
int pos = Search_by_data(L,e);
if(pos ==-1)
return -1;
//再根据位置删除
int flag = Delete_by_pos(L,pos);
if(flag == -1)
return -1;
return 0;
}
#endif
int do_login();
int main(int argc, const char *argv[])
{
Linklist L = Create_head();//创建头结点
//创建报式套接字
int sfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sfd < 0)
{
ERR_MSG("socket");
return -1;
}
//填充服务器的地址信息结构体
struct sockaddr_in sin;
sin.sin_family = AF_INET; //必须填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;
}
struct sockaddr_in cin;
socklen_t addrlen = sizeof(cin);
struct msg sndbuf;
strcat(sndbuf.name,"**system**");
//创建进程
pid_t cpid = fork();
//父进程用于接收
if(cpid > 0)
{
while(1)
{
struct msg rcvbuf;
recvfrom(sfd, &rcvbuf, sizeof(rcvbuf),0, (struct sockaddr*)&cin, &addrlen);
//printf("type = %c\n", rcvbuf.type);
//printf("name = %s\n",rcvbuf.name);
//printf("%d\n",ntohs(cin.sin_port));
switch(rcvbuf.type)
{
case 'L':
Insert_head(L,cin);
Linklist q = L->next;
while(q != NULL)//群发
{
rcvbuf.type = 'L';
if(sendto(sfd,&rcvbuf,sizeof(rcvbuf),0,(struct sockaddr*)&q->addr,sizeof(q->addr)) < 0)
{
ERR_MSG("sendto");
return -1;
}
q = q->next;
}
printf("用户[%s]登陆成功\n",rcvbuf.name);
//do_login();
break;
case 'C':
printf("[%s]: %s\n",rcvbuf.name,rcvbuf.text);
Linklist p = L->next;
while(p != NULL)//群发
{
rcvbuf.type = 'C';
if(sendto(sfd,&rcvbuf,sizeof(rcvbuf),0,(struct sockaddr*)&p->addr,sizeof(p->addr)) < 0)
{
ERR_MSG("sendto");
return -1;
}
p = p->next;
}
//do_chat();
break;
case 'Q':
Delete_by_data(L,cin);
Linklist t = L->next;
while(t != NULL)//群发
{
rcvbuf.type = 'Q';
if(sendto(sfd,&rcvbuf,sizeof(rcvbuf),0,(struct sockaddr*)&t->addr,sizeof(t->addr)) < 0)
{
ERR_MSG("sendto");
return -1;
}
t = t->next;
}
printf("[%s]已下线\n",rcvbuf.name);
//do_quit();
break;
}
}
}
//子进程用于发送
else if(0 == cpid)
{
while(1)
{
memset(sndbuf.text,0,sizeof(sndbuf.text));
scanf("%s",sndbuf.text);
// Linklist q = L->next;
// while(q != NULL)//群发
// {
sndbuf.type = 'C';
if(sendto(sfd,&sndbuf,sizeof(sndbuf),0,(struct sockaddr*)&sin,sizeof(sin)) < 0)
{
ERR_MSG("sendto");
return -1;
}
// q = q->next;
// }
}
}
//关闭文件描述符
close(sfd);
return 0;
}
int do_login()
{
//转发---->遍历链表
//保存客户端信息
}
cli
#include "head.h"
#define ERR_MSG(msg) do{\
fprintf(stderr, "line:%d ", __LINE__);\
perror(msg);\
}while(0)
#define CLI_IP "192.168.0.103" //本机IP ifconfig
#define CLI_PORT 2048 //1024-49151
struct msg
{
char type; //'L'登录 'C'chat 'Q'下线
char name[20];
char text[128];
};
int main(int argc, const char *argv[])
{
//创建报式套接字
int cfd = socket(AF_INET, SOCK_DGRAM, 0);
if(cfd < 0)
{
ERR_MSG("socket");
return -1;
}
//bind 非必须绑定
//如果不绑定则由操作系统自动绑定IP和端口
//填充服务器的地址信息结构体,给sendto函数使用
struct sockaddr_in sin;
sin.sin_family = AF_INET; //必须填AF_INET;
sin.sin_port = htons(CLI_PORT); //端口号的网络字节序
sin.sin_addr.s_addr = inet_addr(CLI_IP); //服务器IP地址
struct msg sndbuf;
sndbuf.type = 'L'; //登录请求
printf("请输入ID>>> ");
scanf("%s",sndbuf.name);
//strcpy(sndbuf.name, "aaa");
//发送数据,发送给服务器
if(sendto(cfd, &sndbuf, sizeof(sndbuf), 0, (struct sockaddr*)&sin, sizeof(sin)) < 0)
{
ERR_MSG("sendto");
return -1;
}
//printf("发送成功\n");
struct sockaddr_in rcvaddr; //存储数据包是从谁哪里来的
socklen_t addrlen = sizeof(rcvaddr);
//创建进程
pid_t cpid = fork();
//父进程用于发送
if(cpid > 0)
{
while(1)
{
memset(sndbuf.text, 0, sizeof(sndbuf.text));
//printf("请输入>>> ");
scanf("%s",sndbuf.text);
if(strcmp("quit",sndbuf.text) == 0)
{
sndbuf.type = 'Q'; //下线请求
//发送数据给客户端
if(sendto(cfd,&sndbuf,sizeof(sndbuf),0,(struct sockaddr*)&sin,sizeof(sin)) < 0)
{
ERR_MSG("sendto");
return -1;
}
exit(0);
}
else
{
sndbuf.type = 'C'; //聊天请求
//发送数据给客户端
if(sendto(cfd,&sndbuf,sizeof(sndbuf),0,(struct sockaddr*)&sin,sizeof(sin)) < 0)
{
ERR_MSG("sendto");
return -1;
}
}
}
}
//子进程用于接收
else if(0 == cpid)
{
while(1)
{
struct msg rcvbuf;
recvfrom(cfd, &rcvbuf, sizeof(rcvbuf),0, (struct sockaddr*)&rcvaddr, &addrlen);
if(strcmp(rcvbuf.name,sndbuf.name) == 0)
continue;
//printf("type = %c\n", rcvbuf.type);
//printf("name = %s\n",rcvbuf.name);
//printf("%d\n",ntohs(sin.sin_port));
switch(rcvbuf.type)
{
case 'L':
printf("[%s]登陆成功\n",rcvbuf.name);
memset(&rcvbuf,0,sizeof(rcvbuf));
//do_login();
break;
case 'C':
printf("[%s] :%s\n",rcvbuf.name,rcvbuf.text);
memset(&rcvbuf,0,sizeof(rcvbuf));
//do_chat();
break;
case 'Q':
printf("[%s]已下线\n",rcvbuf.name);
//do_quit();
break;
}
}
}
//关闭文件描述符
close(cfd);
return 0;
}
单词导入
sqlite3.c
#include "head.h"
int main(int argc, const char *argv[])
{
char sql[128] = "";
char* errmsg = NULL;
//如果数据库不存在,则创建后直接打开
//如果存在则直接打开
sqlite3* db;
if(sqlite3_open("./my.db",&db) != SQLITE_OK)
{
fprintf(stderr,"sqlite3_open : %s errcode : %d\n",sqlite3_errmsg(db),sqlite3_errcode(db));
return -1;
}
printf("sqlite3_open success\n");
//创建一个表
//注意:C代码中编写的sql语句与在数据库中编写一致
strcat(sql,"create table if not exists stu (id int,word char,fanyi float);");
if(sqlite3_exec(db,sql,NULL,NULL,&errmsg) != SQLITE_OK)
{
fprintf(stderr,"line: %d sqlite3_exec: %s\n",__LINE__,errmsg);
return -1;
}
printf("create table stu success\n");
char c = 0;
while(1)
{
system("clear");
printf("------------------------\n");
printf("--------1. 增-----------\n");
printf("--------2. 删-----------\n");
printf("--------3. 改-----------\n");
printf("--------4. 查-----------\n");
printf("--------5. 导入---------\n");
printf("--------6. 退出---------\n");
printf("------------------------\n");
printf("请选择功能>>> ");
c = getchar();
while(getchar() != 10);
switch(c)
{
case '1':
do_insert(db);
break;
case '2':
do_delete(db);
break;
case '3':
do_update(db);
break;
case '4':
do_select(db);
break;
case '5':
do_insertall(db);
break;
case '6':
goto END;
break;
default:
printf("输入错误,请重新输入\n");
}
printf("输入任意字符清屏>>> \n");
while(getchar() != 10);
}
END:
//关闭数据库
if(sqlite3_close(db) != SQLITE_OK)
{
fprintf(stderr,"sqlite3_close : %s errcode : %d\n",sqlite3_errmsg(db),sqlite3_errcode(db));
return -1;
}
return 0;
}
func.c
#include "head.h"
void do_insert(sqlite3* db)
{
char sql[128] = "";
char* errmsg = NULL;
int id;
char name[32] = "";
float score;
printf("请输入id>>> ");
scanf("%d",&id);
while(getchar() != 10);
printf("请输入name>>> ");
scanf("%s",name);
while(getchar() != 10);
printf("请输入score>>> ");
scanf("%f",&score);
while(getchar() != 10);
sprintf(sql,"INSERT INTO stu VALUES (%d,\"%s\",%g);",id,name,score);
if(sqlite3_exec(db,sql,NULL,NULL,&errmsg) != SQLITE_OK)
{
fprintf(stderr,"line: %d sqlite3_exec: %s\n",__LINE__,errmsg);
return ;
}
printf("增 成功\n");
return ;
}
void do_delete(sqlite3* db)
{
char sql[128] = "";
char* errmsg = NULL;
int id;
printf("输入要删除信息的id号>>> ");
scanf("%d",&id);
sprintf(sql,"DELETE FROM stu WHERE id = %d;",id);
if(sqlite3_exec(db,sql,NULL,NULL,&errmsg) != SQLITE_OK)
{
fprintf(stderr,"line: %d sqlite3_exec: %s\n",__LINE__,errmsg);
return ;
}
printf("删 成功\n");
return ;
}
void do_update(sqlite3* db)
{
char sql[128] = "";
char* errmsg = NULL;
int id;
char update[32] = "";
float score;
printf("输入修改的字段>>> ");
scanf("%s",update);
printf("输入修改后的值>>> ");
scanf("%f",&score);
printf("输入id>>> ");
scanf("%d",&id);
sprintf(sql,"UPDATE stu SET %s = %f WHERE id = %d;",update,score,id);
if(sqlite3_exec(db,sql,NULL,NULL,&errmsg) != SQLITE_OK)
{
fprintf(stderr,"line: %d sqlite3_exec: %s\n",__LINE__,errmsg);
return ;
}
printf("改 成功\n");
return ;
}
/*
int select_callBack(void *arg,int columns,char **column_text,char **column_name)
{
if(0 == *(int*)arg)
{
for(int i=0;i<columns;i++)
{
printf("%s\t",column_name[i]);
}
putchar(10);
*(int*)arg = 1;
}
for(int i=0;i<columns;i++)
{
printf("%s\t",column_text[i]);
}
putchar(10);
return 0;
}
*/
int do_select(sqlite3* db)
{
int flag = 0;
char** pres = NULL;
int row,column;
char sql[128] = "";
char* errmsg = NULL;
sprintf(sql,"select * from stu;");
if(sqlite3_get_table(db,sql,&pres,&row,&column,&errmsg) != SQLITE_OK)
{
fprintf(stderr,"line: %d sqlite3_get_table: %s\n",__LINE__,errmsg);
return -1;
}
printf("row = %d column = %d\n",row,column);
int index = 0;
for(int i=0;i<(row+1);i++)
{
for(int j=0;j<column;j++)
printf("%s\t",pres[index++]);
putchar(10);
}
putchar(10);
#if 0
for(int i=0;i<(row+1)*column;i++)
{
printf("%s\t",pres[i]);
if(i%column == column-1)
putchar(10);
}
#endif
sqlite3_free_table(pres);
pres = NULL;
printf("查 成功\n");
return 0;
}
int do_insertall(sqlite3* db)
{
//打开一个文件
FILE *fp=fopen("./dict.txt","r");
if(NULL==fp)
{
perror("fopen");
return -1;
}
//printf("电子词典正在导入请稍后>>>\n");
//指定的文件中格式化获取数据
//存储每一行数据
char buf[512]="";
char get_word[128]="";
char get_fanyi[128]="";
char sql[128]="";
char *errmsg=NULL;
int id = 0;
//循环读取每一行内容
while(1)
{
id++;
bzero(get_word,sizeof(get_word));
bzero(get_fanyi,sizeof(get_fanyi));
if(fgets(buf,sizeof(buf),fp)==NULL)
{
printf("单词导入完毕\n");
return -1;
}
buf[strlen(buf)-1]='\0';
for(int i=0;buf[i]!='\0' ;i++)
{
if(buf[i]==' ' && buf[i+1]==' ')
{
strncpy(get_word,buf,i);
}
if(buf[i]==' ' && buf[i+1]==' ' && buf[i+2]!=' ')
{
strcpy(get_fanyi,buf+i+2);
break;
}
}
bzero(sql,sizeof(sql));
sprintf(sql,"insert into stu values(%d,\"%s\",\"%s\");",id,get_word,get_fanyi);
if(sqlite3_exec(db,sql,NULL,NULL,&errmsg)!=SQLITE_OK)
{
fprintf(stderr,"line:%d sqlite3_exec:%s\n",__LINE__,errmsg);
return -1;
}
}
//关闭文件描述符
fclose(fp);
return 0;
}