多人聊天室程序(3人一组)
linux下 用c语言实现简单的网络聊天室程序,该程序主要包含两部分,分别是
服务器程序和客户端程序。
一、服务器主要功能:
(1)
服务器可以在特定的端口监听客户端的连接请求。
(2)
客户端连接成功后,向客户端发送登录成功信息。
(3)
多个客户端可以同时登录。
(4)
可以向在线的所有客户端发送消息。
(5)
通过日志文件记录客户端登录、退出信息。
二、客户端主要功能:
(1)
能连接到服务器。
(2)
能和服务器进行数据交互。
提供参考实例,【Linux 系统下的简单聊天室(C语言实现) 】,链接:https://www.cnblogs.com/zhengguorong/p/15734236.html
【该实例可正常运行客户端和服务端】
这个课题任务量可不小啊,以下代码能实现服务器和客户端的连接和通信,给你参考。
如果有帮助,望采纳!
server.c
//server.c
/*
广播:服务器 p 接收客户端发送来的消息,再将该消息广播给其他客户端
点播:服务器 p 接收客户端发送来的消息,再将消息转发给制定ip的客户端
运行格式: ./server 端口号
*/
#include "myhead.h"
struct clientlist *head;
struct clientlist
{
int fd;
char ip[16];
unsigned short port;
struct clientlist *next;
};
//节点初始化
struct clientlist *node_init(void)
{
struct clientlist *p;
p = (struct clientlist *)malloc(sizeof(struct clientlist));
if(p == NULL)
{
perror("malloc failure");
return NULL;
}
memset(p, 0, sizeof(struct clientlist));
p->next = NULL;
return p;
}
//插入节点
void insert_node(struct clientlist *new)
{
struct clientlist *p;
if(new == NULL)
{
printf("new is NULL!\n");
return;
}
p = head;
while(p->next != NULL)
{
p = p->next;
}
new->next = p->next;
p->next = new;
}
//删除节点
void del_node(struct clientlist *del)
{
struct clientlist *p;
if(head == NULL || del == NULL)
{
printf("head or del is NULL!\n");
return ;
}
p = head;
while(p->next != del)
{
p = p->next;
}
p->next = del->next;
del->next = NULL;
}
//接收客户端的消息
void *recv_msg(void *arg)
{
int ret;
char *c;
char buf[6] = {0};
char msg[100] = {0};
char msg1[100] = {0};
struct clientlist *client = (struct clientlist *)arg;
struct clientlist *p;
//接收客户端的消息
while(1)
{
ret = recv(client->fd, msg, sizeof(msg), 0);
if(ret == 0) //客户端退出了
{
del_node(client);
free(client);
break;
}
printf("client %d : %s\n", client->port, msg);
p = head->next;
while(p != NULL)
{
if(p->fd != client->fd && strncmp(msg, "broadcast", 9) == 0) //广播
{
send(p->fd, msg, strlen(msg), 0);
}
else //点播
{
c = strchr(msg, ':'); //获取字符串中第一次出现':'的地址
/* if(p->fd != client->fd && strncmp(msg, client->ip, (int)(c-msg)) == 0) //验证ip
{
send(p->fd, msg+(int)(c-msg), strlen(msg)-(int)(c-msg), 0);
} */
sprintf(buf, "%d", p->port); //将端口号由int转为char
if(p->fd != client->fd && strncmp(buf, msg, (int)(c-msg)) == 0) //验证端口号
{
sprintf(msg1, "%hu : %s", client->port, msg+(int)(c-msg)+1);
send(p->fd, msg1, strlen(msg1), 0);
break;
}
}
p = p->next;
memset(buf, 0, sizeof(buf));
}
memset(msg, 0, sizeof(msg));
memset(msg1, 0, sizeof(msg1));
}
pthread_exit(NULL);
}
int main(int argc, char *argv[])
{
int i = 0;
int sockfd, new_sockfd, addr_len, ret;
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
pthread_t tid[100];
struct clientlist *new = NULL;
head = node_init();
addr_len = sizeof(struct sockaddr_in);
memset(&server_addr, 0, sizeof(struct sockaddr_in));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(atoi(argv[1])); //小端序转大端序
//INADDR_ANY就是指定地址为0.0.0.0的地址,这个地址事实上表示不确定地址,或“所有地址”、“任意地址”。
server_addr.sin_addr.s_addr = htonl(INADDR_ANY); //自动获取本地ip,INADDR_ANY 只能用于htonl()
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd == -1)
{
perror("socket fail");
return -1;
}
ret = bind(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_in));
if(ret == -1)
{
perror("bind fail");
return -1;
}
ret = listen(sockfd, 5);
if(ret == -1)
{
perror("listen fail");
return -1;
}
while(1)
{
new_sockfd = accept(sockfd, (struct sockaddr *)&client_addr, &addr_len);
if(new_sockfd == -1)
{
perror("accept fail");
return -1;
}
printf("client ip : %s\n", inet_ntoa(client_addr.sin_addr)); //大端转小端
printf("client port : %hu\n", ntohs(client_addr.sin_port));
new = node_init();
new->fd = new_sockfd;
strcpy(new->ip, inet_ntoa(client_addr.sin_addr));
new->port = ntohs(client_addr.sin_port);
insert_node(new);
pthread_create(&tid[i], NULL, recv_msg, new);
i++;
}
close(sockfd);
close(new_sockfd);
return 0;
}
client.c
//client.c
/*
运行格式:
./client 自身端口号 服务器端口号
输入格式:
广播 --- broadcast:xxxxxx
点播 --- 对方端口号:xxxxxx
*/
#include "myhead.h"
int sockfd;
pthread_t tid;
void fun(int sig)
{
pthread_cancel(tid);
close(sockfd);
exit(0);
}
void *recv_msg(void *arg)
{
int ret;
char msg[100] = {0};
//接收服务器的消息
while(1)
{
ret = recv(*((int *)arg), msg, sizeof(msg), 0);
if(ret == 0)
break;
printf("%s\n", msg);
memset(msg, 0, sizeof(msg));
}
pthread_exit(NULL);
}
int main(int argc, char *argv[])
{
int ret;
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
char msg[100] = {0};
signal(SIGINT, fun);
memset(&server_addr, 0, sizeof(struct sockaddr_in));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(atoi(argv[2])); //小端序转大端序
server_addr.sin_addr.s_addr = inet_addr("192.168.91.26");
memset(&client_addr, 0, sizeof(struct sockaddr_in));
client_addr.sin_family = AF_INET;
client_addr.sin_port = htons(atoi(argv[1])); //小端序转大端序
client_addr.sin_addr.s_addr = inet_addr("192.168.91.26");
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd == -1)
{
perror("socket fail");
return -1;
}
ret = bind(sockfd, (struct sockaddr *)&client_addr, sizeof(struct sockaddr_in));
if(ret == -1)
{
perror("bind fail");
return -1;
}
ret = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_in));
if(ret == -1)
{
perror("connect fail");
return -1;
}
pthread_create(&tid, NULL, recv_msg, &sockfd);
//给服务器发送消息
while(1)
{
scanf("%s", msg);
send(sockfd, msg, strlen(msg), 0);
memset(msg, 0, sizeof(msg));
}
return 0;
}
myhead.h
//myhead.h
#ifndef __MYHEAD_H__
#define __MYHEAD_H__
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <signal.h>
#include <pthread.h>
#include <math.h>
#include <time.h>
#include <utime.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/msg.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdarg.h>
#include <pwd.h>
#include <grp.h>
#include <utmp.h>
#endif
C语言实现--基于UDP的多人在线聊天室
如有帮助,望采纳,辛苦写的
服务端
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <signal.h>
//消息类型
enum type_t
{
Login = 1, //登录
Chat, //聊天
Quit, //退出
};
//定义描述消息结构体
typedef struct msg_t
{
int type; //消息类型:登录 聊天 退出
char name[32]; //姓名
char text[128]; //消息正文
} MSG_t;
//链表的节点结构体
typedef struct node_t
{
struct sockaddr_in addr; //数据域
struct node_t *next; //指针域
} link_t;
link_t *createLink(void);
void client_login(int sockfd, link_t *p, struct sockaddr_in clientaddr, MSG_t msg);
void client_chat(int sockfd, link_t *p, struct sockaddr_in clientaddr, MSG_t msg);
void client_quit(int sockfd, link_t *p, struct sockaddr_in clientaddr, MSG_t msg);
int main(int argc, char const *argv[])
{
int sockfd;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
{
perror("socket err.");
return -1;
}
//填充服务器的ip和port
struct sockaddr_in serveraddr, clientaddr;
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(atoi(argv[1]));
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
socklen_t len = sizeof(clientaddr);
if (bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
{
perror("bind err.");
return -1;
}
MSG_t msg;
//创建子进程,父进程接收客户端的信息并处理,子进程转发消息
pid_t pid = fork();
if (pid < 0)
{
perror("fork err.");
return -1;
}
else if (pid == 0)
{
//创建一个空的有头单向链表
link_t *p = createLink();
while (1) //收到客户端的请求,处理请求
{
if (recvfrom(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&clientaddr, &len) < 0)
{
perror("recvfrom err.");
return -1;
}
switch (msg.type)
{
case Login: //登录
client_login(sockfd, p, clientaddr, msg);
break;
case Chat:
client_chat(sockfd, p, clientaddr, msg);
break;
case Quit:
client_quit(sockfd, p, clientaddr, msg);
break;
}
}
}
else
{
while (1) //服务器发通知
{
msg.type = Chat;
//给结构体中的数组成员变量赋值,一般使用strcpy进行赋值
strcpy(msg.name, "server");
//获取终端输入
fgets(msg.text, sizeof(msg.text), stdin);
//解决发送信息时,会将换行符发送过去的问题
if (msg.text[strlen(msg.text) - 1] == '\n')
msg.text[strlen(msg.text) - 1] = '\0';
//将信息发送给同一局域网的其他客户端
sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&serveraddr,
sizeof(serveraddr));
}
}
//程序结束时,关闭套接字描述符
close(sockfd);
return 0;
}
//链表函数 -- 创建一个空的有头单向链表
link_t *createLink(void)
{
link_t *p = (link_t *)malloc(sizeof(link_t));
if (p == NULL)
{
perror("malloc head node err.");
return NULL;
}
p->next = NULL;
return p;
}
//登录函数 -- 将客户端的clientaddr保存到链表中,循环链表告诉其他用户谁登录了
void client_login(int sockfd, link_t *p, struct sockaddr_in clientaddr, MSG_t msg)
{
//1.告诉其他用户登录的新用户是谁
strcpy(msg.name,"server"); //发送消息的人是服务端
sprintf(msg.text, "%s login!", msg.name); //服务端发送新登陆的用户名
//循环发送给之前已经登陆的用户
while (p->next != NULL)
{
p = p->next;
sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&(p->addr), sizeof(p->addr));
}
//上面的代码运行结束后,此时链表指针已经指向了最后一个用户
//2.创建一个新节点保存新连接的客户端地址 ,连接到链表结尾
link_t *pnew = (link_t *)malloc(sizeof(link_t));
if (pnew == NULL)
{
perror("malloc new node err.");
}
//初始化
pnew->addr = clientaddr;
pnew->next = NULL;
//链接 p是最后一个节点的地址
p->next = pnew;
}
//聊天信息发送函数 -- 将消息转发给所有的用户,除去发送消息的自己
void client_chat(int sockfd, link_t *p, struct sockaddr_in clientaddr, MSG_t msg)
{
//从链表头开始遍历
while (p->next != NULL)
{
p = p->next;
//memcmp函数,比较内存区域a和b的前n个字节
//参数--区域a,区域b,比较字节数n
//返回值--a<b返回负数,a=b返回0,a<b返回正数
if(memcmp(&(p->addr), &clientaddr, sizeof(clientaddr)) != 0)
{
//只要判断出用户地址和发送消息的用户地址不同,就将消息发送给该用户
sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&(p->addr),
sizeof(p->addr));
}
}
//在服务器中打印发送的消息
printf("%s said %s\n", msg.name, msg.text);
}
//退出函数 -- 将客户端的clientaddr从链表中删除,循环链表告诉其他用户谁退出了
void client_quit(int sockfd, link_t *p, struct sockaddr_in clientaddr, MSG_t msg)
{
link_t *pdel = NULL;
//从头开始遍历查找要删除的节点
while (p->next != NULL)
{
//如果循环到的地址是要删除的用户,则删除
if (memcmp(&(p->next->addr), &clientaddr, sizeof(clientaddr)) == 0)
{
//删除指定用户
pdel = p->next;
p->next = pdel->next;
free(pdel);
pdel = NULL;
}
else
{
//如果不是要删除的用户,则向其发送指定用户要删除的消息
sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&(p->next->addr),
sizeof(p->next->addr));
p = p->next;
}
}
}
客户端
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <signal.h>
//消息类型
enum type_t
{
Login = 1, //登录
Chat, //聊天
Quit, //退出
};
//定义描述消息结构体
typedef struct msg_t
{
int type; //消息类型:登录 聊天 退出
char name[32]; //姓名
char text[128]; //消息正文
} MSG_t;
int main(int argc, char const *argv[])
{
int sockfd;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
{
perror("socket err.");
return -1;
}
//填充结构体
struct sockaddr_in serveraddr;
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(atoi(argv[2]));
serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
MSG_t msg;
//UDP客户端不用bind地址,可以直接发送自己的登陆消息
msg.type = Login; //登录
printf("please input login name>>");
fgets(msg.name, sizeof(msg.name), stdin);
if (msg.name[strlen(msg.name) - 1] == '\n')
msg.name[strlen(msg.name) - 1] = '\0';
sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&serveraddr,
sizeof(serveraddr));
//创建父子进程:父进程收消息 子进程发送消息
pid_t pid = fork();
if (pid < 0)
{
perror("fork err.");
return -1;
}
else if (pid == 0)
{
while (1) //发
{
fgets(msg.text, sizeof(msg.text), stdin);
if (msg.text[strlen(msg.text) - 1] == '\n')
msg.text[strlen(msg.text) - 1] = '\0';
//判断发送的消息是否为“quit”退出消息
if (strncmp(msg.text, "quit", 4) == 0)
{
msg.type = Quit;
sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&serveraddr,
sizeof(serveraddr));
//杀死父进程
kill(getppid(), SIGKILL);
//退出循环,子进程结束
break;
}
else
{
msg.type = Chat;
sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&serveraddr,
sizeof(serveraddr));
}
}
}
else
{
while (1) //收
{
if (recvfrom(sockfd, &msg, sizeof(msg), 0, NULL, NULL) < 0)
{
perror("recvfrom err.");
return -1;
}
printf("%s said %s\n", msg.name, msg.text);
}
}
close(sockfd);
return 0;
}