socket编程向服务器发送文件,socket编程与大文件传输

前段时间通过学习socket编程实现了客户端与服务器之间的大尺寸文件的传输,最近在参加一些笔试时,也遇到了socket的问题,遂整理出一些东西出来,和大家分享,也便于对这方面不了解的朋友迅速建立概念。

首先介绍一下基于TCP协议的客户端/服务器程序的一般流程:

a4c26d1e5885305701be709a3d33442f.png

服务器调用socket()、bind()、listen()完成初始化后,调用accept()阻塞等待,处于监听端口的状态,客户端调用socket()初始化后,调用connect()发出SYN段并阻塞等待服务器应答,服务器应答一个SYN+ACK段,客户端收到后从connect()返回,同时应答一个ACK段,服务器收到后从accept()返回。

数据传输的过程:

建立连接后,TCP协议提供全双工的通信服务,但是一般的客户端/服务器程序的流程是由客户端主动发起请求,服务器被动处理请求,一问一答的方式。因此,服务器从accept()返回后立刻调用read(),读socket就像读管道一样,如果没有数据到达就阻塞等待,这时客户端调用write()发送请求给服务器,服务器收到后从read()返回,对客户端的请求进行处理,在此期间客户端调用read()阻塞等待服务器的应答,服务器调用write()将处理结果发回给客户端,再次调用read()阻塞等待下一条请求,客户端收到后从read()返回,发送下一条请求,如此循环下去。

如果客户端没有更多的请求了,就调用close()关闭连接,就像写端关闭的管道一样,服务器的read()返回0,这样服务器就知道客户端关闭了连接,也调用close()关闭连接。注意,任何一方调用close()后,连接的两个传输方向都关闭,不能再发送数据了。如果一方调用shutdown()则连接处于半关闭状态,仍可接收对方发来的数据。

下图是典型的UDP客户端/服务器通讯过程,由于UDP较为简单,不再解释:

a4c26d1e5885305701be709a3d33442f.png

最后谈谈我在编程通过socket实现收发比较大的文件时遇到的问题。我本以为系统会自动为我拆分和合并报文,我可以直接在应用层发送和接受完整的文件。但实际测试结果告诉我,并不是这样。如果收发在一个主机上,一次只能接收最大65535字节的文件;如果不在同一主机,则只有1500字节。而发送可以将整个大文件直接送交文件描述符发送,无需自己分割,系统会自动分割;但接收时,需人为多次接收,才能把整个文件收完整。这一点请大家尤其要注意!下面提供给大家一个可以一直循环收到你所需要的数据的函数:

int read_data(int s, char *buf, int n)

{ int bcount;

int br;

bcount= 0;

br= 0;

while (bcount < n) {

if ((br= read(s,buf,n-bcount)) > 0) {

bcount += br;

buf += br;

}

else if (br < 0)

return(-1);

}

return(bcount);

}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
文件传输Socket 编程中的常见需求之一,下面是一个简单的 C 语言程序,用于实现文件传输: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <unistd.h> #define PORT 8080 // 服务器端口号 #define MAXLINE 1024 // 缓冲区大小 int main() { int sockfd; struct sockaddr_in servaddr, cliaddr; // 创建套接字 if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("socket creation failed"); exit(EXIT_FAILURE); } memset(&servaddr, 0, sizeof(servaddr)); memset(&cliaddr, 0, sizeof(cliaddr)); // 设置服务器地址结构 servaddr.sin_family = AF_INET; // IPv4 servaddr.sin_addr.s_addr = INADDR_ANY; servaddr.sin_port = htons(PORT); // 绑定套接字到服务器地址结构 if (bind(sockfd, (const struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) { perror("bind failed"); exit(EXIT_FAILURE); } int len, n; char buffer[MAXLINE]; len = sizeof(cliaddr); // 接收客户端地址结构的大小 // 接收文件名 n = recvfrom(sockfd, (char *)buffer, MAXLINE, MSG_WAITALL, (struct sockaddr *)&cliaddr, &len); buffer[n] = '\0'; char *filename = buffer; FILE *fp = fopen(filename, "r"); if (fp == NULL) { printf("File not found: %s\n", filename); exit(EXIT_FAILURE); } // 读取文件内容并发送到客户端 while (fgets(buffer, MAXLINE, fp)) { sendto(sockfd, buffer, strlen(buffer), 0, (const struct sockaddr *)&cliaddr, len); } printf("File sent successfully!\n"); fclose(fp); close(sockfd); return 0; } ``` 客户端程序如下: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #define PORT 8080 // 服务器端口号 #define MAXLINE 1024 // 缓冲区大小 int main() { int sockfd; struct sockaddr_in servaddr; // 创建套接字 if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("socket creation failed"); exit(EXIT_FAILURE); } memset(&servaddr, 0, sizeof(servaddr)); // 设置服务器地址结构 servaddr.sin_family = AF_INET; // IPv4 servaddr.sin_port = htons(PORT); servaddr.sin_addr.s_addr = INADDR_ANY; int n, len; char *filename = "test.txt"; char buffer[MAXLINE]; // 发送文件名到服务器 sendto(sockfd, (const char *)filename, strlen(filename), 0, (const struct sockaddr *)&servaddr, sizeof(servaddr)); FILE *fp = fopen("received.txt", "w"); if (fp == NULL) { perror("file open failed"); exit(EXIT_FAILURE); } // 接收文件内容并写入到文件中 while ((n = recvfrom(sockfd, (char *)buffer, MAXLINE, MSG_WAITALL, (struct sockaddr *)&servaddr, &len)) > 0) { buffer[n] = '\0'; fputs(buffer, fp); } printf("File received successfully!\n"); fclose(fp); close(sockfd); return 0; } ``` 该程序使用 UDP 协议传输文件,服务器端先接收客户端发送文件名,然后在本地找到该文件,并将其内容发送给客户端。客户端接收文件名后,向服务器发送请求,并将接收到的文件内容写入到磁盘中。 需要注意的是,该程序具有一定的局限性,只能传输文本文件,对于二进制文件或者大文件,需要进行相应的处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值