Liunx--scoket程式設計學習--客戶端傳檔案給伺服器
阿新 • • 發佈:2018-12-11
一、操作步驟
0.編寫程式碼:程式碼在後面。client.c,server.c,server_multiple.c。用前面兩個程式就好,第三個是多執行緒同時服務多個客戶端的。
1.編譯
(1)編譯客戶端程式
gcc client.c -o client
(2)編譯伺服器端程式
選①或②其中一個。
①伺服器同時只服務一個客戶端。
gcc server.c -o server
②伺服器同時服務多個客戶端,多執行緒。
gcc server_multiple.c -o server_multiple -lpthread
2.執行
(1)執行命令檢視伺服器端自身的ip,並把它記住,例如是:192.168.1.222
ifconfig
(2)執行伺服器端程式
選①或②其中一個。
①伺服器同時只服務一個客戶端。
./server
②伺服器同時服務多個客戶端,多執行緒。
./server_multiple
(3)執行客戶端程式
./client 192.168.1.222
二、程式碼
/*client.c*/
/******client.c*****/ #include <sys/types.h> #include <sys/socket.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <unistd.h> #include <netinet/in.h> #include <netdb.h> #define SERVER_PORT 54321 //#define CLIENT_PORT 6543 #define SEND_BUFFER_SIZE 1024 //#define RECV_BUFFER_SIZE 2048 /******主函式********************/ int main(int argc, char *argv[]) { int sockfd; struct hostent *host; struct sockaddr_in server_sockaddr; //struct sockaddr_in client_sockaddr; if(argc != 2 ) { fprintf(stderr,"Usage: %s Hostname(or ip address) \n",argv[0]); return -1; } /*地址解析函式*/ if ((host = gethostbyname(argv[1])) == NULL) { perror("Server IP Address Error. \n"); return -1; } /*建立socket*/ if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("Create Socket Failed! \n");//建立Socket失敗 return -1; } /*設定sockaddr_in 伺服器端結構體中相關引數*/ server_sockaddr.sin_family = AF_INET; server_sockaddr.sin_port = htons(SERVER_PORT); server_sockaddr.sin_addr = *((struct in_addr *)host->h_addr); bzero(&(server_sockaddr.sin_zero), 8); /*呼叫connect函式主動發起對伺服器端的連線*/ if(connect(sockfd,(struct sockaddr *)&server_sockaddr,sizeof(struct sockaddr))== -1) { perror("Connect To Server IP Failed! \n"); //連線伺服器失敗 close(sockfd); //關閉Socket return -1; } ///////////////////////////////////////////////////// char file_name[SEND_BUFFER_SIZE]={}; FILE *fp; printf("Connected! \n"); printf("please input file name:\n"); do { bzero(file_name,sizeof(file_name)); //清零 scanf("%s",file_name); fp=fopen(file_name,"r"); //開啟檔案準備讀取檔案 if(NULL==fp) { printf("Not Found %s \n",file_name); //沒有找到檔案 printf("Please input file name again:\n"); } }while(NULL==fp); if(send(sockfd,file_name,strlen(file_name),0)<0) //將檔名傳送給伺服器 { printf("Send file name \" %s \" failed. \n",file_name); fclose(fp); //關閉檔案 close(sockfd); //關閉套介面 return -1; } usleep(10000);//延時10ms,一定要延時,不然檔名和資料會連在一起傳送 char buffer[SEND_BUFFER_SIZE]={}; int length=0; //每讀取一段資料,便將其傳送給伺服器,迴圈直到檔案讀完為止 while((length=fread(buffer,sizeof(char),sizeof(buffer),fp))>0) //讀取資料並快取到buffer中 { if(send(sockfd,buffer,length,0)<0) //將buffer的資料傳送到套介面 { printf("Send File %s Failed. \n",file_name);//傳送檔案失敗 fclose(fp); //關閉檔案 close(sockfd); //關閉套介面 return -1; } bzero(buffer,sizeof(buffer)); //將buffer清零 } fclose(fp);//關閉檔案 printf("File:%s Transfer Successful! \n",file_name); close(sockfd); return 0; }
/*server.c*/
/*server.c*/ #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <unistd.h> #define PORT 54321 #define BUFFER_SIZE 2048 #define MAX_BACKLOG 5 //連線請求佇列最大長度 /******主函式********************/ int main( ) { struct sockaddr_in server_sockaddr,client_sockaddr; int sockfd,client_fd; /*建立socket連線*/ if ((sockfd = socket(AF_INET,SOCK_STREAM,0))== -1) { perror("Create Socket Failed! \n");//建立套接字失敗 exit(1); } /*設定sockaddr_in 結構體中相關引數*/ server_sockaddr.sin_family = AF_INET; server_sockaddr.sin_port = htons(PORT); server_sockaddr.sin_addr.s_addr = INADDR_ANY; socklen_t len = sizeof(server_sockaddr); bzero(&(server_sockaddr.sin_zero), 8); int opt = 1;/* 允許重複使用本地地址與套接字進行繫結 */ setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); /*繫結函式bind()*/ //將套接字繫結一個IP地址和埠號 if (bind(sockfd, (struct sockaddr *)&server_sockaddr,sizeof(struct sockaddr)) == -1) { perror("Server Bind Failed! 埠被佔用 \n"); //套介面與本地ip地址和埠繫結失敗 close(sockfd); exit(1); } /*呼叫listen()函式,建立未處理請求的佇列*/ if (listen(sockfd, MAX_BACKLOG) == -1) { perror("Server Listen Failed! \n"); //套接字設定為監聽狀態失敗 close(sockfd); exit(1); } printf("Listening....\n"); /*呼叫accept()函式,阻塞等待客戶端的連線請求*/ if ((client_fd = accept(sockfd,(struct sockaddr *)&client_sockaddr, &len)) == -1) { perror("Server Accept Failed! \n"); //接受連線失敗 close(sockfd); exit(1); } printf("Connected \n"); char file_name[BUFFER_SIZE]={}; if(recv(client_fd,file_name,sizeof(file_name),0)<=0) //接收檔名 { printf("Server Recieve File Name Failed! \n"); //接收資料失敗 close(client_fd); //關閉與客戶端的連線 close(sockfd);//關閉監聽用的socket return -1; } char *temp_1; while((temp_1=strstr(file_name,"/")) != NULL) //將檔名中的目錄符號刪除,只留下檔名字。 { //例如原本file_name[]="/temp/test.txt",經過此段程式碼後,會變成file_name[]="test.txt" temp_1++; strcpy(file_name,temp_1); } printf("Receive file name : %s \n",file_name); FILE *fp=fopen(file_name,"w"); //建立檔案寫入 if(NULL==fp) //新建或開啟檔案進行寫操作失敗 { printf("File %s Can Not Open To Write! \n",file_name); close(client_fd); //關閉與客戶端的連線 close(sockfd);//關閉監聽用的socket return -1; } char buffer[BUFFER_SIZE]={}; int length=0; while((length=recv(client_fd,buffer,sizeof(buffer),0)) >0) //將從客戶端接收資料快取到buffer中 { //每接收一段資料,便將其寫入檔案中,迴圈直到檔案接收並寫完為止 if(fwrite(buffer,sizeof(char),length,fp)<length) { //如果實際寫入的位元組數少於客戶端傳來的位元組數,則寫入錯誤 printf("File %s Write Failed! \n",file_name);//檔案寫入失敗 fclose(fp); //關閉檔案 close(client_fd); //關閉與客戶端的連線 close(sockfd);//關閉監聽用的socket return -1; } bzero(buffer,sizeof(buffer)); //將buffe清零 } printf("Receive File: %s \n",file_name); //從客戶端得到檔案:%s fclose(fp); //關閉檔案 printf("退出 \n"); close(sockfd);//關閉監聽用的socket return 0; //正常退出 }
/*server_multiple.c*/
/**可同時多個客戶端連線並處理****/
//編譯:gcc server_multiple.c -o server_multiple -lpthread
/*server_multiple.c*/
/**可同時多個客戶端連線並處理****/
//編譯:gcc server_multiple.c -o server_multiple -lpthread
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>//執行緒的標頭檔案
#define PORT 54321
#define BUFFER_SIZE 2048
#define MAX_BACKLOG 5 //連線請求佇列最大長度
#define MAX_CONNECT 10 //伺服器允許同時連線的最大客戶端數量
pthread_t thread_id[MAX_CONNECT]={};
typedef struct name
{
char counter_id;
int _client_fd;
// struct sockaddr_in _client_sockaddr;
}A_struct;
/*******多執行緒任務*******/
void *Client_Process(void *arg)
{
A_struct a;
a=*(struct name *)arg;
char file_name[BUFFER_SIZE]={};
if(recv(a._client_fd,file_name,sizeof(file_name),0)<=0) //接收檔名
{
printf("Local: Server Recieve File Name Failed!(client %d ) \n",a._client_fd); //接收資料失敗
printf("Local: 斷開 client %d 連線\n",a._client_fd);
if(send(a._client_fd,"exit",4,0)<0) //傳送exit
{
;
}
usleep(100000); //延時100ms
close(a._client_fd); //關閉與客戶端的連線
thread_id[a.counter_id]=0;
return;
}
char *temp_1;
while((temp_1=strstr(file_name,"/")) != NULL) //將檔名中的目錄符號刪除,只留下檔名字。
{ //例如原本file_name[]="/temp/test.txt",經過此段程式碼後,會變成file_name[]="test.txt"
temp_1++;
strcpy(file_name,temp_1);
}
printf("Local: Receive file name from client %d : %s \n",a._client_fd,file_name);
FILE *fp=fopen(file_name,"w"); //建立檔案寫入
if(NULL==fp) //新建或開啟檔案進行寫操作失敗
{
printf("Local: File %s Can Not Open To Write! (client %d ) \n",file_name,a._client_fd);
printf("Local: 斷開 client %d 連線\n",a._client_fd);
if(send(a._client_fd,"exit",4,0)<0) //傳送exit
{
;
}
usleep(100000); //延時100ms
close(a._client_fd); //關閉與客戶端的連線
thread_id[a.counter_id]=0;
return;
}
char buffer[BUFFER_SIZE]={};
int length=0;
while((length=recv(a._client_fd,buffer,sizeof(buffer),0)) >0) //將從客戶端接收資料快取到buffer中
{ //每接收一段資料,便將其寫入檔案中,迴圈直到檔案接收並寫完為止
if(fwrite(buffer,sizeof(char),length,fp)<length)
{ //如果實際寫入的位元組數少於客戶端傳來的位元組數,則寫入錯誤
printf("Local: File %s Write Failed! (client %d ) \n",file_name,a._client_fd);//檔案寫入失敗
// break;
fclose(fp); //關閉檔案
printf("Local: 斷開 client %d 連線\n",a._client_fd);
if(send(a._client_fd,"exit",4,0)<0) //傳送exit
{
;
}
usleep(100000); //延時100ms
close(a._client_fd); //關閉與客戶端的連線
thread_id[a.counter_id]=0;
return;
}
bzero(buffer,sizeof(buffer)); //將buffe清零
}
printf("Local: Receive File: %s (client %d ) \n",file_name,a._client_fd); //從客戶端得到檔案:%s
fclose(fp); //關閉檔案
printf("Local: 斷開 client %d \n",a._client_fd);
if(send(a._client_fd,"exit",4,0)<0) //傳送exit
{
;
}
usleep(100000); //延時100ms
close(a._client_fd); //關閉與客戶端的連線
thread_id[a.counter_id]=0;
}
/******主函式********************/
int main( )
{
struct sockaddr_in server_sockaddr,client_sockaddr;
int sockfd,client_fd;
/*建立socket連線*/
if ((sockfd = socket(AF_INET,SOCK_STREAM,0))== -1)
{
perror("Create Socket Failed! \n");//建立套接字失敗
exit(1);
}
/*設定sockaddr_in 結構體中相關引數*/
server_sockaddr.sin_family = AF_INET;
server_sockaddr.sin_port = htons(PORT);
server_sockaddr.sin_addr.s_addr = INADDR_ANY;
socklen_t len = sizeof(server_sockaddr);
bzero(&(server_sockaddr.sin_zero), 8);
int opt = 1;/* 允許重複使用本地地址與套接字進行繫結 */
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
/*繫結函式bind()*/ //將套接字繫結一個IP地址和埠號
if (bind(sockfd, (struct sockaddr *)&server_sockaddr,sizeof(struct sockaddr)) == -1)
{
perror("Server Bind Failed! 埠被佔用 \n"); //套介面與本地ip地址和埠繫結失敗
close(sockfd);
exit(1);
}
/*呼叫listen()函式,建立未處理請求的佇列*/
if (listen(sockfd, MAX_BACKLOG) == -1)
{
perror("Server Listen Failed! \n"); //套接字設定為監聽狀態失敗
close(sockfd);
exit(1);
}
printf("Listening....\n");
while(1)
{
/*呼叫accept()函式,阻塞等待客戶端的連線請求*/
if ((client_fd = accept(sockfd,(struct sockaddr *)&client_sockaddr, &len)) == -1)
{
perror("Server Accept Failed! \n"); //接受連線失敗
close(sockfd);
exit(1);
}
char j;
for(j=0;j<MAX_CONNECT;j++)
{
if(thread_id[j]==0)
{
A_struct temp;
temp.counter_id=j;
temp._client_fd=client_fd;
// temp._client_sockaddr=client_sockaddr;
pthread_create(&thread_id[j],NULL,Client_Process,(void *)&temp);
printf("Local: client %d 連線\n",client_fd);
usleep(1000);
break; //成功建立執行緒則退出迴圈
}
}
if(j==MAX_CONNECT) //執行緒滿
{
char buf1[]="連線達最大上限,請稍後再連線!";
if(send(client_fd,buf1,strlen(buf1),0)<0) //將buf1的資料傳送到套介面
{
;
}
usleep(100000); //100ms
if(send(client_fd,"exit",4,0)<0)
{
;
}
usleep(100000); //100ms,等待發完,再關閉client_fd
close(client_fd);
}
}
printf("Local: 退出 \n");
close(sockfd);//關閉監聽用的socket
return 0; //正常退出
}