一 UDP通信框架
UDPSend.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(int arg, char *args[])
{
if (arg < 3)
return -1;
int st = socket(AF_INET, SOCK_DGRAM, 0);//建立socket的时候第二个参数值为SOCK_DGRAM
if (st == -1)
{
printf('socket failed %s\n', strerror(errno));
return 0;
}
int port = atoi(args[2]);
int on = 1;
if (setsockopt(st, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) == -1)//设置UDP socket可以发送广播消息
{
printf('setsockopt failed %s\n', strerror(errno));
return EXIT_FAILURE;
}
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(args[1]);
char buf[1024];
while (1)
{
memset(buf, 0, sizeof(buf));
read(STDIN_FILENO, buf, sizeof(buf));//读取用户键盘输入
if (sendto(st, buf, strlen(buf), 0, (struct sockaddr *) &addr,
sizeof(addr)) == -1)//udp使用sendto发送消息
{
printf('sendto failed %s\n', strerror(errno));
break;
}
}
close(st);
return EXIT_SUCCESS;
}
UDPRecv.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(int arg, char *args[])
{
if (arg < 2)
return -1;
int st = socket(AF_INET, SOCK_DGRAM, 0);
if (st == -1)
{
printf('socket failed %s\n', strerror(errno));
return 0;
}
int port = atoi(args[1]);
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(st, (struct sockaddr *)&addr, sizeof(addr)) == -1)//UDP接收数据,也需要绑定IP,不需要监听和connnect
{
printf('bind failed %s\n', strerror(errno));
return -1;
}
char buf[1024];
struct sockaddr_in client_addr;
socklen_t len = sizeof(client_addr);
while (1)
{
memset(&client_addr, 0, sizeof(client_addr));
memset(buf, 0, sizeof(buf));
if (recvfrom(st, buf, sizeof(buf), 0,
(struct sockaddr *)&client_addr, &len) == -1)
{
printf('recvfrom failed %s\n', strerror(errno));
break;
}
else
{
printf('%s recv is %s\n', inet_ntoa(client_addr.sin_addr), buf);
}
}
close(st);
return 0;
}
makefile文件
.SUFFIXES:.c .o
CC=gcc
SRCS1=udpsend.c
SRCS2=udprecv.c
OBJS1=$(SRCS1:.c=.o)
OBJS2=$(SRCS2:.c=.o)
EXEC1=udprecv
EXEC2=udpsend
start:$(OBJS2) $(OBJS1)
$(CC) -o $(EXEC2) $(OBJS2) -lpthread
$(CC) -o $(EXEC1) $(OBJS1) -lpthread
@echo '======OK!=========\n'
.c.o:
$(CC) -o $@ -c $<
clean:
rm -r $(OBJS2) $(EXEC2) $(EXEC1) $(OBJS1)
二 UDP文件传输系统client.c
#include <stdio.h>
#include <stdlib.h>
#include 'pub.h'
int main(int arg, char *args[])
{
if (arg < 4)//如果参数小于3个,main函数退出
{
printf('Input argumnet: source | IP | Port | resource\n');
return EXIT_FAILURE;
}
int iport = atoi(args[2]);//将第二个参数转化为端口号
if (iport == 0)//如果端口号为0,main函数退出
{
printf('port %d is invalid\n', iport);
return EXIT_FAILURE;
}
printf('%s send begin\n', args[3]);
if (send_work(args[1], iport, args[3]) == 1)
printf('%s send success\n', args[3]);
else
printf('%s send fail\n', args[3]);
return EXIT_SUCCESS;
}
server.c #include <stdio.h>
#include <stdlib.h>
#include 'pub.h'
int main(int arg, char *args[])
{
if (arg < 2)//如果没有参数,main函数退出
{
printf('Input like: source | port\n');
return EXIT_FAILURE;
}
int iport = atoi(args[1]);//将第一个参数转化为端口号,server端socket要在这个端口号上listen
if (iport == 0)
{
printf('port %d is invalid\n', iport);
return EXIT_FAILURE;
}
printf('recv is begin\n');
if (recv_work(iport) == 1)//server端socket在port指定的端口上listen,接收来自client发送的文件
printf('recv success\n');
else
printf('recv fail\n');
return EXIT_SUCCESS;
}
#ifndef PUB_H_
#define PUB_H_
int send_work(const char *hostname, int port, const char *filename);
int recv_work(int port);
#endif
#ifdef WIN
#include <WinSock2.h>
#else
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
#define SOCKET int
#endif
#include <stdio.h>
#include 'pub.h'
#define BUFSIZE 262144 //256k
void getfilename(const char *filename, char *name)//从完整路径名中解析出文件名称,例如:/home/test/abc.txt,解析完成后为abc.txt
{
int len = strlen(filename);
int i;
for (i = (len - 1); i >= 0; i--)
{
if ((filename[i] == '\\') || (filename[i] == '/'))
{
break;
}
}
strcpy(name, &filename[i 1]);
return;
}
SOCKET init_socket()//初始化socket
{
//如果是windows,执行如下代码
#ifdef WIN
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(1, 1);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0)
{
return -1;
}
if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
{
WSACleanup();
return -1;
}
#endif
return 0;
}
SOCKET create_recv_socket(int port)//在port指定的端口上建立接收数据的IDP SOCKET
{
//if (init_socket() == -1)
//return;
SOCKET st = socket(AF_INET, SOCK_DGRAM, 0);//建立socket的时候第二个参数值为SOCK_DGRAM
if (st == 0)
return 0;//失败,返回0
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(INADDR_ANY);
if (bind(st, (struct sockaddr *)&addr, sizeof(addr)) == -1)//UDP接收数据,也需要绑定IP,不需要监听和connnect
{
printf('bind failed %s\n', strerror(errno));
return 0;
}
return st;
}
SOCKET create_send_socket()//建立发送数据的UDP Socket
{
if (init_socket() == -1)
return;
SOCKET st = socket(AF_INET, SOCK_DGRAM, 0);//建立socket的时候第二个参数值为SOCK_DGRAM
if (st == 0)
return 0;//失败,返回0
return st;//成功
}
int send_work(const char *hostname, int port, const char *filename)//向hostname指定的IP地址发送filename指定的文件
{
SOCKET st_send = create_send_socket();//建立发送数据的UDP Socket
SOCKET st_recv = create_recv_socket(port 1);//建立接收数据的UDP Socket
if (st_send == 0)//建立失败,函数返回
return 0;
if (st_recv == 0)
return 0;
FILE *fd = fopen(filename, 'rb');//以只读方式打开filename指定的文件
if (fd == NULL)//如果文件打开失败,函数返回
{
printf('open %s failed %s\n', filename, strerror(errno));
return 0;
}
char *buf = malloc(BUFSIZE);//申请一个缓冲区,存放接收到的文件内容
memset(buf, 0, BUFSIZE);
getfilename(filename, buf);//从完整路径名中解析出文件名称,例如:/home/test/abc.txt,解析完成后为abc.txt
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(hostname);
size_t rc = sendto(st_send, buf, strlen(buf), 0, (struct sockaddr *) &addr,sizeof(addr));//客户端第一次给server端发送的数据为要传递的文件名称,将解析完成后的文件名通过socket发送给server端
if (rc <= 0)//发送失败
{
printf('send failed %s\n', strerror(errno));
}
else //发送成功
{
struct sockaddr_in client_addr;
#ifdef WIN
int len = 0;
#else
unsigned int len = 0;
#endif
len = sizeof(client_addr);
memset(&client_addr, 0, sizeof(client_addr));
memset(buf, 0, BUFSIZE);
if (recvfrom(st_recv, buf, sizeof(buf), 0, (struct sockaddr *)&client_addr, &len) <= 0)//接收来自server端的回复
{
printf('recv failed %s\n', strerror(errno));
}
else
{
if (strncmp(buf, 'OK', 2) == 0)//如果收到来自服务端的回复,代表服务端认可,可以发送文件了
{
while (1)
{
memset(buf, 0, BUFSIZE);
rc = fread(buf, 1, BUFSIZE, fd);//循环读取文件,直到读到文件尾,循环break
if (rc <= 0)
{
break;//如果到文件最后,循环终止
}
else
{
rc = sendto(st_send, buf, rc, 0, (struct sockaddr *) &addr, sizeof(addr));//将从文件中读到的数据,通过socket发送到server端,其中rc为从文件中读到的数据大小
if (rc <= 0)//如果发送失败,代表TCP连接出错,循环break
{
printf('send failed %s\n', strerror(errno));
}
}
}
}
memset(buf, 0, BUFSIZE);
rc = sendto(st_send, buf, 128, 0, (struct sockaddr *) &addr, sizeof(addr));//连续发送128个0字节,代表文件发送完毕
}
}
fclose(fd);
free(buf);
#ifdef WIN
closesocket(st_send);
closesocket(st_recv);
WSACleanup();
#else
close(st_send);
close(st_recv);
#endif
return 1;
}
int recv_work(int port)//server端socket在port指定的端口上listen,接收来自client发送的文件
{
SOCKET st_send = create_send_socket();//建立发送数据的UDP Socket
SOCKET st_recv = create_recv_socket(port 1);//建立接收数据的UDP Socket
if (st_send == 0)//建立失败,函数返回
return 0;
if (st_recv == 0)
return 0;
char *buf = malloc(BUFSIZE);//建立接收文件数据缓冲区
FILE *fd = NULL;
#ifdef WIN
int len = 0;
#else
unsigned int len = 0;
#endif
struct sockaddr_in client_addr;
len = sizeof(client_addr);
memset(&client_addr, 0, len);
memset(buf, 0, BUFSIZE);
size_t rc = recvfrom(st_recv, buf, sizeof(buf), 0, (struct sockaddr *)&client_addr, &len);//接收来自client的数据,客户端第一次要发送的文件名称
if (rc <= 0)
{
printf('recv failed %s\n', strerror(errno));
}
else
{
printf('receiving %s\n', buf);
fd = fopen(buf, 'wb');//以只写方式打开文件
if (fd == NULL)
{
printf('open %s failed %s\n', buf, strerror(errno));
}
else
{
client_addr.sin_port = htons(port 1);//客户端接收的端口号
memset(buf, 0, BUFSIZE);
strcpy(buf, 'OK');
rc = sendto(st_send, buf, strlen(buf), 0, (struct sockaddr *) &client_addr, sizeof(client_addr));//回复客户端,同意接收文件
if (rc <= 0)
{
printf('send failed %s\n', strerror(errno));
}
while (1)
{
memset(buf, 0, BUFSIZE);
rc = recvfrom(st_recv, buf, sizeof(buf), 0, (struct sockaddr *)&client_addr, &len);//循环接收来自client的数据,数据为发送文件的内容
char tmp[128];
memset(tmp, 0, 128);
if (memcmp(buf, tmp, sizeof(tmp)) == 0)//连续接收到128Z字节的0,结束
break;
if (rc <= 0)//如果client连接断开,代表文件传递完成,或者网络意外中断,循环break
{
printf('recv failed %s\n', strerror(errno));
break;
}
else
{
fwrite(buf, 1, rc, fd);//将从client端收到的内容写入文件
}
}
}
}
if (fd)
fclose(fd);//关闭打开的文件
free(buf);
#ifdef WIN
closesocket(st_send);
closesocket(st_recv);
WSACleanup();
#else
close(st_send);
close(st_recv);
#endif
return 1;
}
makefile .SUFFIXES:.c .o
CC=gcc
SRCS1=client.c pub.c
SRCS2=server.c pub.c
OBJS1=$(SRCS1:.c=.o)
OBJS2=$(SRCS2:.c=.o)
EXEC1=client
EXEC2=server
start:$(OBJS2) $(OBJS1)
$(CC) -o $(EXEC2) $(OBJS2) -lpthread
$(CC) -o $(EXEC1) $(OBJS1) -lpthread
@echo '======OK!=========\n'
.c.o:
$(CC) -o $@ -c $<
clean:
rm -r $(OBJS2) $(EXEC2) $(EXEC1) $(OBJS1)
|
|