一 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的時候第二個參數(shù)值為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)//設(shè)置UDP socket可以發(fā)送廣播消息
{
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發(fā)送消息
{
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接收數(shù)據(jù),,也需要綁定IP,不需要監(jiān)聽和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文件傳輸系統(tǒng)client.c
#include <stdio.h>
#include <stdlib.h>
#include 'pub.h'
int main(int arg, char *args[])
{
if (arg < 4)//如果參數(shù)小于3個,,main函數(shù)退出
{
printf('Input argumnet: source | IP | Port | resource\n');
return EXIT_FAILURE;
}
int iport = atoi(args[2]);//將第二個參數(shù)轉(zhuǎn)化為端口號
if (iport == 0)//如果端口號為0,main函數(shù)退出
{
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)//如果沒有參數(shù),,main函數(shù)退出
{
printf('Input like: source | port\n');
return EXIT_FAILURE;
}
int iport = atoi(args[1]);//將第一個參數(shù)轉(zhuǎn)化為端口號,,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發(fā)送的文件
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,,執(zhí)行如下代碼
#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指定的端口上建立接收數(shù)據(jù)的IDP SOCKET
{
//if (init_socket() == -1)
//return;
SOCKET st = socket(AF_INET, SOCK_DGRAM, 0);//建立socket的時候第二個參數(shù)值為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接收數(shù)據(jù),,也需要綁定IP,不需要監(jiān)聽和connnect
{
printf('bind failed %s\n', strerror(errno));
return 0;
}
return st;
}
SOCKET create_send_socket()//建立發(fā)送數(shù)據(jù)的UDP Socket
{
if (init_socket() == -1)
return;
SOCKET st = socket(AF_INET, SOCK_DGRAM, 0);//建立socket的時候第二個參數(shù)值為SOCK_DGRAM
if (st == 0)
return 0;//失敗,,返回0
return st;//成功
}
int send_work(const char *hostname, int port, const char *filename)//向hostname指定的IP地址發(fā)送filename指定的文件
{
SOCKET st_send = create_send_socket();//建立發(fā)送數(shù)據(jù)的UDP Socket
SOCKET st_recv = create_recv_socket(port 1);//建立接收數(shù)據(jù)的UDP Socket
if (st_send == 0)//建立失敗,函數(shù)返回
return 0;
if (st_recv == 0)
return 0;
FILE *fd = fopen(filename, 'rb');//以只讀方式打開filename指定的文件
if (fd == NULL)//如果文件打開失敗,,函數(shù)返回
{
printf('open %s failed %s\n', filename, strerror(errno));
return 0;
}
char *buf = malloc(BUFSIZE);//申請一個緩沖區(qū),,存放接收到的文件內(nèi)容
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端發(fā)送的數(shù)據(jù)為要傳遞的文件名稱,將解析完成后的文件名通過socket發(fā)送給server端
if (rc <= 0)//發(fā)送失敗
{
printf('send failed %s\n', strerror(errno));
}
else //發(fā)送成功
{
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端的回復(fù)
{
printf('recv failed %s\n', strerror(errno));
}
else
{
if (strncmp(buf, 'OK', 2) == 0)//如果收到來自服務(wù)端的回復(fù),,代表服務(wù)端認(rèn)可,,可以發(fā)送文件了
{
while (1)
{
memset(buf, 0, BUFSIZE);
rc = fread(buf, 1, BUFSIZE, fd);//循環(huán)讀取文件,直到讀到文件尾,,循環(huán)break
if (rc <= 0)
{
break;//如果到文件最后,,循環(huán)終止
}
else
{
rc = sendto(st_send, buf, rc, 0, (struct sockaddr *) &addr, sizeof(addr));//將從文件中讀到的數(shù)據(jù),通過socket發(fā)送到server端,,其中rc為從文件中讀到的數(shù)據(jù)大小
if (rc <= 0)//如果發(fā)送失敗,,代表TCP連接出錯,循環(huán)break
{
printf('send failed %s\n', strerror(errno));
}
}
}
}
memset(buf, 0, BUFSIZE);
rc = sendto(st_send, buf, 128, 0, (struct sockaddr *) &addr, sizeof(addr));//連續(xù)發(fā)送128個0字節(jié),,代表文件發(fā)送完畢
}
}
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發(fā)送的文件
{
SOCKET st_send = create_send_socket();//建立發(fā)送數(shù)據(jù)的UDP Socket
SOCKET st_recv = create_recv_socket(port 1);//建立接收數(shù)據(jù)的UDP Socket
if (st_send == 0)//建立失敗,,函數(shù)返回
return 0;
if (st_recv == 0)
return 0;
char *buf = malloc(BUFSIZE);//建立接收文件數(shù)據(jù)緩沖區(qū)
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的數(shù)據(jù),,客戶端第一次要發(fā)送的文件名稱
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));//回復(fù)客戶端,同意接收文件
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);//循環(huán)接收來自client的數(shù)據(jù),,數(shù)據(jù)為發(fā)送文件的內(nèi)容
char tmp[128];
memset(tmp, 0, 128);
if (memcmp(buf, tmp, sizeof(tmp)) == 0)//連續(xù)接收到128Z字節(jié)的0,,結(jié)束
break;
if (rc <= 0)//如果client連接斷開,代表文件傳遞完成,,或者網(wǎng)絡(luò)意外中斷,循環(huán)break
{
printf('recv failed %s\n', strerror(errno));
break;
}
else
{
fwrite(buf, 1, rc, fd);//將從client端收到的內(nèi)容寫入文件
}
}
}
}
if (fd)
fclose(fd);//關(guān)閉打開的文件
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)
|
|