目录
网络聊天室
服务器:
库:
#ifndef __CHATROOM_H__
#define __CHATROOM_H__
#include<myhead.h>
struct msg
{
char type;
char name[20];
char text[128];
};
#define ERR_MSG(msg) do{\
printf("%d:",__LINE__);\
perror(msg);\
}while(0);
/*服务器的所需*/
struct addmsg{
char name[20];
struct sockaddr_in cin;
};
typedef struct Link
{
union{
int len;
struct addmsg data;
};
struct Link *next;
}linkList,*linkListPtr;
//创建UDP服务器模型函数
int UDP_ser_create(int *psfd,const char *pIP, unsigned short port);
//用户信息保存链表创建函数
linkListPtr linklist_creat();
//创建数据发送函数
int msg_send(int sfd,linkListPtr L, char *text, char *name, int mode);
//创建头插函数
int cli_addrmsg_save(linkListPtr L, char *name, struct sockaddr_in cin);
//创建链表的按值查找并任意位置删除函数
int linkList_selete_value(linkListPtr L, struct sockaddr_in cin);
//查找对应的地址信息是否已存在函数
int linkList_search_value(linkListPtr L, struct sockaddr_in cin);
/****************服务器所需***************/
/********客户端所需**************/
/********客户端所需**************/
#endif
源文件:
#include<myhead.h>
#include"chatroom.h"
int UDP_ser_create(int *psfd, const char *pIP, unsigned short port){
//创建套接字文件
if((*psfd = socket(AF_INET, SOCK_DGRAM, 0))<0){
ERR_MSG("socket error");
return -1;
}
//允许端口被快速复用
int resue = 1;
if(-1 == setsockopt(*psfd, SOL_SOCKET, SO_REUSEADDR, &resue, sizeof(resue))){
ERR_MSG("setsockopt error");
return -1;
}
//定义地址信息结构体用于绑定bind
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
sin.sin_addr.s_addr = inet_addr(pIP);
//绑定
if(bind(*psfd, (struct sockaddr*)&sin, sizeof(sin))==-1){
ERR_MSG("bind error");
return -1;
}
return 0;
}
//用户信息保存链表创建函数
linkListPtr linklist_creat(){
linkListPtr L = (linkListPtr)malloc(sizeof(linkList));
if(NULL == L){
return NULL;
}
printf("用户链表申请成功\n");
//申请成功,初始化
L->len = 0;
L->next = NULL;
return L;
}
//创建数据发送函数
int msg_send(int sfd, linkListPtr L, char *text, char *name, int mode){
if(NULL == L){
printf("链表不存在\n");
return -1;
}
linkListPtr p = L->next;
//printf("%s,%ld\n",text,strlen(text));
char buf[] = "**********系统消息**********";
if(1 == mode){
//说明是服务器发送的系统消息
while(p!=NULL){
if(strcmp(name,p->data.name)){
sendto(sfd, buf, strlen(buf), 0, (struct sockaddr*)&p->data.cin, sizeof(p->data.cin));
sendto(sfd, text, strlen(text), 0, (struct sockaddr*)&p->data.cin, sizeof(p->data.cin));
sendto(sfd, buf, strlen(buf), 0, (struct sockaddr*)&p->data.cin, sizeof(p->data.cin));
}
p = p->next;
}
}else{
//说明是来自用户的,需要转发的消息
while(p!=NULL){
if(strcmp(name,p->data.name)){
sendto(sfd, text, 150, 0, (struct sockaddr*)&p->data.cin, sizeof(p->data.cin));
}
p = p->next;
}
}
return 0;
}
//创建头插函数
int cli_addrmsg_save(linkListPtr L, char *name, struct sockaddr_in cin){
linkListPtr p = (linkListPtr)malloc(sizeof(linkList));
if(NULL == p){
return -1;
}
//保存数据
strcpy(p->data.name,name);
p->data.cin = cin;
p->next = L->next;
L->next = p;
return 0;
}
//创建链表的按值查找并任意位置删除函数
int linkList_selete_value(linkListPtr L, struct sockaddr_in cin){
if(L == NULL){
printf("链表不存在\n");
return -1;
}
linkListPtr p = L;
linkListPtr q = NULL;
while(p->next!=NULL){
if(p->next->data.cin.sin_family == cin.sin_family&&\
p->next->data.cin.sin_port == cin.sin_port&&\
p->next->data.cin.sin_addr.s_addr == cin.sin_addr.s_addr){
q = p->next;
p->next = q->next;
free(q);
q = NULL;
}
if(p->next == NULL){
break;
}
p=p->next;
}
return 0;
}
//查找对应的地址信息是否已存在函数
int linkList_search_value(linkListPtr L, struct sockaddr_in cin){
if(L == NULL){
printf("链表不存在\n");
return -1;
}
linkListPtr p = L;
while(p->next!=NULL){
if(p->next->data.cin.sin_family == cin.sin_family&&\
p->next->data.cin.sin_port == cin.sin_port&&\
p->next->data.cin.sin_addr.s_addr == cin.sin_addr.s_addr){
return 1;
}
p=p->next;
}
return 0;
}
测试文件:
#include<myhead.h>
#include"chatroom.h"
int main(int argc, const char *argv[])
{
//通过外部传参传入IP地址
if(argc!=2){
printf("the number of arguments is wrong!\n");
printf("usage:./xxx IP\n");
return -1;
}
//一些准备用的变量
int sfd = -1;
unsigned short port = 8888;
//创建UDP服务器模型
if(-1==UDP_ser_create(&sfd, argv[1], port)){
printf("服务器创建失败\n");
return -1;
}
printf("服务器创建成功\n");
//创建保存用户信息的链表
linkListPtr L;
if((L = linklist_creat()) == NULL){
printf("链表申请失败\n");
return -1;
}
char name11[20] = "";
//子进程号
pid_t pid;
pid = fork();
if(pid <0){
ERR_MSG("fork error");
return -1;
}else if(0 == pid){
char buf[128] = "";
while(1){
bzero(buf,sizeof(buf));
//阻塞等待从终端获取消息以发送
//fgets(buf, sizeof(buf), stdin);
scanf("%s",buf);
//去除垃圾字符
//getchar();
buf[strlen(buf)] = 10;
//调用发送函数
if(-1 == msg_send(sfd, L, buf, name11, 1)){
printf("发送失败\n");
}
}
exit(EXIT_SUCCESS);
}else{
//创建存储收到的信息的结构体变量
struct msg climsg;
//存储获取用户地址信息的结构体变量
struct sockaddr_in cin;
socklen_t cinlen = sizeof(cin);
//用来存储登录信息
char loadmsg[128] = "";
char buf1[151] = "";
while(1){
bzero(loadmsg,sizeof(loadmsg));
bzero(buf1,sizeof(buf1));
bzero(climsg.name,sizeof(climsg.name));
bzero(climsg.text,sizeof(climsg.text));
recvfrom(sfd, &climsg, sizeof(climsg), 0, (struct sockaddr*)&cin, &cinlen);
//printf("我们收到消息\n");
if('L' == climsg.type){
if(linkList_search_value(L, cin)){
//说明该地址信息已存在,提示无需重复登录
snprintf(loadmsg, sizeof(loadmsg), "您已在线,无需重复登录\n");
sendto(sfd, loadmsg, strlen(loadmsg), 0, (struct sockaddr*)&cin, sizeof(cin));
}
//登录
else{
if(-1 == cli_addrmsg_save(L, climsg.name, cin)){
printf("登录失败\n");
return -1;
}
snprintf(loadmsg, sizeof(loadmsg), "[%s]:上线了!", climsg.name);
if(-1 == msg_send(sfd, L, loadmsg, climsg.name, 1)){
printf("发送失败\n");
}
}
}else if('C' == climsg.type){
if(linkList_search_value(L, cin)){
//如果地址信息已保存在链表中,说明在线
//群聊
snprintf(buf1, sizeof(buf1), "[%s] :%s", climsg.name, climsg.text);
if(-1 == msg_send(sfd, L, buf1, climsg.name , 0)){
printf("发送失败\n");
}
}else{
//如果地址信息不在链表中,提示先登录
snprintf(loadmsg, sizeof(loadmsg), "请登录\n");
sendto(sfd, loadmsg, strlen(loadmsg), 0, (struct sockaddr*)&cin, sizeof(cin));
}
}else{
if(linkList_search_value(L, cin)){
//如果已存在就退出
//退出
linkList_selete_value(L, cin);
snprintf(loadmsg, sizeof(loadmsg), "[%s]退出群聊!", climsg.name);
if(-1 == msg_send(sfd, L, loadmsg, name11, 1)){
printf("发送失败\n");
}
}
//如果不存在无需退出
}
}
}
kill(pid,SIGKILL);
wait(NULL);
close(sfd);
return 0;
}
客户端:
测试文件:
#include<myhead.h>
#include"chatroom.h"
#define PORT 8888
#define IP "192.168.114.139"
int main(int argc, const char *argv[])
{
int cfd;
//调用函数创建客户端模型
if((cfd = socket(AF_INET, SOCK_DGRAM, 0))<0){
ERR_MSG("socket error");
return -1;
}
//登录
struct msg climsg;
climsg.type = 'L';
printf("请输入您的昵称:");
scanf("%s",climsg.name);
//组装服务器的地址信息结构体
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT);
sin.sin_addr.s_addr = inet_addr(IP);
if(sendto(cfd, &climsg, sizeof(climsg), 0, (struct sockaddr*)&sin, sizeof(sin))<0){
ERR_MSG("sendto error");
return -1;
}
//登录成功后,发送的消息就是群聊消息
climsg.type = 'C';
//需要的变量
char buf[150] = "";
//创建子进程
pid_t pid;
pid = fork();
if(pid < 0){
ERR_MSG("fork error");
return -1;
}else if(0 == pid){
//子进程
while(1){
//清空数组
bzero(buf,sizeof(buf));
//从终端获取
scanf("%s",climsg.text);
if(!strcmp(climsg.text,"quit")){
climsg.type = 'Q';
if(sendto(cfd, &climsg, sizeof(climsg), 0, (struct sockaddr*)&sin, sizeof(sin))<0){
ERR_MSG("sendto error");
return -1;
}
kill(getppid(),SIGKILL);
break;
}else{
if(sendto(cfd, &climsg, sizeof(climsg), 0, (struct sockaddr*)&sin, sizeof(sin))<0){
ERR_MSG("sendto error");
return -1;
}
}
}
exit(EXIT_SUCCESS);
}else{
//父进程
while(1){
//清空数组
bzero(buf,sizeof(buf));
//接收信息
recv(cfd, buf, sizeof(buf),0);
//输出读取到的数据
printf("%s\n",buf);
}
}
wait(NULL);
close(cfd);
return 0;
}