服务器
server.c
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
typedef struct msg
{
char code; //L:登录 B:广播 Q:退出
char name[32];
char text[128];
} msg_t;
typedef struct node_t
{
struct sockaddr_in addr;
struct node_t *next;
} link_node_t, *link_list_t;
link_node_t *CreateLinkList();
void process_login(int sockfd, link_list_t p, msg_t msg, struct sockaddr_in caddr);
void process_chat(int sockfd, link_node_t *p, msg_t msg, struct sockaddr_in caddr);
void process_quit(int sockfd, link_list_t head, msg_t msg, struct sockaddr_in caddr);
int main(int argc, char const *argv[])
{
int sockfd;
struct sockaddr_in serveraddr, clientaddr;
socklen_t len;
char buf[128] = "";
pid_t pid;
msg_t msg;
if (argc != 3)
{
printf("Usage:%s <ip> <port>\n", argv[0]);
return -1;
}
//1.创建套接字
sockfd = socket(AF_INET, SOCK_DGRAM, 0); //AF_INET:ipv4;SOCK_DGRAM:UDP
if (sockfd < 0)
{
perror("socket error");
return -1;
}
printf("socket ok.\n");
//2.填充结构体
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(atoi(argv[2])); //htons:主机字节序->网络字节序
serveraddr.sin_addr.s_addr = inet_addr(argv[1]); //inet_addr:将argv[1]所指的字符串转换成32位的网络字节序二进制值
len = sizeof(struct sockaddr_in);
//3.绑定
if (bind(sockfd, (struct sockaddr *)&serveraddr, len) < 0)
{
perror("bind error");
return -1;
}
printf("bind ok.\n");
if ((pid = fork()) < 0)
{
perror("fork error");
return -1;
}
else if (pid == 0) //子进程,接收数据
{
link_list_t p = CreateLinkList();
while (1)
{
if ((recvfrom(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&clientaddr, &len)) < 0)
{
perror("recvfrom error");
return -1;
}
switch (msg.code)
{
case 'L':
process_login(sockfd, p, msg, clientaddr);
break;
case 'B':
process_chat(sockfd, p, msg, clientaddr);
break;
case 'Q':
process_quit(sockfd, p, msg, clientaddr);
break;
}
}
}
else //父进程,发给子进程数据,让子进程去转发
{
memset(&msg, 0, sizeof(msg));
strcpy(msg.name, "server");
msg.code = 'B';
while (1)
{
fgets(msg.text, 128, stdin);
msg.text[strlen(msg.text) - 1] = '\0';
sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&serveraddr, len);
}
}
close(sockfd);
return 0;
}
//创建有头单向空链表
link_node_t *CreateLinkList()
{
//molloc一个头节点
link_list_t p = (link_list_t)malloc(sizeof(link_node_t));
if (NULL == p)
{
printf("CreateLinkList error.\n");
return NULL;
}
p->next = NULL;
return p;
}
//登录函数
void process_login(int sockfd, link_list_t p, msg_t msg, struct sockaddr_in clientaddr)
{
link_list_t pnew = p->next;
strcpy(msg.text, msg.name);
msg.text[strlen(msg.text)] = '\0';
while (pnew != NULL)
{
sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&(pnew->addr), sizeof(pnew->addr));
pnew = pnew->next;
}
pnew = (link_list_t)malloc(sizeof(link_node_t));
if (NULL == pnew)
{
printf("pnew malloc failed\n");
}
pnew->addr = clientaddr;
pnew->next = p->next;
p->next = pnew;
printf("connect ip is %s port is %d.\n", inet_ntoa((pnew->addr).sin_addr), ntohs((pnew->addr).sin_port));
strcat(msg.text, " login.");
puts(msg.text);
}
//聊天
void process_chat(int sockfd, link_node_t *p, msg_t msg, struct sockaddr_in clientaddr)
{
link_list_t pnew = p->next;
char s[128] = "";
printf("connect ip is %s port is %d.\n", inet_ntoa((pnew->addr).sin_addr), ntohs((pnew->addr).sin_port));
sprintf(s, "%s said: %s", msg.name, msg.text);
strcpy(msg.text, s);
puts(msg.text);
while (pnew != NULL)
{
if (memcmp(&clientaddr, &pnew->addr, sizeof(clientaddr)) != 0)
{
if (sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&(pnew->addr), sizeof(pnew->addr)) < 0)
{
perror("fail to sendto");
}
}
pnew = pnew->next;
}
}
//客户端退出
void process_quit(int sockfd, link_list_t head, msg_t msg, struct sockaddr_in caddr)
{
link_list_t p = head;
link_list_t pdel = NULL;
link_list_t pnew = p->next;
printf("connect ip is %s port is %d.\n", inet_ntoa((pnew->addr).sin_addr), ntohs((pnew->addr).sin_port));
sprintf(msg.text, "%s offline.", msg.name);
puts(msg.text);
while (p->next)
{
if (memcmp(&caddr, &p->next->addr, sizeof(caddr)) == 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;
}
}
}
客户端
client.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
typedef struct msg
{
char code;
char name[32];
char text[128];
} msg_t;
int main(int argc, char const *argv[])
{
int sockfd;
struct sockaddr_in serveraddr;
socklen_t len;
char buf[128] = "";
msg_t msg; //保存消息
pid_t pid;
if (argc != 3)
{
printf("Usage:%s <ip> <port>\n", argv[0]);
return -1;
}
//1.创建套接字
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
{
perror("socket error");
return -1;
}
printf("socket ok\n");
//2.填充结构体
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(atoi(argv[2]));
serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
len = sizeof(struct sockaddr_in); //sizeof(serveraddr) 完全没区别
printf("Input name>");
fgets(msg.name, sizeof(msg.name), stdin);
msg.name[strlen(msg.name) - 1] = '\0';
msg.text[0] = '\0';
msg.code = 'L';
if (sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&serveraddr, len) < 0)
{
perror("sendto error");
return -1;
}
//循环收发消息,用父子进程
pid = fork();
if (pid < 0)
{
perror("fork error");
return -1;
}
else if (pid == 0) //子进程
{
while (1)
{
recvfrom(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&serveraddr, &len);
puts(msg.text);
}
}
else
{
while (1)
{
printf("Input text>>>");
fgets(msg.text, 128, stdin);
msg.text[strlen(msg.text) - 1] = '\0';
if (strcmp(msg.text, "quit") == 0)
{
msg.code = 'Q';
if (sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&serveraddr, len) < 0)
{
perror("sendto error");
return -1;
}
kill(getpid(), SIGKILL);
exit(0);
}
else
{
msg.code = 'B';
sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&serveraddr, len);
}
}
wait(NULL);
}
close(sockfd);
return 0;
}
Makefile
Makefile
all:
gcc server.c -o server
gcc client.c -o client
clean:
rm server client