1. 广播,组播代码重新写一遍
2. 多进程通过模型自己写一遍
3. 多线程代码写一遍,能否将newfd定义成全局(不行,找原因)
4. 上传下载写完。
1. 广播、组播
1.1 广播
接收方:boardrRcv.c
#include <myhead.h>
/*--------------------广播的接收方 (类似于UDP 服务器端)---------------------*/
#define ERR_MSG(msg) do{\
fprintf(stderr,"_%d_",__LINE__);\
perror(msg);\
return -1;\
}while(0)
#define IP "192.168.0.255" // ifconfig 广播IP地址
#define PORT 8888
int main(int argc, const char *argv[])
{
//创建报式套接字
int sfd = socket(AF_INET,SOCK_DGRAM,0);
if(sfd < 0)
{
ERR_MSG("socket error");
}
printf("sfd = %d\n",sfd);
//2. 填充服务器的地址信息结构体 AF_INET: man 7 ip
struct sockaddr_in sin;
sin.sin_family = AF_INET; //IPv4
sin.sin_port = htons(PORT); //端口号
sin.sin_addr.s_addr = inet_addr(IP); //广播IP
//3. 将套接字和网络信息结构体绑定
if(-1 == bind(sfd,(struct sockaddr*)&sin,sizeof(sin)))
{
ERR_MSG("bind error");
}
printf("bind success\n");
//4.接收,发送收据
struct sockaddr_in cin; //用来存储客户端数据包
socklen_t addrlen = sizeof(cin);
char buf[128]="";
while(1)
{
//接收收据
memset(buf,0,sizeof(buf));
int res = recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr *)&cin,&addrlen); //注意没有新的文件描述符产生
if(-1 == res)
{
ERR_MSG("recv error");
}
printf("接收到来自[%s : %d] :%s\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),buf);
/* //发送数据
strcat(buf,"***");
if(-1 == sendto(sfd,buf,sizeof(buf),0,(struct sockaddr *)&cin,sizeof(cin)))
{
ERR_MSG("sendto error");
}
printf("发送信息到[%s : %d] :%s\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),buf); */
}
//5.关闭套接字
close(sfd);
return 0;
}
发送方:boardSend.c
#include <myhead.h>
/*--------------------广播的发送方 (类似于UDP 客户端)---------------------*/
#define ERR_MSG(msg) do{\
fprintf(stderr,"_%d_",__LINE__);\
perror(msg);\
return -1;\
}while(0)
#define IP "192.168.0.255" //广播 IP地址
#define PORT 8888 //服务器端 端口号
int main(int argc, const char *argv[])
{
//创建报式套接字
int sfd = socket(AF_INET,SOCK_DGRAM,0); //UDP协议: SOCK_DGRAM
if(sfd < 0)
{
ERR_MSG("socket error");
}
printf("sfd = %d\n",sfd);
//设置允许广播 --> man 7 socket
int board = 1;
if(-1 == setsockopt(sfd,SOL_SOCKET,SO_BROADCAST,&board,sizeof(board)))
{
perror("setsockopt error");
return -1;
}
printf("设置允许广播成功\n");
//绑定客户端的地址信息--->非必须绑定
//若不绑定则会自动绑定本机IP以及随机端口
//2. 填充要发送的服务器的地址信息结构体,给下面的sendto函数使用
struct sockaddr_in sin;
sin.sin_family = AF_INET; //必须填IPv4
sin.sin_port = htons(PORT); //服务器绑定的端口号(网络字节序)
sin.sin_addr.s_addr = inet_addr(IP); //服务器绑定的IP地址(网络字节序)
char buf[128]="";
while(1)
{
//发送数据
memset(buf,0,sizeof(buf)); //缓存清0
printf("请输入>>>"); //通过终端输入
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1] = '\0';
if(-1 == sendto(sfd,buf,sizeof(buf),0,(struct sockaddr *)&sin,sizeof(sin)))
{
ERR_MSG("sendto error");
}
printf("发送成功:%s\n",buf);
}
//4.关闭套接字
close(sfd);
return 0;
}
1.2 组播
接收方:memberRcv.c
#include <myhead.h>
/*--------------------组播的接收方 (类似于UDP 服务器端)---------------------*/
//加入多播组 setsockopt
//填充地址信息结构体,增加组播IP
#define ERR_MSG(msg) do{\
fprintf(stderr,"_%d_",__LINE__);\
perror(msg);\
return -1;\
}while(0)
#define GRP_IP "224.8.8.8" //组播IP
#define IP "192.168.0.107" //本机IP地址,ifconfig
#define PORT 8888
int main(int argc, const char *argv[])
{
//创建报式套接字
int sfd = socket(AF_INET,SOCK_DGRAM,0);
if(sfd < 0)
{
ERR_MSG("socket error");
}
printf("sfd = %d\n",sfd);
/*=====================加入多播组==================*/
//加入多播组
struct ip_mreqn mq; //组播结构体 man 7 IPPROTO_IP
mq.imr_multiaddr.s_addr = inet_addr(GRP_IP); //组播IP地址
mq.imr_address.s_addr = inet_addr(IP); //本机IP地址
mq.imr_ifindex = 0; //网络设备索引号,0表示自动, 网卡ens33的设备索引号,也可填2
if(-1 == setsockopt(sfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mq,sizeof(mq)))
{
ERR_MSG("setsockopt error");
}
printf("加入 %s 组播的 %d 端口成功\n",GRP_IP,PORT);
//2. 填充服务器的地址信息结构体 AF_INET: man 7 ip
struct sockaddr_in sin;
sin.sin_family = AF_INET; //IPv4
sin.sin_port = htons(PORT); //端口号
sin.sin_addr.s_addr = inet_addr(GRP_IP); //组播IP
//3. 将套接字和网络信息结构体绑定
if(-1 == bind(sfd,(struct sockaddr*)&sin,sizeof(sin)))
{
ERR_MSG("bind error");
}
printf("bind success\n");
//4.接收,发送收据
struct sockaddr_in cin; //用来存储客户端数据包
socklen_t addrlen = sizeof(cin);
char buf[128]="";
while(1)
{
//接收收据
memset(buf,0,sizeof(buf));
int res = recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr *)&cin,&addrlen); //注意没有新的文件描述符产生
if(-1 == res)
{
ERR_MSG("recv error");
}
printf("接收到来自[%s : %d] :%s\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),buf);
}
//5.关闭套接字
close(sfd);
return 0;
}
发送方:memberSend.c
#include <myhead.h>
/*--------------------组播的发送方 (类似于UDP 客户端)---------------------*/
//更改组播地址即可
#define ERR_MSG(msg) do{\
fprintf(stderr,"_%d_",__LINE__);\
perror(msg);\
return -1;\
}while(0)
#define IP "224.8.8.8" //组播IP 224.0.0.0 - 239.255.255.255
#define PORT 8888 //端口号
int main(int argc, const char *argv[])
{
//创建报式套接字
int sfd = socket(AF_INET,SOCK_DGRAM,0); //UDP协议: SOCK_DGRAM
if(sfd < 0)
{
ERR_MSG("socket error");
}
printf("sfd = %d\n",sfd);
//绑定客户端的地址信息--->非必须绑定
//若不绑定则会自动绑定本机IP以及随机端口
//2. 填充要发送的服务器的地址信息结构体,给下面的sendto函数使用
struct sockaddr_in sin;
sin.sin_family = AF_INET; //必须填IPv4
sin.sin_port = htons(PORT); //绑定的端口号(网络字节序)
sin.sin_addr.s_addr = inet_addr(IP); //组播IP(网络字节序) 224.0.0.0 - 239.255.255.255
char buf[128]="";
while(1)
{
//发送数据
memset(buf,0,sizeof(buf)); //缓存清0
printf("请输入>>>"); //通过终端输入
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1] = '\0';
if(-1 == sendto(sfd,buf,sizeof(buf),0,(struct sockaddr *)&sin,sizeof(sin)))
{
ERR_MSG("sendto error");
}
printf("发送成功:%s\n",buf);
}
//4.关闭套接字
close(sfd);
return 0;
}
2.多进程实现并发服务器
#include <myhead.h>
/*=======================TCP并发服务器 多进程实现========================*/
/*可以处理来自多个客户端的任务
//sfd = socket();
//bind();
//while(1){
newfd = accept();
if(fork() == 0){ //创建子进程
close(sfd);
while(1){
recv();
send();
}
}
}
*/
#define ERR_MSG(msg) do{\
fprintf(stderr," %d ",__LINE__);\
perror(msg);\
return -1; \
}while(0)
#define IP "192.168.0.109" //ifconfig查看本机IP
#define PORT 6768
int task_cli(int newfd,struct sockaddr_in cin); //处理客户端任务
void handler(int sig)
{
while(waitpid(-1,NULL,WNOHANG) > 0); //非阻塞方式回收资源
}
int main(int argc, const char *argv[])
{
//捕获17号信号SIGCHID
//子进程退出时会给父进程发一个信号SIGCHID,
//父进程收到信号后,再去给子进程回收资源,handler函数
if(signal(SIGCHLD,handler) == SIG_ERR)
{
ERR_MSG("signal");
}
//1.创建流式套接字
int sfd = socket(AF_INET,SOCK_STREAM,0);
if(sfd < 0)
{
ERR_MSG("socket error");
}
//允许端口快速被重用
int reuse = 1;
if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse)) < 0)
{
ERR_MSG("setsockopt error");
}
printf("允许端口快速被重用成功\n");
//2.填充服务器的地址信息结构体
struct sockaddr_in sin;
sin.sin_family = AF_INET; //必须填AF_INET
sin.sin_port = htons(PORT); //端口号的网络字节序
sin.sin_addr.s_addr = inet_addr(IP); //本机IP的网络字节序
//3.绑定服务器的IP和端口--绑定
if(-1 == bind(sfd,(struct sockaddr*)&sin,sizeof(sin)))
{
ERR_MSG("bind error");
}
printf("bind success\n");
//4.将套接字设置为被动监听状态,监听是否有客户端连接
if(listen(sfd,128) <0 )
{
ERR_MSG("listen error");
}
printf("listen success\n");
//5.有客户端连接后,创建一个新的通信套接字与客户端通信
struct sockaddr_in cin;
socklen_t addrlen =sizeof(cin);
pid_t pid = 0;
int newfd = -1;
while(1)
{
printf("正在等待客户端连接...\n");
//阻塞函数,阻塞等待客户端连接
//产生新的文件描述符,才是与客户端通信的文件描述符
int newfd = accept(sfd,(struct sockaddr*)&cin,&addrlen);
if(newfd < 0)
{
ERR_MSG("accept error");
}
printf("[%s :%d] newfd = %d 客户端连接成功\n",\
inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd); //网络字节序转换成点分字节序
pid = fork(); //创建进程
if(pid == 0 )
{
//子进程处理与客户端交互:读写
task_cli(newfd,cin);
close(newfd);
exit(0); //子进程只负责与客户端交互,当客户端退出后,子进程也许退出
}
close(newfd);
}
//8.关闭文件描述符
close(sfd);
return 0;
}
int task_cli(int newfd,struct sockaddr_in cin)
{
char buf[128] = "";
ssize_t res = 0;
while(1)
{
//1.接收数据
memset(buf,0,sizeof(buf));
res = recv(newfd,buf,sizeof(buf),0);
if(res == -1)
{
ERR_MSG("recv error");
}
if(res == 0) //客户端下线
{
printf("[%s :%d] newfd = %d 客户端已断开连接.....\n",\
inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd); //网络字节序转换成点分字节序
break;
}
printf("newfd = %d: %s\n",newfd,buf);
//2.发送数据
strcat(buf,"*_*"); //可以修改为从终端获取
if(-1 == send(newfd,buf,sizeof(buf),0))
{
ERR_MSG("send error");
}
printf("发送成功\n");
}
}
3. 多线程实现并发服务器,能否将newfd定义成全局(不行,找原因)
#include <myhead.h>
/*=======================TCP并发服务器 多线程实现========================*/
/*可以处理来自多个客户端的任务
sfd = socket();
bind();
while(1)
{
newfd = accept();
pthread(&tid,NULL,task(),(void*)&msg) //创建子线程
pthread_detach(tid);
}
close(sfd);
void* task(void *arg)
{
while(1){
recv();
send();
}
close(newfd);
}
*/
#define ERR_MSG(msg) do{\
fprintf(stderr," %d ",__LINE__);\
perror(msg);\
return -1; \
}while(0)
#define IP "192.168.0.109" //ifconfig查看本机IP
#define PORT 6768
struct info
{
int newfd;
struct sockaddr_in cin;
};
void* task(void* arg); //处理客户端任务
int main(int argc, const char *argv[])
{
//1.创建流式套接字
int sfd = socket(AF_INET,SOCK_STREAM,0);
if(sfd < 0)
{
ERR_MSG("socket error");
}
//允许端口快速被重用
int reuse = 1;
if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse)) < 0)
{
ERR_MSG("setsockopt error");
}
printf("允许端口快速被重用成功\n");
//2.填充服务器的地址信息结构体
struct sockaddr_in sin;
sin.sin_family = AF_INET; //必须填AF_INET
sin.sin_port = htons(PORT); //端口号的网络字节序
sin.sin_addr.s_addr = inet_addr(IP); //本机IP的网络字节序
//3.绑定服务器的IP和端口--绑定
if(-1 == bind(sfd,(struct sockaddr*)&sin,sizeof(sin)))
{
ERR_MSG("bind error");
}
printf("bind success\n");
//4.将套接字设置为被动监听状态,监听是否有客户端连接
if(listen(sfd,128) <0 )
{
ERR_MSG("listen error");
}
printf("listen success\n");
//5.有客户端连接后,创建一个新的通信套接字与客户端通信
struct sockaddr_in cin;
socklen_t addrlen =sizeof(cin);
int newfd = -1;
pthread_t tid;
struct info msg;
while(1)
{
//主线程负责连接 accept
printf("正在等待客户端连接...\n");
//阻塞函数,阻塞等待客户端连接
//产生新的文件描述符,才是与客户端通信的文件描述符
int newfd = accept(sfd,(struct sockaddr*)&cin,&addrlen);
if(newfd < 0)
{
ERR_MSG("accept error");
}
printf("[%s :%d] newfd = %d 客户端连接成功\n",\
inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd); //网络字节序转换成点分字节序
//创建子线程
msg.newfd = newfd;
msg.cin = cin;
if(pthread_create(&tid,NULL,task,(void*)&msg) != 0)
{
fprintf(stderr,"子线程创建失败 __%d__\n",__LINE__);
return -1;
}
pthread_detach(tid); //设置成分离状态,回收子线程,自动回收
}
//8.关闭文件描述符
close(sfd);
return 0;
}
void* task(void* arg)
{
int newfd = ((struct info*)arg) ->newfd;
struct sockaddr_in cin = ((struct info*)arg) ->cin;
char buf[128] = "";
ssize_t res = 0;
while(1)
{
//1.接收数据
memset(buf,0,sizeof(buf));
res = recv(newfd,buf,sizeof(buf),0);
if(res == -1)
{
perror("recv error");
break;
}
if(res == 0) //客户端下线
{
printf("[%s :%d] newfd = %d 客户端已断开连接.....\n",\
inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd); //网络字节序转换成点分字节序
break;
}
printf("newfd = %d: %s\n",newfd,buf);
//2.发送数据
strcat(buf,"*_*"); //可以修改为从终端获取
if(-1 == send(newfd,buf,sizeof(buf),0))
{
perror("send error");
}
printf("发送成功\n");
}
close(newfd);
pthread_exit(NULL);
}
编译结果如下:
ubuntu@ubuntu:05tcp_pthread$ gcc pthread_tcpSer.c -o ser.out -pthread
ubuntu@ubuntu:05tcp_pthread$ ./ser.out
允许端口快速被重用成功
bind success
listen success
正在等待客户端连接...
[192.168.0.109 :47152] newfd = 4 客户端连接成功
正在等待客户端连接...
newfd = 4: hello
发送成功
newfd = 4: summer
发送成功
4.TFTP上传下载
#include <myhead.h>
/*===============================基于UDP 的tftp客户端===========================*/
#define ERR_MSG(msg) do{\
fprintf(stderr,"_%d_",__LINE__);\
perror(msg);\
return -1;\
}while(0)
#define IP "192.168.0.104" //查看服务器的IP地址 windows下ipconfig
#define PORT 69 //tftp专用
int download(int sfd,struct sockaddr_in sin);
int upload(int sfd,struct sockaddr_in sin);
int main(int argc, const char *argv[])
{
//创建报式套接字
int sfd = socket(AF_INET,SOCK_DGRAM,0);
if(sfd < 0)
{
ERR_MSG("socket error");
}
//绑定客户端的地址信息-->非必须绑定
//若不绑定则会自动绑定本机IP以及随机端口
//2. 填充服务器的地址信息结构体,给下面的sendto函数使用
struct sockaddr_in sin;
sin.sin_family = AF_INET; //
sin.sin_port = htons(PORT); //端口号
sin.sin_addr.s_addr = inet_addr(IP); //IP地址
char c = 0;
while(1)
{
printf("\n----------------------------------\n");
printf(" 1.上传 \n");
printf(" 2.下载 \n");
printf(" 3.退出 \n");
printf("********************************* \n");
printf("请选择>>> ");
c = getchar();
while(getchar() != 10);
switch(c)
{
case '1': //上传
upload(sfd,sin);
break;
case '2': //下载
download(sfd,sin);
break;
case '3': //退出
goto END;
default:
printf("输入有误,请重新输入\n");
}
}
END:
//5.关闭套接字
close(sfd);
return 0;
}
int download(int sfd,struct sockaddr_in sin)
{
char filename[30] = "";
printf("请输入要下载的文件名>>>");
scanf("%s",filename);
while(getchar() != 10);
//组下载请求协议包
char buf[516] = "";
unsigned short *p1 = (unsigned short*)buf;
*p1 = htons(1); //p1操作码
char *p2 = buf+2;
strcpy(p2,filename);
char *p3 =p2+strlen(filename);
*p3 = 0;
char *p4 = p3+1;
strcpy(p4,"octet");
int size = 4+strlen(p2)+strlen(p4);
//向服务器发送下载请求
if(-1 == sendto(sfd,buf,size,0,(struct sockaddr*)&sin,sizeof(sin)))
{
ERR_MSG("sendto error");
}
printf("发送下载请求成功\n");
unsigned short code=0; //操作码
unsigned short block=0; //块编号
char data[512]=""; //数据
int count=0;
int len=0;
int fd;
unsigned short *p = p1+1; //指向块编号
socklen_t addrlen = sizeof(sin);
while(1)
{
//接收数据包
memset(buf,0,sizeof(buf));
if(-1 == (len=recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,&addrlen))) //需接收新的端口号(临时端口号12345)
{
ERR_MSG("recv error");
}
//解析接收到的数据包
code = ntohs(*p1); //解析操作码
block = ntohs(*p); //解析块编号
strcpy(data,buf+4); //解析数据
if(code == 3 && block==count+1) //下载数据校验
{
count++;
if(count==1)
{
fd = open(filename,O_WRONLY | O_CREAT | O_TRUNC,0666);
if(-1 == fd)
{
ERR_MSG("file open error");
}
}
if(-1 == write(fd,data,len-4))
{
ERR_MSG("write error");
}
//回复ACK
*p1 = htons(4); //ACK操作码
*p = htons(block); //ACK块编号
if(-1 == sendto(sfd,buf,4,0,(struct sockaddr*)&sin,addrlen))
{
ERR_MSG("sendto error");
}
//判断数据是否小于512,若<512,则传输结束
if(len-4 < 512)
{
close(fd);
printf("已完成下载\n");
break;
}
}
else if(code == 5) //error
{
printf("error... %d: %s\n",block,data);
break;
}
}
}
int upload(int sfd,struct sockaddr_in sin)
{
char filename[30] = "";
printf("请输入要上传的文件名>>>");
scanf("%s",filename);
while(getchar() != 10);
int fd = open(filename,O_RDONLY);
if(fd<0)
{
ERR_MSG("file open error");
}
char buf[516] = "";
unsigned short *p1 = (unsigned short*)buf; // 写操作
unsigned short *p2 = p1+1; //指向块编号
//向服务器发送上传请求 //写操作
int size = sprintf(buf,"%c%c%s%c%s%c",0,2,filename,0,"octet",0); //组写请求协议包
if(-1 == sendto(sfd,buf,size,0,(struct sockaddr*)&sin,sizeof(sin)))
{
ERR_MSG("sendto error");
}
printf("发送上传请求成功\n");
unsigned short code=0; //操作码
unsigned short block=0; //块编号
char data[512]=""; //数据
int count=0;
int len=0;
socklen_t addrlen = sizeof(sin);
while(1)
{
//接收数据包
memset(buf,0,sizeof(buf));
if(-1 == (len=recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,&addrlen)))
{
ERR_MSG("recv error");
}
//解析接收到的数据包
//code = ntohs(*p1); //解析操作码
//block = ntohs(*p2); //解析块编号
code = ntohs(*(unsigned short*)buf);
block = ntohs(*(unsigned short*)(buf+2));
if(code == 4)
{
if(block == count)
{
count++;
*p1 = htons(3);
*p2 = htons(block+1);
//*(unsigned short*)(buf+2) = htons(block+1);
//发送数据
int res = read(fd, data, sizeof(data));
if(res < 0)
{
ERR_MSG("read error");
}
else if(0 == res)
{
printf("文件上传成功\n");
break;
}
//发送数据包
if(sendto(sfd, buf, res+4, 0, (struct sockaddr*)&sin, sizeof(sin)) <0)
{
ERR_MSG("sendto error");
}
// printf("code=%d : block=%d, data= %s\n", code,block,data);
}
else
{
printf(" 文上传失败 \n");
break;
}
}
else if(code == 5)
{
printf("error: %d,%s\n", block,data);
break;
}
}
return 0;
}
1860

被折叠的 条评论
为什么被折叠?



