TCP通信:
client.cpp为客户端实现代码
sever.cpp为服务器端实现代码
执行make命令后,生成server和client两个可执行文件。分别打开两个终端窗口,分别执行./server命令和./client 127.0.0.1命令,表示连上本机的6666端口,./server命令的首先执行。执行./client 127.0.0.1命令后,client客户端执行完毕直接退出,这时server的终端窗口输出“recv msg from client:”。打开server.cpp文件所在的目录,可看到tt.jpg文件已经生成。
client.cpp
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netdb.h>
#define MAXLINE 4096
int main(int argc, char** argv){
int sockfd, len;
char buffer[MAXLINE];
struct sockaddr_in servaddr;
FILE *fq;
//创建套接字,打印客户端所要连接的服务器ip地址:127.0.0.1
if( argc != 2){
printf("usage: ./client <ipaddress>\n");
return 0;
}
if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
printf("create socket error: %s(errno: %d)\n", strerror(errno),errno);
return 0;
}
//设置远程地址信息
memset(&servaddr, 0, sizeof(servaddr)); //先把servaddr地址清空,再复制
servaddr.sin_family = AF_INET; //地址族(指定地址格式) ,设为AF_INET
servaddr.sin_port = htons(6666); //连接服务器端口号
// arg[1]是写ip地址的地方,inet_pton 是 IP 地址转换函数,可以在将 IP 地址在“点分十进制”和“二进制整数”之间转换
//inet_pton函数的原型是:int inet_pton(int af,const char *src,void *dst)
//这个函数将字符串转换成网络地址,第一个参数af=AF_INET,,src指向ASCII的地址的首地址(x.x.x.x的格式)
//函数将该地址转换成in_addr的结构体并复制到*dst,即sin_addr中
if( inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0){
printf("inet_pton error for %s\n",argv[1]);
return 0;
}
if( connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0){
printf("connect error: %s(errno: %d)\n",strerror(errno),errno); //连接失败响应函数
return 0;
}
//连接成功后,读取文件内容(/home/kkk/桌面/tt.jpg),发送给服务器端
if( ( fq = fopen("/home/kkk/桌面/tt.jpg","rb") ) == NULL ){
printf("File open.\n");
close(sockfd);
exit(1);
}
bzero(buffer,sizeof(buffer));
//服务器端新建tt.png文件,将接收到的文件内容保存到tt.png中,tt.jpg在当前目录下;
while(!feof(fq)){
len = fread(buffer, 1, sizeof(buffer), fq);
if(len != write(sockfd, buffer, len)){
printf("write.\n");
break;
}
}
close(sockfd); // 关闭套节字
fclose(fq);
return 0;
}
sever.cpp
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<unistd.h>
#define MAXLINE 4096
int main(int argc, char** argv){
int listenfd, connfd;
struct sockaddr_in servaddr;
char buff[4096];
FILE *fp;
int n;
//指定套接字的类型: SOCK_STREAM(即TCP协议,一种可靠的、双向的通信数据流,数据可以准确无误地到达另一台计算机,如果损坏或丢失,可以重新发送。)
//建立一个socket描述符,socket(ipv4,提供面向连接的稳定传输数据,指定协议为0),AFINET决定了要用ipv4地址(32位)与端口号(16位)的组合
if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ){
printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);
return 0; //如果连接错误,打印错误提示
}
printf("----init socket----\n"); //初始化套接字
memset(&servaddr, 0, sizeof(servaddr)); //先把servaddr地址清空,再复制
servaddr.sin_family = AF_INET; //套接字使用的地址族(指定地址格式):ipv4
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //AF_INET表示什么IP都可以连上
servaddr.sin_port = htons(6666); //端口号6666,1024 ~ 49151:普通用户注册的端口号
//设置端口可重用
int contain;
setsockopt(listenfd,SOL_SOCKET, SO_REUSEADDR, &contain, sizeof(int));
将servaddr地址绑定到该socket描述符
if( bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){
printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);
return 0;
}
printf("----bind sucess----\n");
//进入监听模式,监听这个socket描述符,10这里指的是监听队列中允许保持的尚未处理的最大连接数是10
if( listen(listenfd, 10) == -1){
printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);
return 0;
}
if((fp = fopen("tt.jpg","ab") ) == NULL )
{
printf("File.\n");
close(listenfd);
exit(1);
}
printf("======waiting for client's request======\n");
// 循环接受客户的连接请求
while(1){
struct sockaddr_in client_addr;
socklen_t size=sizeof(client_addr);
//一个指向client_addr的指针用于获取对方的地址
if( (connfd = accept(listenfd, (struct sockaddr*)&client_addr, &size)) == -1){
printf("accept socket error: %s(errno: %d)",strerror(errno),errno);
continue;
}
//从客户端接收数据
while(1){
n = read(connfd, buff, MAXLINE); //recv 函数接收到的字符串是不带 ”\0”结束符的
if(n == 0)
break;
fwrite(buff, 1, n, fp);
}
buff[n] = '\0';
printf("recv msg from client: %s\n", buff); 要用 printf输出时必须得先加上结束符”\0"
close(connfd);
fclose(fp);
}
/*在 while 循环里持续接收包,注意, accept 和 read 是都是在 while 循环里的,
也就是收到包之后,listenfd就没用了,并关闭它,下一个包重新接收包 。
*/
close(listenfd); //关闭监听套节字
return 0;
}
运行结果:
参考文章:
Linux C/C++ TCP Socket传输文件或图片实例 - zkfopen - 博客园
Socket原理及实践(Java/C/C++) - xiuzhublog - 博客园文章来源:https://www.toymoban.com/news/detail-644956.html
Linux C/C++ TCP Socket通信实例 - zkfopen - 博客园文章来源地址https://www.toymoban.com/news/detail-644956.html
到了这里,关于TCP通信实现客户端向服务器发送图片的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!