基础知识
socket编程——socket_in结构体
sockaddr_in在头文件#include<netinet/in.h>或#include <arpa/inet.h>中定义,该结构体解决了sockaddr的缺陷,把port和addr 分开储存在两个变量中,如下:
参数说明
sin_family主要用于定义是地址族
sin_port主要用来保存端口号
sin_addr主要用来保存IP地址信息
sin_zero是为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节。用来将sockaddr_in结构填充到与struct sockaddr同样的长度,可以用bzero()或memset()函数将其置为零。
socklen_t类型
socklen_t是一种数据类型,和int差不多,在32位机下,size_t和int的长度相同,都是32 bits,但在64位机下,size_t(32bits)和int(64 bits)的长度是不一样的,socket编程中的accept函数的第三个参数的长度必须和int的长度相同。于是有了socklen_t类型。
名词解释
网络字节顺序 (Network Byte Order) NBO
结构体的sin_port和sin_addr都必须是NBO
本机字节顺序 (Host Byte Order) HBO
一般可视化的数字都是HBO
NBO,HBO二者转换
inet_addr() 将字符串点数格式地址转化成无符号长整型(unsigned long s_addr s_addr;)
inet_aton() 将字符串点数格式地址转化成NBO
inet_ntoa () 将NBO地址转化成字符串点数格式
htons() “Host to Network Short”
htonl() “Host to Network Long”
ntohs() “Network to Host Short”
ntohl() “Network to Host Long”
常用的是htons(),inet_addr()正好对应结构体的端口类型和地址类型
htons()作用是将端口号由主机字节序转换为网络字节序的整数值。(host to net)
htonl()作用和htons()一样,不过它针对的是32位的(long),而htons()针对的是两个字节,16位的(short)。
inet_addr()作用是将一个IP字符串转化为一个网络字节序的整数值,用于sockaddr_in.sin_addr.s_addr。
inet_ntoa()作用是将一个sin_addr结构体输出成IP字符串(network to ascii)。
TCP/UDP网络通信大概交互图
UDP用户数据包模式
报错解决
在测试过程中遇到以下报错:
Connection reset by peer
TCP链接中常见名词是Client Server, 但是网络连接中经常出现Connection reset by peer
报错分析:
client:客户端
server: 服务端
peer: tcp端。
peer是脱离固定场景的, tcp的任意一端都叫peer.
客户端收到Connection reset by peer代表服务端关闭了链接
服务端收到Connection reset by peer代表客户端关闭了链接
关闭链接的原因可能性就比较多了:
防火墙,路由器等。
解决方法:重启服务端端口
tcp
tcp服务端代码
//tcp-server
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#define N 128
#define ERRLOG(errmsg) do{\
perror(errmsg);\
printf("%s--%s(%d)\n", __FILE__, __func__, __LINE__);\
exit(-1);\
}while(0)
typedef struct __MSG{
char buff[N];
int bytes;
}msg_t;
int main(int argc, const char *argv[]){
if(2 != argc){
printf("Usage : %s <port>\n", argv[0]);
exit(-1);
}
//1.创建套接字
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == sockfd){
ERRLOG("socket error");
}
//创建服务器网络信息结构体
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(atoi(argv[1]));
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
socklen_t addrlen = sizeof(server_addr);
//3.将套接字和网络信息结构体进行绑定
if(-1 == bind(sockfd, (struct sockaddr *)&server_addr, addrlen)){
ERRLOG("bind error");
}
//4.将服务器的套接字设置成被动监听状态
if(-1 == listen(sockfd, 5)){
ERRLOG("listen error");
}
//定义一个结构体,保存客户端的信息
struct sockaddr_in client_addr;
memset(&server_addr, 0, sizeof(client_addr));//清空
socklen_t clientaddrlen = sizeof(client_addr);
char buff[N] = {0};
int acceptfd = 0;
ACCEPT:
//5.阻塞等待客户端连接
acceptfd = accept(sockfd, (struct sockaddr *)&client_addr, &clientaddrlen);
if(-1 == acceptfd){
ERRLOG("accept error");
}
printf("客户端 %s:%d 连接到服务器了\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
RENAME:
if(-1 == recv(acceptfd, buff, N, 0)){
ERRLOG("recv error");
}
printf("客户端要下载的文件名为:[%s]\n", buff);
int fd = open(buff, O_RDONLY);
if(-1 == fd){
if(errno == ENOENT){
printf("文件[%s]不存在\n",buff);
if(-1 == send(acceptfd, "****NO EXIST****", N, 0)){
perror("send error");
goto RENAME;
}
}else{
ERRLOG("open error");
}
}
printf("文件[%s]存在\n",buff);
if(-1 == send(acceptfd, "****EXIST****", N, 0)){
ERRLOG("send error");
}
int bytes = 0;
msg_t msg;
memset(&msg, 0, sizeof(msg));
//循环读取文件内容并发送给客户端
while((bytes = read(fd, msg.buff, N))>0){
msg.bytes = bytes;
if(-1 == send(acceptfd, &msg, sizeof(msg), 0)){
ERRLOG("send error");
}
memset(&msg, 0, sizeof(msg));
}
//发送传输完毕的信息
strcpy(msg.buff, "****OVER****");
msg.bytes = 0;
if(-1 == send(acceptfd, &msg, sizeof(msg), 0)){
ERRLOG("send error");
}
close(acceptfd);
goto ACCEPT;
close(sockfd);
return 0;
}
tcp客户端代码
//tcp-cliaent
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#define N 128
#define ERRLOG(errmsg) do{\
perror(errmsg);\
printf("%s--%s(%d)\n", __FILE__, __func__, __LINE__);\
exit(-1);\
}while(0)
typedef struct __MSG{
char buff[N];
int bytes;
}msg_t;
int main(int argc, const char *argv[]){
if(3 != argc){
printf("Usage : %s <ip> <port>\n", argv[0]);
exit(-1);
}
//1.创建套接字
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == sockfd){
ERRLOG("socket error");
}
//创建服务器网络信息结构体
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(atoi(argv[2]));
server_addr.sin_addr.s_addr = inet_addr(argv[1]);
socklen_t addrlen = sizeof(server_addr);
//与服务器建立连接
if(-1 == connect(sockfd, (struct sockaddr *)&server_addr, addrlen)){
ERRLOG("connect error");
}
char filename[N] = {0};
char buff[N] = {0};
int fd = 0;
RENAME:
printf("请输入要下载的文件:");
scanf("%s", filename);
//将要下载的文件发送给服务器
if(-1 == send(sockfd, filename, N, 0)){
ERRLOG("send error");
}
//接收文件是否存在的信息
if(-1 == recv(sockfd, buff, N, 0)){
ERRLOG("recv error");
}
if(0 == strcmp(buff, "****NO EXIST****")){
printf("文件不存在\n");
goto RENAME;
}else if(0 == strcmp(buff, "****EXIST****")){
//创建并打开并清空一个文件,准备下载文件内容
if(-1 == (fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0664))){
ERRLOG("open error");
}
}
int bytes = 0;
msg_t msg;
memset(&msg, 0, sizeof(msg));
while(recv(sockfd, &msg, sizeof(msg), 0)>0){
if(msg.bytes == 0){
break;
}
if(-1 == write(fd, msg.buff, msg.bytes)){
ERRLOG("write error");
}
memset(&msg, 0, sizeof(msg));
}
close(fd);
printf("文件下载完成\n");
//关闭套接字
close(sockfd);
return 0;
}
测试
udp_289">udp
udpdemoudp_290">前言:由于udp是不可靠连接,在传输文件的过程中不能保证文件的完整性,该demo只是实现了udp文件传输,不保证准确性
udp_291">udp服务端
//udp-server
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#define N 512
#define ERRLOG(errmsg) do{\
perror(errmsg);\
printf("%s--%s(%d)\n", __FILE__, __func__, __LINE__);\
exit(-1);\
}while(0)
typedef struct __MSG{
char buff[N];
int bytes;
}msg_t;
int main(int argc, const char *argv[]){
off_t count=0, m,sz;//long
long int n;
char buff[N];
int acceptfd = 0;
if(2 != argc){
printf("Usage : %s <port>\n", argv[0]);
exit(-1);
}
//1.创建用户数据报式套接字
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(-1 == sockfd){
ERRLOG("socket error");
}
//创建服务器网络信息结构体
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(atoi(argv[1]));
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
socklen_t addrlen = sizeof(server_addr);
//3.将套接字和网络信息结构体进行绑定
if(-1 == bind(sockfd, (struct sockaddr *)&server_addr, addrlen)){
ERRLOG("bind error");
}
//定义一个结构体,保存客户端的信息
struct sockaddr_in client_addr;
memset(&server_addr, 0, sizeof(client_addr));//清空
socklen_t clientaddrlen = sizeof(client_addr);
#if 0
while(1){
//接收数据,如果想要给对方回应,就必须保存对方的网络信息结构体
//如果不回应,后两个参数写 NULL 也行
if(-1 == recvfrom(sockfd, buff, N, 0, (struct sockaddr *)&client_addr, &clientaddrlen)){
ERRLOG("recvfrom error");
}
printf("%s(%d):%s\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), buff);
//组装应答信息
strcat(buff, "--server");
if(-1 == sendto(sockfd, buff, N, 0, (struct sockaddr *)&client_addr, clientaddrlen)){
ERRLOG("sendto error");
}
memset(buff, 0, N);
}
close(sockfd);
#endif
ACCEPT:
//5.阻塞等待客户端连接
acceptfd = recvfrom(sockfd,buff,N,0, (struct sockaddr *)&client_addr, &clientaddrlen);
if(-1 == acceptfd ){
ERRLOG("recvfrom error");
}
printf("客户端 %s:%d 连接到服务器了\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
RENAME:
printf("客户端要下载的文件名为:[%s]\n", buff);
int fd = open(buff, O_RDONLY);
if(-1 == fd){
if(errno == ENOENT){
printf("file [%s] inexistence\n",buff);
if(-1 == sendto(sockfd, "****NO EXIST****", N, 0,(struct sockaddr *)&client_addr, clientaddrlen)){
perror("send error");
goto RENAME;
}
}else{
ERRLOG("open error");
}
}
printf("file [%s] exist\n",buff);
if(-1 == sendto(sockfd, "****EXIST****", N, 0,(struct sockaddr *)&client_addr, clientaddrlen)){
ERRLOG("send error");
}
int bytes = 0;
msg_t msg;
memset(&msg, 0, N);
n=read(fd,buff,N);
printf("sending.....\n");
while(n){
if(n == -1){
ERRLOG("read fails");
}
m=sendto(sockfd,buff,N,0,(struct sockaddr*)&client_addr,clientaddrlen);
if(m==-1){
ERRLOG("send error");
}
count+=m;
bzero(buff,N);
n=read(fd,buff,N);
}
msg.bytes = 0;
printf("send over\n");
m=sendto(sockfd,buff,0,0,(struct sockaddr*)&client_addr,clientaddrlen);
printf("The number of bytes transferred : %lld\n",count);
/*
//循环读取文件内容并发送给客户端
while((bytes = read(fd, msg.buff, N))>0){
msg.bytes = bytes;
if(-1 == sendto(sockfd, &msg, sizeof(msg), 0,(struct sockaddr *)&client_addr, clientaddrlen)){
ERRLOG("send error");
}else{
}
memset(&msg, 0, sizeof(msg));
}
//发送传输完毕的信息
strcpy(msg.buff, "****OVER****");
msg.bytes = 0;
if(-1 == sendto(sockfd, &msg, sizeof(msg), 0,(struct sockaddr *)&client_addr, clientaddrlen)){
ERRLOG("send error");
} */
//close(acceptfd);
//goto ACCEPT;
close(sockfd);
close(fd);
return 0;
}
udp_459">udp客户端代码
//udp-client
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#define N 512
#define ERRLOG(errmsg) do{\
perror(errmsg);\
printf("%s--%s(%d)\n", __FILE__, __func__, __LINE__);\
exit(-1);\
}while(0)
typedef struct __MSG{
char buff[N];
int bytes;
}msg_t;
int main(int argc, const char *argv[]){
off_t count=0, n; // long type
char filename[N];
char buff[N];
int fd = 0;
if(3 != argc){
printf("Usage : %s <ip> <port>\n", argv[0]);
exit(-1);
}
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(-1 == sockfd){
ERRLOG("socket error");
}
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(atoi(argv[2]));
server_addr.sin_addr.s_addr = inet_addr(argv[1]);
socklen_t addrlen = sizeof(server_addr);
#if 0
while(1){
printf("input your msg:");
fgets(buff, N, stdin);
buff[strlen(buff)-1] = '\0';//清除 \n
if(0 == strcmp(buff, "quit")){
break;
}
if(-1 == sendto(sockfd, buff, N, 0, (struct sockaddr *)&server_addr, addrlen)){
ERRLOG("sendto error");
}
if(-1 == recvfrom(sockfd, buff, N, 0, NULL, NULL)){
ERRLOG("recvfrom error");
}
printf("recv:[%s]\n", buff);
memset(buff, 0, N);
}
//关闭套接字
close(sockfd);
#endif
RENAME:
printf("Please enter the file you want to download:");
scanf("%s", filename);
if(-1 == sendto(sockfd, filename, N, 0,(struct sockaddr *)&server_addr, addrlen)){
ERRLOG("send error");
}
if(-1 == recvfrom(sockfd, buff, N, 0,(struct sockaddr *)&server_addr, &addrlen)){
ERRLOG("recv error");
}
if(0 == strcmp(buff, "****NO EXIST****")){
printf("file does not exist\n");
goto RENAME;
}else if(0 == strcmp(buff, "****EXIST****")){
if(-1 == (fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0664))){
ERRLOG("open error");
}
}
// int bytes = 0;
//msg_t msg;
bzero(&buff,N);
n=recvfrom(sockfd,&buff,N,0,(struct sockaddr *)&server_addr,&addrlen);
printf("start receiving.......\n");
while(n){
if(n==-1){
ERRLOG("read fails");
}
count+=n;
write(fd,buff,n);
bzero(buff,N);
n=recvfrom(sockfd,&buff,N,0,(struct sockaddr *)&server_addr,&addrlen);
}
printf("file download completes\n");
printf("The number of bytes receive : %lld\n",count);
/* memset(&msg, 0, sizeof(msg));
while(recvfrom(sockfd, &msg, sizeof(msg), 0,(struct sockaddr *)&server_addr, &addrlen)>0){
if(msg.bytes == 0){
break;
}
if(-1 == write(fd, msg.buff, msg.bytes)){
ERRLOG("write error");
}
memset(&msg, 0, sizeof(msg));
} */
close(fd);
close(sockfd);
return 0;
}