1、udp实现网络聊天室
代码:
/*******************************************/
文件名:chat.h
/*******************************************/
#ifndef CHAT_H
#define CHAT_H
#include <myhead.h>
struct Msg
{
char type;
char name[10];
char languege[128];
};
//绑定服务器
void ser_bind(struct ip_mreqn *imr, struct sockaddr_in *min);
//端口号重新启用
void port_reuse(int sockfd);
//登录界面
void log_menu();
//注册
void log_reg();
//登录
void log_in();
#endif
/*******************************************/
文件名:chat.c
/*******************************************/
#include "chat.h"
//绑定服务器
void ser_bind(struct ip_mreqn *imr, struct sockaddr_in *min)
{
imr->imr_multiaddr.s_addr = inet_addr("224.1.2.3"); //组播ip
imr->imr_address.s_addr = inet_addr("192.168.2.91"); //主机ip
imr->imr_ifindex = 2; //网卡编号
min->sin_family = AF_INET;
min->sin_port = htons(9999);
min->sin_addr.s_addr = inet_addr("224.1.2.3");
}
//端口号重新启用
void port_reuse(int sockfd)
{
//将端口号快速重用
int reuse = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1)
{
perror("setsockopt error");
return;
}
printf("端口号快速重用成功\n");
}
//登录界面
void log_menu()
{
printf("/*******************/\n");
printf("/*****1、登录******/\n");
printf("/*****2、注册******/\n");
printf("/*****0、退出******/\n");
printf("/*******************/\n");
}
//注册
void log_reg()
{
//账号
char reg_acc[21] = "";
//密码
char reg_pass[21] = "";
//再次输入密码
char repass[21] = "";
while (1)
{
printf("请输入注册账号:");
fgets(reg_acc, sizeof(reg_acc), stdin);
reg_acc[strlen(reg_acc) - 1] = 0;
int size = strlen(reg_acc);
if (size <= 0 || size > 20)
{
printf("账号格式错误,请重新输入\n");
continue;
}
else
{
break;
}
}
while (1)
{
printf("请输入密码:");
fgets(reg_pass, sizeof(reg_pass), stdin);
reg_pass[strlen(reg_pass) - 1] = 0;
int size = strlen(reg_pass);
if (size < 6 || size > 20)
{
printf("密码格式错误,请重新输入\n");
continue;
}
else
{
break;
}
}
while (1)
{
printf("请重新输入密码:");
fgets(repass, sizeof(repass), stdin);
repass[strlen(repass) - 1] = 0;
if (strcmp(repass, reg_pass) != 0)
{
printf("密码不一致,请重新输入\n");
continue;
}
else
{
break;
}
}
char buf[60] = "";
//组合信息
FILE *fp = fopen("./text.txt", "w");
if (NULL == fp)
{
perror("fopen error");
return;
}
fprintf(fp, "%s %s\n", reg_acc, reg_pass);
fclose(fp);
fp = fopen("./text.txt", "r");
if (NULL == fp)
{
perror("fopen error");
return;
}
rewind(fp);
fread(buf, 1, sizeof(buf), fp);
fclose(fp);
//追加形式写入新文件
int fd = open("./log.txt", O_WRONLY | O_CREAT | O_APPEND, 0664);
if (fd == -1)
{
perror("open error");
return;
}
write(fd, buf, strlen(buf));
close(fd);
}
//登录
void log_in()
{
char in_acc[21] = "";
char in_pass[21] = "";
//用于读取已注册的信息
FILE *fp=fopen("./log.txt","r");
if(NULL==fp){
perror("fopen error");
return ;
}
while (1)
{
printf("请输入登录账号:");
fgets(in_acc, sizeof(in_acc), stdin);
in_acc[strlen(in_acc) - 1] = 0;
fseek(fp, 0, SEEK_SET);
int len = 2;
//用于判断退出循环
int flag = 0;
//接收数据
char buf1[22] = "";
char buf2[22]="";
while (len == 2)
{
len = fscanf(fp,"%s %s\n",buf1,buf2);
//从所有数据分别比较,符合则退出
if (strcmp(buf1, in_acc) == 0)
{
flag = 1;
break;
}
}
if (flag == 0)
{
printf("账号错误,请重新输入\n");
}
else if (flag == 1)
{
break;
}
}
while (1)
{
printf("请输入登录密码:");
fgets(in_pass, sizeof(in_pass), stdin);
in_pass[strlen(in_pass) - 1] = 0;
fseek(fp, 0, SEEK_SET);
int len = 2;
//接收数据
char buf1[22] = "";
char buf2[22]="";
while (len == 2)
{
len = fscanf(fp,"%s %s",buf1,buf2);
//从所有数据分别比较,符合则退出
if (strcmp(buf2, in_pass) == 0)
{
fclose(fp);
return ;
}
}
printf("密码错误,请重新输入\n");
}
}
/*******************************************/
文件名:main.c
/*******************************************/
#include "chat.h"
int main(int argc, char const *argv[])
{
int sfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sfd == -1)
{
perror("socket error");
return -1;
}
port_reuse(sfd);
struct sockaddr_in sin;
struct ip_mreqn imr;
ser_bind(&imr, &sin);
if (setsockopt(sfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr)) == -1)
{
perror("setsockopt error");
return -1;
}
if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))==-1){
perror("bind error");
return -1;
}
struct Msg msg_send;
struct Msg msg_recv;
//定义用于退出的变量
int flag = 0;
while (1)
{
int key1 = -1;
log_menu();
printf("操作指令:");
scanf("%d", &key1);
getchar();
switch (key1)
{
case 1:
{
log_in();
usleep(1000);
printf("请输入昵称:");
fgets(msg_send.name, sizeof(msg_send.name), stdin);
msg_send.name[strlen(msg_send.name) - 1] = 0;
msg_send.type = 'j';//将此次发送的信息性质定义为加入信息
sendto(sfd, &msg_send, sizeof(msg_send), 0, (struct sockaddr *)&sin, sizeof(sin));
pid_t pid = -1;
pid=fork();
if (pid > 0)
{
while (1)
{
//清除容器需要附加新内容的部分的缓存
memset(msg_send.languege, 0, sizeof(msg_send.languege));
fgets(msg_send.languege, sizeof(msg_send.languege), stdin);
msg_send.languege[strlen(msg_send.languege) - 1] = 0;
msg_send.type = 'r';//将此次发送的信息性质定义为正常阅读对话
if (strcmp(msg_send.languege, "quit") == 0)
{
msg_send.type = 'q';//将此次发送的信息性质定义为退出信息
sendto(sfd, &msg_send, sizeof(msg_send), 0, (struct sockaddr *)&sin, sizeof(sin));
// 获取当前系统中的所有子进程
pid_t child_pid = waitpid(-1, NULL, WNOHANG);
usleep(10000);
while (child_pid > 0)
{
// 发送SIGKILL信号给每个子进程
kill(child_pid, SIGKILL);
child_pid = waitpid(-1, NULL, WNOHANG);
}
break;
}
sendto(sfd, &msg_send, sizeof(msg_send), 0, (struct sockaddr *)&sin, sizeof(sin));
}
}
else if(pid==0){
while(1){
memset(msg_recv.languege,0,sizeof(msg_recv.languege));
recv(sfd,&msg_recv,sizeof(msg_recv),0);
switch(msg_recv.type){
case 'j':
{
printf("[%s]加入了群聊\n",msg_recv.name);
}
break;
case 'r':
{
printf("[%s]:%s\n",msg_recv.name,msg_recv.languege);
}
break;
case 'q':
{
printf("[%s]退出了群聊\n",msg_recv.name);
}
break;
default:
printf("error\n");
}
}
}
}
break;
case 2:
{
log_reg();
}
break;
case 0:
{
flag = 1;
}
break;
default:
printf("error\n");
}
if (flag == 1)
{
break;
}
}
return 0;
}
/*******************************************/
文件名:ad.c
/*******************************************/
#include <myhead.h>
struct Msg
{
char type;
char name[10];
char languege[128];
};
//绑定服务器
void ser_bind(struct ip_mreqn *imr, struct sockaddr_in *min)
{
imr->imr_multiaddr.s_addr = inet_addr("224.1.2.3"); //组播ip
imr->imr_address.s_addr = inet_addr("192.168.2.91"); //主机ip
imr->imr_ifindex = 2; //网卡编号
min->sin_family = AF_INET;
min->sin_port = htons(9999);
min->sin_addr.s_addr = inet_addr("224.1.2.3");
}
//端口号重新启用
void port_reuse(int sockfd)
{
//将端口号快速重用
int reuse = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1)
{
perror("setsockopt error");
return;
}
printf("端口号快速重用成功\n");
}
int main(int argc, char const *argv[])
{
int sfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sfd == -1)
{
perror("socket error");
return -1;
}
port_reuse(sfd);
struct sockaddr_in sin;
struct ip_mreqn imr;
ser_bind(&imr, &sin);
if (setsockopt(sfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr)) == -1)
{
perror("setsockopt error");
return -1;
}
if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))==-1){
perror("bind error");
return -1;
}
struct Msg msg_send;
struct Msg msg_recv;
pid_t pid = fork();
if (pid > 0)
{
while (1)
{
memset(msg_send.languege, 0, sizeof(msg_send.languege));
fgets(msg_send.languege, sizeof(msg_send.languege), stdin);
msg_send.languege[strlen(msg_send.languege) - 1] = 0;
msg_send.type = 'r';
strcpy(msg_send.name,"管理员");
if (strcmp(msg_send.languege, "quit") == 0)
{
msg_send.type = 'q';
sendto(sfd, &msg_send, sizeof(msg_send), 0, (struct sockaddr *)&sin, sizeof(sin));
// 获取当前系统中的所有子进程
pid_t child_pid = waitpid(-1, NULL, WNOHANG);
usleep(10000);
while (child_pid > 0)
{
// 发送SIGKILL信号给每个子进程
kill(child_pid, SIGKILL);
child_pid = waitpid(-1, NULL, WNOHANG);
}
break;
}
sendto(sfd, &msg_send, sizeof(msg_send), 0, (struct sockaddr *)&sin, sizeof(sin));
}
}
else if (pid == 0)
{
while (1)
{
memset(msg_recv.languege, 0, sizeof(msg_recv.languege));
recv(sfd, &msg_recv, sizeof(msg_recv), 0);
switch (msg_recv.type)
{
case 'j':
{
printf("[%s]加入了群聊\n", msg_recv.name);
fflush(stdout);
}
break;
case 'r':
{
printf("[%s]:%s\n", msg_recv.name, msg_recv.languege);
fflush(stdout);
}
break;
case 'q':
{
printf("[%s]退出了群聊\n", msg_recv.name);
fflush(stdout);
}
break;
default:
printf("error\n");
}
}
}
return 0;
}
结果:
2、tftp协议上传下载
代码:
/*******************************************/
文件名:load.h
/*******************************************/
#ifndef LOAD_H
#define LOAD_H
#include <myhead.h>
#define SER_PORT 69
#define SER_IP "192.168.3.54"
#define CLI_PORT 8888
#define CLI_IP "192.168.3.87"
typedef struct LOAD
{
char buf[516];
size_t size;
} LOAD;
//传输菜单
void load_menu();
//下载申请
void load_apply(struct LOAD *ld, char *text);
//绑定服务器
void ser_bind(struct sockaddr_in *sin);
//端口号重新启用
void port_reuse(int sockfd);
//安装
void load_in(int sockfd, int fd, struct LOAD *ld, struct sockaddr_in *sin);
//上传申请
void up_apply(struct LOAD *ld, char *text);
//上传
void load_out(int sockfd, int fd, struct LOAD *ld, struct sockaddr_in *sin);
#endif
/*******************************************/
文件名:load.c
/*******************************************/
#include "load.h"
//传输菜单
void load_menu()
{
printf("/***********************/\n");
printf(" /*******1、上传********/\n");
printf(" /*******2、下载********/\n");
printf(" /*******0、退出********/\n");
printf("/***********************/\n");
}
//下载申请
void load_apply(struct LOAD *ld, char *text)
{
short *p1 = (short *)ld->buf; //操作码
*p1 = htons(1);
char *p2 = ld->buf + 2; //文件名
strcpy(p2, text);
char *p4 = p2 + strlen(p2) + 1; //模式位
strcpy(p4, "octet");
ld->size = 2 + strlen(p2) + strlen(p4) + 2;
}
//绑定服务器
void ser_bind(struct sockaddr_in *sin)
{
sin->sin_family = AF_INET;
sin->sin_port = htons(SER_PORT);
sin->sin_addr.s_addr = inet_addr(SER_IP);
}
//端口号重新启用
void port_reuse(int sockfd)
{
//将端口号快速重用
int reuse = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1)
{
perror("setsockopt error");
return;
}
printf("端口号快速重用成功\n");
}
//安装
void load_in(int sockfd, int fd, struct LOAD *ld, struct sockaddr_in *sin)
{
if (sockfd < 0 || fd < 0 || ld == NULL || sin == NULL)
{
fprintf(stderr, "Invalid arguments.\n");
return;
}
int num = 0; // 用于跟踪数据块编号
socklen_t addrlen = sizeof(*sin);
while (1)
{
// 清空缓冲区
memset(ld->buf, 0, sizeof(ld->buf));
// 接收数据
ssize_t bytes = recvfrom(sockfd, ld->buf, sizeof(ld->buf), 0, (struct sockaddr *)sin, &addrlen);
if (bytes < 0)
{
perror("recvfrom error");
return;
}
else if (bytes == 0)
{
// 对方关闭连接
printf("Server closed the connection.\n");
break;
}
// 检查操作码是否表示错误
if (ld->buf[1] == 5)
{
printf("Error code received: %d\n", (int)ld->buf[1]);
// 这里可以添加错误处理逻辑
break;
}
// 检查操作码是否表示数据块
if (ld->buf[1] == 3)
{
unsigned short block_num = ntohs(*(unsigned short *)(ld->buf + 2));
if (block_num == num + 1 && bytes > 4)
{
// 写入文件,跳过操作码和块编号
if (write(fd, ld->buf + 4, bytes - 4) < 0)
{
perror("write error");
return;
}
num = block_num; // 更新块编号
// 发送 ACK
ld->buf[1] = 4;
if (sendto(sockfd, ld->buf, 4, 0, (struct sockaddr *)sin, addrlen) < 0)
{
perror("send ack error");
return;
}
}
else
{
// 接收到的数据块不是预期的块编号
printf("Received block number is not continuous or invalid size.\n");
break;
}
}
else
{
// 未知操作码
printf("Unknown operation code received.\n");
break;
}
// 检查是否接收完毕
if (bytes < sizeof(ld->buf))
{
printf("下载完成\n");
break;
}
}
}
//上传申请
void up_apply(struct LOAD *ld, char *text)
{
short *p1 = (short *)ld->buf; //操作码
*p1 = htons(2);
char *p2 = ld->buf + 2; //文件名
strcpy(p2, text);
char *p4 = p2 + strlen(p2) + 1; //模式位
strcpy(p4, "octet");
ld->size = 2 + strlen(p2) + strlen(p4) + 2;
}
//上传
void load_out(int sockfd, int fd, struct LOAD *ld, struct sockaddr_in *sin)
{
if (sockfd < 0 || fd < 0 || ld == NULL || sin == NULL)
{
fprintf(stderr, "Invalid arguments.\n");
return;
}
int num = 0; // 用于跟踪数据块编号
socklen_t addrlen = sizeof(*sin);
while (1)
{
ssize_t bytes = recvfrom(sockfd, ld->buf, sizeof(ld->buf), 0, (struct sockaddr *)sin, &addrlen);
if (bytes < 0)
{
perror("recvfrom error");
return ;
}
if (bytes == 4)
{
//长度是4是应答报文
if (ld->buf[1] == 4)
{
if (htons(num) == *(unsigned short *)(ld->buf + 2))
{
num++;
printf("ack码正确\n");
}
else
{
printf("ack玛错误\n");
}
}
else if (ld->buf[1] == 5)
{
printf("操作玛为5,错误\n");
return ;
}
}
// 清空缓冲区
bzero(ld->buf + 4, 512);
ssize_t rate = read(fd, ld->buf + 4, 512);
if (rate < 0)
{
perror("read error");
return;
}
else if (rate == 0)
{
printf("未读取到数据\n");
break;
}
ld->buf[1]=3;
*((unsigned short *)(ld->buf + 2)) = htons(num);
// 写入文件,跳过操作码和块编号
if (sendto(sockfd, ld->buf, sizeof(ld->buf), 0, (struct sockaddr *)sin, sizeof(*sin)) < 0)
{
perror("sendto error");
return;
}
// 检查是否接收完毕
if (rate < 512)
{
printf("上传完成\n");
break;
}
}
}
/*******************************************/
文件名:main.c
/*******************************************/
#include "load.h"
int main(int argc, char const *argv[])
{
int sfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sfd == -1)
{
perror("socket error");
return -1;
}
printf("sfd=%d\n", sfd);
port_reuse(sfd);
struct sockaddr_in sin;
ser_bind(&sin);
struct LOAD ld;
int key = -1;
int flag = 0;
while (1)
{
load_menu();
printf("指令:");
scanf("%d", &key);
getchar();
switch (key)
{
case 1:
{
while (1)
{
char text[128] = "";
printf("请输入你要上传的文件名:");
fgets(text, sizeof(text), stdin);
text[strlen(text) - 1] = 0;
if (strcmp(text, "quit") == 0)
{
printf("退出成功\n");
break;
}
up_apply(&ld, text);
sendto(sfd, ld.buf, ld.size, 0, (struct sockaddr *)&sin, sizeof(sin));
int fd = open(text, O_RDONLY);
if (fd == -1)
{
perror("open error");
return -1;
}
lseek(fd,0,SEEK_SET);
load_out(sfd, fd, &ld, &sin);
close(fd);
}
}
break;
case 2:
{
while (1)
{
char text[128] = "";
printf("请输入你要安装的文件名:");
fgets(text, sizeof(text), stdin);
text[strlen(text) - 1] = 0;
if (strcmp(text, "quit") == 0)
{
printf("退出成功\n");
break;
}
load_apply(&ld, text);
sendto(sfd, ld.buf, ld.size, 0, (struct sockaddr *)&sin, sizeof(sin));
int fd = open(text, O_WRONLY | O_CREAT | O_TRUNC, 0664);
if (fd == -1)
{
perror("open error");
return -1;
}
lseek(fd,0,SEEK_SET);
load_in(sfd, fd, &ld, &sin);
close(fd);
}
}
break;
case 0:
{
flag = 1;
}
break;
default:
printf("error\n");
}
//退出外层循环
if (flag == 1)
{
break;
}
}
close(sfd);
return 0;
}
结果:
当然是成功了