服务端
相当于一个服务器,接收用户发送的过来的消息(登录消息,文本消息,退出登录消息),然后将其转发给其用户。
基本功能:
1.把新注册用户登陆消息告诉其它用户
2.把新用户插入到用户链表中
3.服务器打印一下
4.把聊天信息发给其它用户
5.发给自己一份
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
typedef struct node_t
{
struct sockaddr_in caddr; //数据域
struct node_t *next; //指针域,指向自身结构体的指针
} link_node_t, *link_list_t;
//1.创建一个空的单向链表(有头单向链表)
link_node_t *CreateEpLinkList();
//2.向单向链表插入数据
void InsertIntoPostLinkList(link_node_t *p, struct sockaddr_in caddr,struct sockaddr_in clientaddr);
//3.遍历单向链表
void ShowLinkList(link_node_t *p, struct sockaddr_in caddr, int sockfd,struct sockaddr_in clientaddr);
//4.删除单向链表中出现的指定数据
void DeleteDataLinkList(link_node_t *p, struct sockaddr_in caddr, int sockfd,struct sockaddr_in clientaddr);
struct mychat
{
char type;
char name[32];
char message[32];
} chat;
int main(int argc, const char *argv[])
{
int sockfd;
pid_t pid;
struct sockaddr_in serveraddr, clientaddr;
socklen_t len = sizeof(clientaddr);
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
{
perror("socket err");
return -1;
}
//printf("sockfd ok\n");
if (argc != 2)
{
printf("Usage:%s <port>\n", argv[0]);可执行文件+端口号
return -1;
}
//填充结构体
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(atoi(argv[1]));
//addr.sin_addr.s_addr = inet_addr(argv[1]);
//自动获取主机ip
//addr.sin_addr.s_addr = inet_addr("0.0.0.0");
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
//绑定
if (bind(sockfd, (struct sockaddr *)&serveraddr, len) < 0)
{
perror("bind err\n");
return -1;
}
//printf("bind ok\n");
printf("Server starts success\n");
//创建父子进程
pid = fork();
if (pid == -1)
{
perror("fork error");
return -1;
}
else if (pid == 0)
{
int recvbyte;
link_node_t *p = CreateEpLinkList();
while (1)
{
if ((recvbyte = recvfrom(sockfd, &chat, sizeof(chat), 0, (struct sockaddr *)&clientaddr, &len)) < 0)
{
perror("recvfrom err");
return -1;
}
if (chat.type == 'L')//'L'表示用户登录
{
if (chat.name[strlen(chat.name) - 1] == '\n')
chat.name[strlen(chat.name) - 1] = '\0';
printf("connect ip is %s\n", inet_ntoa(clientaddr.sin_addr));
printf("%s login\n", chat.name);
InsertIntoPostLinkList(p, clientaddr,clientaddr);
ShowLinkList(p, clientaddr, sockfd,clientaddr);
}
else if (chat.type == 'B')//'B'聊天
{
printf("connect ip is %s\n", inet_ntoa(clientaddr.sin_addr));
printf("%s said:%s\n", chat.name, chat.message);
ShowLinkList(p, clientaddr, sockfd,clientaddr);
}
else if (chat.type == 'Q')//'Q'用户退出
{
printf("%s offline\n", chat.name);
DeleteDataLinkList(p, clientaddr, sockfd,clientaddr);
ShowLinkList(p, clientaddr, sockfd,clientaddr);
}
}
}
else
{
while (1)
{
strcpy(chat.name, "server");
chat.type = 'B';
fgets(chat.message, 128, stdin);
if (chat.message[strlen(chat.message) - 1] == '\n')
chat.message[strlen(chat.message) - 1] = '\0';
sendto(sockfd, &chat, sizeof(chat), 0, (struct sockaddr *)&serveraddr, sizeof(serveraddr));//将内容发送到子进程
};
}
close(sockfd);
return 0;
}
//1.创建一个空的单向链表(有头单向链表)
link_node_t *CreateEpLinkList()
{
link_list_t p = (link_list_t)malloc(sizeof(link_node_t));
if (NULL == p)
{
printf("creat error\n");
return NULL;
}
p->next = NULL;
return p;
}
//2.向单向链表插入数据
void InsertIntoPostLinkList(link_node_t *p, struct sockaddr_in caddr,struct sockaddr_in clientaddr)
{
link_list_t pnew = (link_list_t)malloc(sizeof(link_node_t));
while (p->next != NULL)
{
p = p->next;
}
pnew->caddr = clientaddr;
pnew->next = NULL;
p->next = pnew;
}
//3.遍历单向链表
void ShowLinkList(link_node_t *p, struct sockaddr_in caddr, int sockfd,struct sockaddr_in clientaddr)
{
p = p->next;
while (p != NULL)
{
if (((p->caddr.sin_addr.s_addr) != (clientaddr.sin_addr.s_addr)) || ((p->caddr.sin_port) != (clientaddr.sin_port))) //不给自己发消息
{
sendto(sockfd, &chat, sizeof(chat), 0, (struct sockaddr *)&(p->caddr), sizeof(p->caddr));
}
p = p->next;
}
}
//4.删除单向链表中出现的指定数据
void DeleteDataLinkList(link_node_t *p, struct sockaddr_in caddr, int sockfd,struct sockaddr_in clientaddr)
{
link_node_t *pdel = NULL;
while (p->next != NULL)
{
if ((p->next->caddr.sin_addr.s_addr == clientaddr.sin_addr.s_addr) && (p->next->caddr.sin_port == clientaddr.sin_port)) //说明需要删除q指向的节点
{
pdel = p->next;
p->next = pdel->next;
free(pdel);
pdel = NULL;
}
else
{
sendto(sockfd, &chat, sizeof(chat), 0, (struct sockaddr *)&p->caddr, sizeof(p->caddr));
p = p->next;
}
}
}
客户端
该部分会发送消息和接受服务器转发过来其他人发送的消息登录消息,文本消息,退出登录消息)。
基本功能:
1. 输入用户名,并发给服务器登录信息
2.将文本消息发送给服务器
3.接收服务器转发的其他用户消息
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>
struct mychat
{
char type;
char name[32];
char message[32];
} chat;
int main(int argc, const char *argv[])
{
int sockfd;
pid_t pid;
struct sockaddr_in addr;
socklen_t len;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
{
perror("socket err");
return -1;
}
if (argc != 3)
{
printf("Usage:%s <ip> <port>\n", argv[0]);//可执行文件+ip+端口号
return -1;
}
//填充结构体
addr.sin_family = AF_INET;
addr.sin_port = htons(atoi(argv[2]));
addr.sin_addr.s_addr = inet_addr(argv[1]);
len = sizeof(struct sockaddr_in);
printf("name:");
fgets(chat.name, sizeof(chat.name), stdin);
if (chat.name[strlen(chat.name) - 1] == '\n')
chat.name[strlen(chat.name) - 1] = '\0';
chat.type = 'L';
sendto(sockfd, &chat, sizeof(chat), 0, (struct sockaddr *)&addr, len);
pid = fork();
if (pid == -1)
{
perror("fork error");
return -1;
}
else if (pid == 0)
{
while (1)
{
printf("input:");
fgets(chat.message, 128, stdin);
if (chat.message[strlen(chat.message) - 1] == '\n')
chat.message[strlen(chat.message) - 1] = '\0';
if (strncmp(chat.message, "quit", 4) == 0)
{
chat.type = 'Q';
sendto(sockfd, &chat, sizeof(chat), 0, (struct sockaddr *)&addr, len);
kill(getppid(), SIGKILL);
exit(-1);
}
else
{
chat.type = 'B';
sendto(sockfd, &chat, sizeof(chat), 0, (struct sockaddr *)&addr, len);
}
}
}
else
{
int recvbyte;
while (1)
{
if ((recvbyte = recvfrom(sockfd, &chat, sizeof(chat), 0, NULL, NULL)) < 0)
{
perror("recvfrom err");
return -1;
}
if (chat.type == 'L')
{
printf("%s login\n", chat.name);
}
else if (chat.type == 'B')
printf("%s said:%s\n", chat.name, chat.message);
else if (strncmp(chat.message, "quit", 4) == 0)
{
printf("%s offline\n", chat.name);
}
}
wait(NULL);
}
close(sockfd);
return 0;
}
改了几次了,可能还有没改到位的地方,欢迎指正。