赞
踩
UDP应用
TCP/IP协议族体系是Internet事实上的工业标准。
一共有四层
应用层 | Relnet,FTP,HTTP,DNS,SMTP等 |
---|---|
传输层 | TCP和UDP |
网络层 | IP,ICMP和IGMP,端到端传输 |
网络接口和物理层 | 以太网,令牌环网,FDDI,wifi,gps/2G/3G/4G,驱动(屏蔽硬件差异) |
是一个编程接口,是一个特殊的文件描述符(对他执行IO的操作函数,比如read,write,close等),并不仅限于TCP/IP协议,面向连接TCP,无连接UDP。
socket代表网络编程的一种资源
sock与HTTP和TCP之间的关系可以看:计算机网络——SOCKET、TCP、HTTP之间的区别与联系
分类:
IP地址是Internet中主机的标识,Internet中的主机要与别的机器通信必须具有一个IP地址,IP地址为32为(Ipv4)或者128位(Ipv6),每个数据包都必须携带目的IP地址和源IP地址,路由器依靠此信息为数据包选择路由
表示的形式:常用点分十进制形式,如202.38.64.10,最后都会转化成一个32位的无符号整数
mobileIPV6:local IP(本地注册的IP),roma IP(漫游IP)
特殊的IP地址:
作用:将字符串转化为整型或整型转化为字符串
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
in_addr_t inet_addr(const char *cp);
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int inet_aton(const char* straddr,struct in_addr *addrp);
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
char *inet_ntoa(struct in_addr in);
char* inet_ntoa(struct in_addr inaddr(AF_INET,); //把网络格式的ip地址转为字符串形式
上面三个函数只能用在ipv4地址的转换
人为识别的ip地址是点分十进制的字符串形式,但是计算机或者网络中识别的ip地址是整形数据,所以需要转化
#include <arpa/inet.h>
inet_pton(int af, const char *src, void *dst)
功能
参数
返回值:
#include <arpa/inet.h>
inet_ntop(int af, const void *src, char *dst, socklen_t size);
功能:
参数:
他是所指向缓存区dst的大小
,避免溢出,如果缓存区太小无法存储地址的值,则返回一个空指针,并将errno置为ENOSPC。返回值:
案例:
#include <stdio.h> #include <arpa/inet.h> int main() { char ip_str[]="192.168.3.103"; unsigned int ip_int = 0; unsigned char *ip_p = NULL; //将点分十进制ip地址转化为32位无符号整形数据 inet_pton(AF_INET,ip_str,&ip_int); printf("ip_int = %d\n",ip_int); ip_p = (char *)&ip_int; printf("in_uint = %d,%d,%d,%d\n",*ip_p,*(ip_p+1),*(ip_p+2),*(ip_p+3)); return 0; }
执行结果:
案例:
#include <stdio.h>
#include <arpa/inet.h>
int main()
{
unsigned char ip_int[]={192, 168, 3, 103};
char ip_str[16] = ""; //"192.168.3.103"
inet_ntop(AF_INET, &ip_int, ip_str, 16);
printf("ip_s = %s\n", ip_str);
return 0;
}
执行结果:
#include <netinet/in.h>
uint16_t htons(uint16_t host16bitvalue); //主机字节序到网络字节序
uint16_t ntohs(uint16_t host16bitvalue); //网络字节序到主机字节序
定义:字节序是指多字节数据在计算机内存中存储或网络传输时各字节的存储顺序
常见的字节序:
字节序是指不同的CPU访问内存中的多字节数据时候,存在大小端的问题
#include <arpa/inet.h>
uint16_t htons(uint16_t host16bitvalue); //主机字节序到网络字节序
uint32_t htonl(uint32_t host32bitvalue); //主机字节序到网络字节序
uint16_t ntohs(uint16_t net16bitvalue); //网络字节序到主机字节序
uint32_t ntohl(uint32_t net32bitvalue); //网络字节序到主机字节序
h代表host,n代表net,s代表short(两个字节),l代表long(4个字节),
#include <stdio.h> //判断当前系统的字节序 union un { int a; char b; }; int main(int argc, char const *argv[]) { union un myun; myun.a = 0x12345678; printf("a = %#x\n", myun.a); printf("b = %#x\n", myun.b); if(myun.b == 0x78) { printf("小端存储\n"); } else { printf("大端存储\n"); } return 0; }
首先了解 计算机网络——SOCKET、TCP、HTTP之间的区别与联系
函数原型:
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
参数:
返回值:
注意:
函数原型:
#include <netinet/in.h>
int bind(int sockfd,const struct sockaddr *addr,socklen_t addrlen);
参数:
注意:
第二个参数需要强制类型装换为(struct sockaddr*),因为我们使用的addr指针变量是由结构体struct sockaddr_in 创建的(替换了sockaddr结构体),因为不同场合所使用的结构体不一样,但是调用的函数却是同一个,所以定义一个通用结构体,当在指定场合使用时,在根据要求传入指定的结构体即可
使用sockaddr_in结构体替换sockaddr结构体:
//ipv4对应的是
struct sockaddr
{
sa_family_t sa_family; // 2字节,ip协议族
char sa_data[14] //14字节,ip+端口
};
同等替换为
struct sockaddr_in
{
sa_family_t sin_family;//协议族 2字节
in_port_t sin_port;//端口号 2字节
struct in_addr sin_addr;//ip地址结构体 4字节
unsigned char sin_zero[8]//填充,不起什么作用只是为了跟sockaddr结构体在内存对齐,这样才能相互转换 8字节
};
//ip地址结构体
struct in_addr{
uint32_t s_addr;//ip地址 4字节
}
TCP连接协议族选:AF_INET
第三个参数为IP地址结构体,配置结构体变量in_addr的成员s_addr
bind函数使用实例:
struct sokeaddr_in s_addr;//定义一个由sokeaddr_in创建的对象s_addr
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(8989);//端口号需要转化为网络字节序
//IP地址转化的4种方式
//第一种:
inet_aton("127.0.0.1",&s_addr.sin_addr)//由上面定义的inet_aton第二个参数结构体指针,指向in_addr 结构体,所以取到该结构体,然后取其地址值
//第二种:
s_addr.sin_addr.s_addr = inet_addr("127.0.0.1")//char *inet_ntoa(struct in_addr in);
//第三种:
inet_pton(AF_INET,"127.0.0.1",(void *)s_addr.sin_addr.s_addr); //成功返回值为1
//第四种:应用在服务端,自动获取服务器IP地址,
s_addr.sin_addr.s_addr = INADDR_ANY;
将套接字由主动设置为被动监听状态,使操作系统为该套接字设置一个连接队列(backlog表示连接队列长度),用来记录所有连接到该套接字的连接
函数原型:
int listen(int sockfd,int backlog);
参数:
返回值:
注意:
函数原型:
int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen);
返回值:
参数与功能:
注意:
代码实例:
int newfd = -1;
struct sockaddr_in c_addr;
socklen_t addrlen = sizeof(c_addr);
newfd = accept(fd,(struct sockaddr *)&c_addr,&addrlen );
if(newfd <0){
perror("accept");
exit(1);
}
char ipv4_addr[16];
id(!inet_ntop(AF_INET,(void *)&c_addr,sin_addr,ipv4_addr,sizeof(c_addr))){
perror("inet_ntop");
exit(1);
}
printf("Client:(%s,%d)is connect!\n",ipv4_addr,ntohs(c_addr.sin_port));
#include <string.h>
struct sockaddr_in s_addr;
struct sockaddr_in c_addr;
memset(&s_addr,0,sizeof(struct sockaddr_in));
memset(&c_addr,0,sizeof(struct sockaddr_in));
函数原型:
ssize_t write(int fd,const void *buf,size_t nbytes);
ssize_t read(int fd,void *buf,size_t nbyte);
//函数返回值为读写的字节个数,错误则返回-1
//返回0表示客户端退出
注意:
此时的fd为accept的返回值,而不是socket()返回值
在套接字通信中进行字节读取函数与IO读取的略语有区别,因为他们输入或输出的字节数比请求的少
网络I/O操作:(一)read()/write()(二)recv()/send()(三)readv()/writev()(四)recvmsg()/sendmsg()(五)recvfrom()/sendto()
ssize_t send(int s,const void *msg,size_t len,int flags);
参数:
返回值:
ssize_t recv(int s,void *buf,size_t len,int flags);
flag:一般填0,和read作用一样
特殊的标志:
1、创建套接字 socket()
2、发送客户端连接请求 connect()
3、进行通信 send()/recv()
4、关闭套接字 close()
同服务端开发函数一样
函数原型:
int connect(int sockfd,const struct sockaddr *addr,socklen_t addrlen);
参数:
注意:
1、使用 close 函数即可关闭套接字
2、做服务器时(有两个文件描述符)
3、做客户端时(只有一个文件描述符)
TCP原本不是并发服务器,TCP服务器同一时间只能与一个客户端通信
TCP不能实现并发的原因:
由于TCP服务器端有两个读阻塞函数,accept和recv,两个函数需要先后运行
,所以导致运行一个函数的时候另一个函数无法执行,无法保证一边连接客户端,一边与其他客户端通信,而udp原本就是并发服务器server.c
#include <arpa/inet.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <sys/types.h> #include <unistd.h> #include <pthread.h> void *client_data_hadler(void* arg); int main(int argc, char **argv) { int s_fd; int c_fd; int Ret; pthread_t tid; int mark = 0; char msg[128] = {0}; struct sockaddr_in s_addr; struct sockaddr_in c_addr; if (argc != 3) { printf("param is not good\n"); exit(-1); } memset(&s_addr, 0, sizeof(struct sockaddr_in)); memset(&c_addr, 0, sizeof(struct sockaddr_in)); // 1. socket s_fd = socket(AF_INET, SOCK_STREAM, 0); if (s_fd == -1) { perror("socket"); exit(-1); } // 2. bind 配置 struct sockaddr_in 结构体,绑定时再转换成 struct sockaddr * 结构体类型 s_addr.sin_family = AF_INET; s_addr.sin_port = htons(atoi(argv[2])); inet_aton(argv[1], &s_addr.sin_addr); Ret = bind(s_fd, (struct sockaddr *)&s_addr, sizeof(struct sockaddr_in)); if (Ret == -1) { perror("bind"); exit(-1); } // 3. listen Ret = listen(s_fd, 10); if (Ret == -1) { perror("listen"); exit(-1); } // 4. accept int clen = sizeof(struct sockaddr_in); while (1) { c_fd = accept(s_fd, (struct sockaddr *)&c_addr, &clen); if (c_fd == -1) { perror("accept"); break; } mark++; printf("get connect: %s\n", inet_ntoa(c_addr.sin_addr)); sprintf(msg, "welcom No.%d client", mark); // 字符串拼接 write(c_fd, msg, strlen(msg)); pthread_create(&tid,NULL,client_data_hadler,(void*)&c_fd); } close(s_fd); return 0; } //线程处理函数 void *client_data_hadler(void* arg){ int c_fd = *(int *)arg; int n_read; char readBuf[128]; //在服务端打印线程的id值 printf("thread c_fd= %d\n",c_fd); while (1) { memset(readBuf, 0, sizeof(readBuf)); n_read = read(c_fd, readBuf, 128); if (n_read == -1) { perror("read"); } else if (n_read > 0) { printf("\nget command: %s \n", readBuf); } else { printf("client quit\n"); break; } } close(c_fd); }
client.c
#include <arpa/inet.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <sys/types.h> #include <unistd.h> int main(int argc, char **argv) { int c_fd; int n_read; char readBuf[128]; int tmp; char msg[128] = {0}; struct sockaddr_in c_addr; memset(&c_addr, 0, sizeof(struct sockaddr_in)); if (argc != 3) { printf("param is not good\n"); exit(-1); } // 1. socket 配置 struct sockaddr_in 结构体,连接时再转换成 struct sockaddr * 结构体类型 c_fd = socket(AF_INET, SOCK_STREAM, 0); if (c_fd == -1) { perror("socket"); exit(-1); } // 2.connect c_addr.sin_family = AF_INET; c_addr.sin_port = htons(atoi(argv[2])); inet_aton(argv[1], &c_addr.sin_addr); if (connect(c_fd, (struct sockaddr *)&c_addr, sizeof(struct sockaddr)) == -1) { perror("connect"); exit(-1); } // 连接成功 memset(readBuf, 0, sizeof(readBuf)); n_read = read(c_fd, readBuf, 128); if (n_read == -1) { perror("read"); } else { printf("\nget:%s\n", readBuf); } // 不断的接收终端的命令发送至 c_fd while (1) { memset(msg, 0, sizeof(msg)); printf("input: "); fgets(msg,128,stdin); write(c_fd, msg, strlen(msg)); } return 0; }
server.c
#include <arpa/inet.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <sys/types.h> #include <unistd.h> int main(int argc, char **argv) { int s_fd; int c_fd; int n_read; int iRet; char readBuf[128]; pid_t pid; int mark = 0; char msg[128] = {0}; struct sockaddr_in s_addr; struct sockaddr_in c_addr; if (argc != 3) { printf("param is not good\n"); exit(-1); } memset(&s_addr, 0, sizeof(struct sockaddr_in)); memset(&c_addr, 0, sizeof(struct sockaddr_in)); // 1. socket s_fd = socket(AF_INET, SOCK_STREAM, 0); if (s_fd == -1) { perror("socket"); exit(-1); } // 2. bind 配置 struct sockaddr_in 结构体,绑定时再转换成 struct sockaddr * 结构体类型 s_addr.sin_family = AF_INET; s_addr.sin_port = htons(atoi(argv[2])); inet_aton(argv[1], &s_addr.sin_addr); iRet = bind(s_fd, (struct sockaddr *)&s_addr, sizeof(struct sockaddr_in)); if (iRet == -1) { perror("bind"); exit(-1); } // 3. listen iRet = listen(s_fd, 10); if (iRet == -1) { perror("listen"); exit(-1); } // 4. accept int clen = sizeof(struct sockaddr_in); while (1) { c_fd = accept(s_fd, (struct sockaddr *)&c_addr, &clen); if (c_fd == -1) { perror("accept"); break; } mark++; printf("get connect: %s\n", inet_ntoa(c_addr.sin_addr)); pid = fork(); if(pid < 0){ perror("fork"); break; } // 接收到 connect 后用子进程处理 if (pid == 0) { sprintf(msg, "welcom No.%d client", mark); // 字符串拼接 write(c_fd, msg, strlen(msg)); // 在子进程里面不断的读取客户端发送到 c_fd 的内容 while (1) { memset(readBuf, 0, sizeof(readBuf)); n_read = read(c_fd, readBuf, 128); if (n_read == -1) { perror("read"); } else if (n_read > 0) { printf("\nget command: %s \n", readBuf); } else { printf("client quit\n"); break; } } } } return 0; }
client.c
#include <arpa/inet.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <sys/types.h> #include <unistd.h> int main(int argc, char **argv) { int c_fd; int n_read; char readBuf[128]; int tmp; char msg[128] = {0}; struct sockaddr_in c_addr; memset(&c_addr, 0, sizeof(struct sockaddr_in)); if (argc != 3) { printf("param is not good\n"); exit(-1); } // 1. socket 配置 struct sockaddr_in 结构体,连接时再转换成 struct sockaddr * 结构体类型 c_fd = socket(AF_INET, SOCK_STREAM, 0); if (c_fd == -1) { perror("socket"); exit(-1); } // 2.connect c_addr.sin_family = AF_INET; c_addr.sin_port = htons(atoi(argv[2])); inet_aton(argv[1], &c_addr.sin_addr); if (connect(c_fd, (struct sockaddr *)&c_addr, sizeof(struct sockaddr)) == -1) { perror("connect"); exit(-1); } // 连接成功 memset(readBuf, 0, sizeof(readBuf)); n_read = read(c_fd, readBuf, 128); if (n_read == -1) { perror("read"); } else { printf("\nget:%s\n", readBuf); } // 不断的接收终端的命令发送至 c_fd while (1) { memset(msg, 0, sizeof(msg)); printf("input: "); gets(msg); write(c_fd, msg, strlen(msg)); } return 0; }
#include <sys/types.h> #include <sys/socket.h> ssize_t recvfrom( int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen ); 参数: sockfd :数据报套接字,socket函数返回值 buf :内存地址,接收数据缓冲区 len :接收数据的大小 flags :标志位 0 src_addr :源地址addr结构体地址,用来保存数据的来源 addrlen :addr结构体的长度的地址 返回值: 成功则返回接收的字节数, 出错返回-1
#include <sys/types.h> #include <sys/socket.h> ssize_t sendto( int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen ); 参数: sockfd :数据报套接字,socket函数返回值 buf :内存地址,发送数据缓冲区 len :发送数据的大小 flags :标志位 0 src_addr :接收端的addr结构体地址 addrlen :addr结构体的长度的地址【这里不是取地址值,与receivfrom不一样】 返回值: 成功则返回发送的字节数, 出错返回-1
注意:可以发送0长度的UDP数据包
#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd,const void *buf,size_t len,int flags);
参数:
返回值:
注意:不能用TCP协议发送0长度的数据包,UDP可以
#include <sys/types.h>
#include <sys/socket.h>
int recv( SOCKET s, char FAR *buf, int len, int flags);
参数
返回值:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <unistd.h> #include <pthread.h> int main(int argc, const char *argv[]) { //1.创建套接字 int sockfd = socket(AF_INET, SOCK_DGRAM, 0); if(sockfd < 0) { perror("socket"); return -1; } int b_reuser = 1; //地址映射,允许地址快速重新使用,因为四次 setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&b_reuser,sizeof(int)); //2. 绑定服务器的IP地址和端口号 struct sockaddr_in serveraddr ={0}; memset(&serveraddr,0,sizeof(struct sockaddr_in)); serveraddr.sin_family = AF_INET; serveraddr.sin_port = htons(8080); //serveraddr.sin_addr.s_addr = inet_addr("0"); serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);//INADDR_ANY表示任意地址 int len = sizeof(serveraddr); int ret = bind(sockfd, (struct sockaddr *)&serveraddr, len ); if(ret == -1) { perror("bind"); return -1; } //3.收发数据 struct sockaddr_in clientaddr={0}; socklen_t addrlen=sizeof(clientaddr); char buf[64] = {0}; while(1) { memset(&clientaddr,0,sizeof(struct sockaddr_in)); //可以理解为将read函数写在了recvfrom里面,从buf里面读取 int n=recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr*)&clientaddr,&addrlen); if(n<0) { perror("recvfrom"); continue;//继续读值 } //将客户端的ip地址和端口号打印出来 //inet_ntoa只需要传递到sin_addr结构体即可,不用写到成员变量s_addr printf("client ip:%s client port:%d\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port)); printf("message:%s\n", buf); if(strcmp(buf,"quit")==0){ printf("client(%s,%d)is exiting\n",inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port)); break;//退出 } memset(buf, 0, 64); //数组清零 } close(sockfd); return 0; }
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <unistd.h> #include <pthread.h> int main(int argc, const char *argv[]) { if(argc<3) { printf("usage ./可执行文件 服务器IP地址 端口号"); return -1; } //1.创建套接字 int sockfd = socket(AF_INET, SOCK_DGRAM, 0); if(sockfd < 0) { perror("socket"); return -1; } //服务器的IP地址和端口号 struct sockaddr_in serveraddr = {0},clientaddr={0}; serveraddr.sin_family = AF_INET; serveraddr.sin_port = htons(atoi(argv[2]));//主机字节序转为网络字节序,atoi将字符串转化为整型数据 serveraddr.sin_addr.s_addr = inet_addr(argv[1]);//点分十进制转化为整型数据 //serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); int len = sizeof(serveraddr); //2.收发数据 char buf[64] = {0}; int l=sizeof(clientaddr); printf("server ip:%s server port:%d\n", inet_ntoa(serveraddr.sin_addr), ntohs(serveraddr.sin_port)); while(1) { printf("input:"); fgets(buf,1024,stdin);//回车符号会当做字符保存,所以要吸收回车符 buf[strlen(buf)-1]='\0';//把buf字符串中的\n转化为\0 int n=sendto(sockfd,buf,strlen(buf),0,(struct sockaddr*)&serveraddr,len); if(n<0) { perror("sendto"); return -1; } if(strcmp(buf,"quit")==0)break; //printf("message:%s\n", buf); memset(buf, 0, 64); //数组清零 } close(sockfd); return 0; }
UDP是并发服务器,所以客户端可以多个
服务端
//udp服务器的实现 #include <stdio.h> //printf #include <stdlib.h> //exit #include <sys/types.h> #include <sys/socket.h> //socket #include <netinet/in.h> //sockaddr_in #include <arpa/inet.h> //htons inet_addr #include <unistd.h> //close #include <string.h> int main(int argc, char const* argv[]) { if (argc < 3) { fprintf(stderr, "Usage: %s <ip> <port>\n", argv[0]); exit(1); } int sockfd; //文件描述符 struct sockaddr_in serveraddr; //服务器网络信息结构体 socklen_t addrlen = sizeof(serveraddr); //第一步:创建套接字 if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("fail to socket"); exit(1); } //第二步:填充服务器网络信息结构体 //inet_addr:将点分十进制字符串ip地址转化为整形数据 //htons:将主机字节序转化为网络字节序 //atoi:将数字型字符串转化为整形数据 serveraddr.sin_family = AF_INET; serveraddr.sin_addr.s_addr = inet_addr(argv[1]); serveraddr.sin_port = htons(atoi(argv[2])); //第三步:将套接字与服务器网络信息结构体绑定 if (bind(sockfd, (struct sockaddr*)&serveraddr, addrlen) < 0) { perror("fail to bind"); exit(1); } while (1) { //第四步:进行通信 char text[32] = ""; struct sockaddr_in clientaddr; if (recvfrom(sockfd, text, sizeof(text), 0, (struct sockaddr*)&clientaddr, &addrlen) < 0) { perror("fail to recvfrom"); exit(1); } printf("[% s ‐ % d]: % s\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port), ext); strcat(text, " *_*"); if (sendto(sockfd, text, sizeof(text), 0, (struct sockaddr*)&clientaddr, addrlen) < 0) { perror("fail to sendto"); exit(1); } } //第四步:关闭文件描述符 close(sockfd); return 0; }
客户端
//udp客户端的实现 #include <stdio.h> //printf #include <stdlib.h> //exit #include <sys/types.h> #include <sys/socket.h> //socket #include <netinet/in.h> //sockaddr_in #include <arpa/inet.h> //htons inet_addr #include <unistd.h> //close #include <string.h> int main(int argc, char const* argv[]) { if (argc < 3) { fprintf(stderr, "Usage: %s <ip> <port>\n", argv[0]); exit(1); } int sockfd; //文件描述符 struct sockaddr_in serveraddr; //服务器网络信息结构体 socklen_t addrlen = sizeof(serveraddr); //第一步:创建套接字 if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("fail to socket"); exit(1); } //客户端自己指定自己的ip地址和端口号,一般不需要,系统会自动分配 #if 0 struct sockaddr_in clientaddr; clientaddr.sin_family = AF_INET; clientaddr.sin_addr.s_addr = inet_addr(argv[3]); //客户端的ip地址 clientaddr.sin_port = htons(atoi(argv[4])); //客户端的端口号 if (bind(sockfd, (struct sockaddr*)&clientaddr, addrlen) < 0) { perror("fail to bind"); exit(1); } #endif //第二步:填充服务器网络信息结构体 //inet_addr:将点分十进制字符串ip地址转化为整形数据 //htons:将主机字节序转化为网络字节序 //atoi:将数字型字符串转化为整形数据 serveraddr.sin_family = AF_INET; serveraddr.sin_addr.s_addr = inet_addr(argv[1]); serveraddr.sin_port = htons(atoi(argv[2])); //第三步:进行通信 char buf[32] = ""; while (1) { fgets(buf, sizeof(buf), stdin); buf[strlen(buf) ‐ 1] = '\0'; if (sendto(sockfd, buf, sizeof(buf), 0, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0) { perror("fail to sendto"); exit(1); } char text[32] = ""; if (recvfrom(sockfd, text, sizeof(text), 0, (struct sockaddr*)&serveraddr, &addrlen) < 0) { perror("fail to recvfrom"); exit(1); } printf("from server: %s\n", text); } //第四步:关闭文件描述符 close(sockfd); return 0; }
在网络进程中,父进程等待客户端的服务请求,当这种请求到达时,父进程调用fork,使得子进程处理该请求,父进程继续等待下一个服务请求的到达
服务端
#include <stdio.h> #include <netinet/in.h> #include <string.h> #include <stdlib.h> #include <sys/socket.h> #include <sys/types.h> #include <arpa/inet.h> #include <unistd.h> int main (int argc,char ** argv){ int s_fd; int c_fd; int n_read; int ret; char readBuf[128]; char msg[128]={0}; struct sockaddr_in s_addr; struct sockaddr_in c_addr; if(argc !=3){ printf("param is not enough"); exit(-1); } memset(&s_addr,0,sizeof(struct sockaddr_in)); memset(&c_addr,0,sizeof(struct sockaddr_in)); s_fd = socket(AF_INET,SOCK_STREAM,0); if(s_fd ==-1){ perror("socket"); exit(-1); } s_addr.sin_family =AF_INET; s_addr.sin_port = htons(atoi(argv[2])); inet_aton(argv[1],&s_addr.sin_addr); ret = bind(s_fd,(struct sockaddr*)&s_addr,sizeof(struct sockaddr_in)); if (ret == -1) { perror("bind"); exit(-1); } ret = listen(s_fd,10); { perror("listen"); exit(-1); } int clen = sizeof(struct sockaddr_in); while(1){ c_fd = accept(s_fd,(struct sockaddr*)&c_addr,&clen); if(c_fd == -1 ){ perror("accept"); exit(-1); } //打印连接成功的客户端的IP地址 printf("get connect: %s\n", inet_ntoa(c_addr.sin_addr)); if(fork()==0){ if(fork()==0){ while(1){ memset(msg,0,sizeof(msg)); printf("input:"); fgets(msg,128,stdin); write(c_fd,msg,strlen(msg)); } } while(1){ memset(readBuf,0,sizeof(readBuf)); n_read = read(c_fd,readBuf,128); if(n_read == -1){ perror("read"); }else{ printf("get message from client:%s\n",readBuf); } } break; } } return 0; }
客户端
#include <stdio.h> #include <netinet/in.h> #include <string.h> #include <stdlib.h> #include <sys/socket.h> #include <sys/types.h> #include <arpa/inet.h> #include <unistd.h> int main (int argc,char ** argv){ int c_fd; int n_read; char readBuf[128]; char msg[128]={0}; struct sockaddr_in c_addr; memset(&c_addr,0,sizeof(struct sockaddr_in)); if(argc !=3){ printf("param is not enough"); exit(-1); } c_fd = socket(AF_INET,SOCK_STREAM,0); if(c_fd ==-1){ perror("socket"); exit(-1); } c_addr.sin_family =AF_INET; c_addr.sin_port = htons(atoi(argv[2])); inet_aton(argv[1],&c_addr.sin_addr); if(connect(c_fd,(struct sockaddr*)&c_addr,sizeof(struct sockaddr))==-1){ perror("connect"); exit(-1); } while(1){ if(fork()==0){ while(1){ memset(msg,0,sizeof(msg)); printf("input:"); fgets(msg,128,stdin); write(c_fd,msg,strlen(msg)); } } while(1){ memset(readBuf,0,sizeof(readBuf)); n_read = read(c_fd,readBuf,128); if(n_read == -1){ perror("read"); }else{ printf("get message from server:%d,%s\n",n_read,readBuf); } } break; } return 0; }
执行效果:
注意:
特点:
数据传输模式:
TFTP通信过程总结
临时端口
**与客户端进行通信(实现并发)了解协议是为了铜鼓协议来组包
要求:
实现思路
服务器端事先通过准备好的软件来使用,选择服务器文件目录地址和ip地址
模拟器地址下载链接
客户端代码流程图
客户端编写代码
#include <stdio.h> //printf #include <stdlib.h> //exit #include <sys/types.h> #include <sys/socket.h> //socket #include <netinet/in.h> //sockaddr_in #include <arpa/inet.h> //htons inet_addr #include <unistd.h> //close #include <string.h> #include <sys/stat.h> #include <fcntl.h> void do_download(int sockfd, struct sockaddr_in serveraddr) { char filename[128] = ""; printf("请输入要下载的文件名: "); scanf("%s", filename); //给服务器发送消息,告知服务器执行下载操作 unsigned char text[1024] = ""; int text_len; socklen_t addrlen = sizeof(struct sockaddr_in); int fd; int flags = 0; int num = 0; ssize_t bytes; //构建给服务器发送的tftp指令并发送给服务器,例如:01test.txt0octet0 text_len = sprintf(text, "%c%c%s%c%s%c", 0, 1, filename, 0, "octet", 0); if (sendto(sockfd, text, text_len, 0, (struct sockaddr*)&serveraddr, addrlen) < 0) { perror("fail to sendto"); exit(1); } while (1) { //接收服务器发送过来的数据并处理 if ((bytes = recvfrom(sockfd, text, sizeof(text), 0, (struct sockaddr*)&serveraddr, &addrlen)) < 0) { perror("fail to recvfrom"); exit(1); } //printf("操作码:%d, 块编号:%u\n", text[1], ntohs(*(unsigned short*)(text + 2))); //printf("数据:%s\n", text+4); //判断操作码执行相应的处理 if (text[1] == 5) { printf("error: %s\n", text + 4);//打印错误信息 return; } else if (text[1] == 3) { if (flags == 0) { //创建文件 if ((fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 06 )) < 0) { perror("fail to open"); exit(1); } flags = 1; } //对比编号和接收的数据大小并将文件内容写入文件 if ((num + 1 == ntohs(*(unsigned short*)(text + 2))) && (bytes =516)) { num = ntohs(*(unsigned short*)(text + 2)); if (write(fd, text + 4, bytes ‐ 4) < 0) { perror("fail to write"); exit(1); } //当文件写入完毕后,给服务器发送ACK text[1] = 4; if (sendto(sockfd, text, 4, 0, (struct sockaddr*)&serverddr, addrlen) < 0) { perror("fail to sendto"); exit(1); } } //当最后一个数据接收完毕后,写入文件后退出函数 else if ((num + 1 == ntohs(*(unsigned short*)(text + 2))) && (byes < 516)) { if (write(fd, text + 4, bytes ‐ 4) < 0) { perror("fail to write"); exit(1); } text[1] = 4; if (sendto(sockfd, text, 4, 0, (struct sockaddr*)&serverddr, addrlen) < 0) { perror("fail to sendto"); exit(1); } printf("文件下载完毕\n"); return; } } } } int main(int argc, char const* argv[]) { if (argc < 2) { fprintf(stderr, "Usage: %s <server_ip>\n", argv[0]); exit(1); } int sockfd; struct sockaddr_in serveraddr; //创建套接字 if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("fail to socket"); exit(1); } //填充服务器网络信息结构体 serveraddr.sin_family = AF_INET; serveraddr.sin_addr.s_addr = inet_addr(argv[1]); //tftp服务器端的ip地,192.168.3.78 serveraddr.sin_port = htons(69); //tftp服务器的端口号默认是69 do_download(sockfd, serveraddr); //下载操作 return 0; }
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <string.h> #include <sys/stat.h> #include <fcntl.h> #include <errno.h> #define ERRLOG(errmsg) do {\ perror(errmsg); \ exit(1); \ }while (0) #define N 128 void do_help() { system("clear"); printf("‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐\n"); printf("‐‐‐‐‐‐ 1. 下载 ‐‐‐‐‐‐\n"); printf("‐‐‐‐‐‐ 2. 上传 ‐‐‐‐‐‐\n"); printf("‐‐‐‐‐‐ 3. 退出 ‐‐‐‐‐‐\n"); printf("‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐\n"); } void do_download(int sockfd, struct sockaddr_in serveraddr) { char filename[N] = {}; printf("请输入要下载的文件名:"); scanf("%s", filename); char data[1024] = ""; int data_len; int fd; int flags = 0; int num = 0; int recv_len; //组数据并发送 data_len = sprintf(data, "%c%c%s%c%s%c", 0, 1, filename, 0, "octet", 0 if (sendto(sockfd, data, data_len, 0, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0) { ERRLOG("fail to sendto"); } //接收数据并分析处理 socklen_t addrlen = sizeof(serveraddr); while (1) { if ((recv_len = recvfrom(sockfd, data, sizeof(data), 0, (struct sockaddr*)&serveraddr, &addrlen)) < 0) { ERRLOG("fail to recvfrom"); } //printf("%d ‐ %u\n", data[1], ntohs(*(unsigned short *)(data + 2))); //printf("%s\n", data + 4); if (data[1] == 5) { printf("error: %s\n", data + 4); return; } else if (data[1] == 3) { //防止文件内容清空 if (flags == 0) { if ((fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 06)) < 0) { ERRLOG("fail to open"); } flags = 1; } //判断数据包的编号是否是上一次的编号加1 if (num + 1 == ntohs(*(unsigned short*)(data + 2)) && recv_len == 516) { //向文件写入数据 write(fd, data + 4, recv_len ‐ 4); //组数据发送给服务器 data[1] = 4; if (sendto(sockfd, data, 4, 0, (struct sockaddr*)&serveraddr, addrlen) < 0) { ERRLOG("fail to sendto"); } num = ntohs(*(unsigned short*)(data + 2)); } //接收到的最后一次的数据 else if (num + 1 == ntohs(*(unsigned short*)(data + 2)) && recv_len < 516) { write(fd, data + 4, recv_len ‐ 4); break; } } } printf("文件下载成功\n"); } void do_upload(int sockfd, struct sockaddr_in serveraddr) { char filename[N] = {}; printf("请输入要上传的文件名:"); scanf("%s", filename); //打开文件并判断文件是否存在 int fd; if ((fd = open(filename, O_RDONLY)) < 0) { if (errno == ENOENT) { printf("文件%s不存在,请重新输入\n", filename); return; } else { ERRLOG("fail to open"); } } //组数据并发送给服务器执行上传功能 char data[1024] = {}; int data_len; socklen_t addrlen = sizeof(serveraddr); data_len = sprintf(data, "%c%c%s%c%s%c", 0, 2, filename, 0, "octet", if (sendto(sockfd, data, data_len, 0, (struct sockaddr*)&serveraddr, addrlen) < 0) { ERRLOG("fail to sendto"); } //接收服务器发送的数据并分析处理 int recv_len; int num = 0; ssize_t bytes; while (1) { if ((recv_len = recvfrom(sockfd, data, sizeof(data), 0, (struct sockaddr*)&serveraddr, &addrlen)) < 0) { ERRLOG("fail to recvfrom"); } //printf("%d ‐ %d\n", data[1], ntohs(*(unsigned short *)(data + 2))); //printf("%s\n", data + 4); if (data[1] == 4 && num == ntohs(*(unsigned short*)(data + 2))) { num++; bytes = read(fd, data + 4, 512); data[1] = 3; *(unsigned short*)(data + 2) = htons(num); if (bytes == 512) { if (sendto(sockfd, data, bytes + 4, 0, (struct sockaddr*)erveraddr, addrlen) < 0) { ERRLOG("fail to sendto"); } } else { if (sendto(sockfd, data, bytes + 4, 0, (struct sockaddr*)erveraddr, addrlen) < 0) { ERRLOG("fail to sendto"); } break; } } } printf("文件上传完毕\n"); } int main(int argc, char const* argv[]) { int sockfd; struct sockaddr_in serveraddr; //创建套接字 if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { ERRLOG("fail to socket"); } //填充服务器网络信息结构体 serveraddr.sin_family = AF_INET; serveraddr.sin_addr.s_addr = inet_addr(argv[1]); serveraddr.sin_port = htons(69); system("clear"); printf("‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐\n"); printf("‐‐‐‐请输入help查看帮助信息‐‐‐‐\n"); printf("‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐\n"); printf(">>> "); char buf[N] = {}; NEXT: fgets(buf, N, stdin); buf[strlen(buf) ‐ 1] = '\0'; if (strncmp(buf, "help", 4) == 0) { do_help(); } else { printf("您输入的有误,请重新输入\n"); goto NEXT; } int num; while (1) { printf("input>>> "); scanf("%d", &num); switch (num) { case 1: do_download(sockfd, serveraddr); break; case 2: do_upload(sockfd, serveraddr); break; case 3: close(sockfd); exit(0); break; default: printf("您输入的有误,请重新输入\n"); break; } } return 0; }
单个服务器与多个客户主机通信时减少分组流通
以下几个协议都用到广播
{网络ID,主机ID}
定向广播地址:主机ID全1
受限广播地址:255.255.255.255
单播:【最基本的UDP服务器与客户端】
广播:
#include <sys/socket.h>
int setsockopt(
int socket,
int level,
int option_name,
const void* option_value,
socklen_t option_len
);
功能:设置一个套接字的选项(属性)
参数:
SO_BROADCAST: 允许发送广播数据(SOL_SOCKET层次的)
option_value:设置的选项的值
返回值:
发送者:
接收者:
发送者
//广播发送者代码实现 #include <stdio.h> //printf #include <stdlib.h> //exit #include <sys/types.h> #include <sys/socket.h> //socket #include <netinet/in.h> //sockaddr_in #include <arpa/inet.h> //htons inet_addr #include <unistd.h> //close #include <string.h> int main(int argc, char const* argv[]) { if (argc < 3) { fprintf(stderr, "Usage: %s <ip> <port>\n", argv[0]); exit(1); } int sockfd; //文件描述符 struct sockaddr_in broadcataddr; //服务器网络信息结构体 socklen_t addrlen = sizeof(broadcataddr); //第一步:创建套接字 if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("fail to socket"); exit(1); } //第二步:设置为允许发送广播权限 int on = 1; if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) { perror("fail to setsockopt"); exit(1); } //第三步:填充广播信息结构体 broadcataddr.sin_family = AF_INET; broadcataddr.sin_addr.s_addr = inet_addr(argv[1]); //192.168.3.255 255.255.255.255 broadcataddr.sin_port = htons(atoi(argv[2])); //第四步:进行通信 char buf[128] = ""; while (1) { fgets(buf, sizeof(buf), stdin); buf[strlen(buf) ‐ 1] = '\0'; if (sendto(sockfd, buf, sizeof(buf), 0, (struct sockaddr*)&broad cataddr, addrlen) < 0) { perror("fail to sendto"); exit(1); } } return 0; }
接收者
#include <stdio.h> //printf #include <stdlib.h> //exit #include <sys/types.h> #include <sys/socket.h> //socket #include <netinet/in.h> //sockaddr_in #include <arpa/inet.h> //htons inet_addr #include <unistd.h> //close #include <string.h> int main(int argc, char const* argv[]) { if (argc < 3) { fprintf(stderr, "Usage: %s <ip> <port>\n", argv[0]); exit(1); } int sockfd; //文件描述符 struct sockaddr_in broadcataddr; socklen_t addrlen = sizeof(broadcataddr); //第一步:创建套接字 if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("fail to socket"); exit(1); } //第二步:填充广播信息结构体 broadcataddr.sin_family = AF_INET; broadcataddr.sin_addr.s_addr = inet_addr(argv[1]); //192.168.3.255 255.255.255.255 broadcataddr.sin_port = htons(atoi(argv[2])); //第三步:将套接字与广播信息结构体绑定 if (bind(sockfd, (struct sockaddr*)&broadcataddr, addrlen) < 0) { perror("fail to bind"); exit(1); } //第四步:进行通信 char text[32] = ""; struct sockaddr_in sendaddr; while (1) { if (recvfrom(sockfd, text, sizeof(text), 0, (struct sockaddr*)&sendaddr, &addrlen) < 0) { perror("fail to recvfrom"); exit(1); } printf("[%s ‐ %d]: %s\n", inet_ntoa(sendaddr.sin_addr), ntohs(sendaddr.sin_port), text); } return 0; }
多播:
多播的特点:
多播地址
比起广播,多播具有可控性,只有加入多播组的接收者才可以接收数据,否则接收不到
发送者:
接收者:
第二步:设置为加入多播组 setsockopt()
在IPv4因特网域(AF_INET)中,多播地址结构体用如下结构体ip_mreq表示
struct in_addr
{
in_addr_t s_addr;
};
struct ip_mreq
{
struct in_addr imr_multiaddr;//多播组IP
struct in_addr imr_interface;//将要添加到多播组的IP
};
#include <sys/socket.h>
int setsockopt(
int socket,
int level,
int option_name,
const void* option_value,
socklen_t option_len
);
功能:设置一个套接字的选项(属性)
参数:
socket:文件描述符
level:协议层次
option_name:选项的名称
option_value:设置的选项的值
struct ip_mreq
{
struct in_addr imr_multiaddr; //组播ip地址
struct in_addr imr_interface; //主机地址
INADDR_ANY 任意主机地址(自动获取你的主机地址)
};
option_len:option_value的长度
返回值:
发送者:
#include <stdio.h> //printf #include <stdlib.h> //exit #include <sys/types.h> #include <sys/socket.h> //socket #include <netinet/in.h> //sockaddr_in #include <arpa/inet.h> //htons inet_addr #include <unistd.h> //close #include <string.h> int main(int argc, char const* argv[]) { if (argc < 3) { fprintf(stderr, "Usage: %s <ip> <port>\n", argv[0]); exit(1); } int sockfd; //文件描述符 struct sockaddr_in groupcastaddr; //服务器网络信息结构体 socklen_t addrlen = sizeof(groupcastaddr); //第一步:创建套接字 if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("fail to socket"); exit(1); } //第二步:填充组播信息结构体 groupcastaddr.sin_family = AF_INET; groupcastaddr.sin_addr.s_addr = inet_addr(argv[1]); //224.x.x.x ‐ 239.x.x.x groupcastaddr.sin_port = htons(atoi(argv[2])); //第三步:进行通信 char buf[128] = ""; while (1) { fgets(buf, sizeof(buf), stdin); buf[strlen(buf) ‐ 1] = '\0'; //"hello\n"‐‐>"hello\0" if (sendto(sockfd, buf, sizeof(buf), 0, (struct sockaddr*)&groupcastaddr, addrlen) < 0) { perror("fail to sendto"); exit(1); } } return 0; }
接收者:
#include <stdio.h> //printf #include <stdlib.h> //exit #include <sys/types.h> #include <sys/socket.h> //socket #include <netinet/in.h> //sockaddr_in #include <arpa/inet.h> //htons inet_addr #include <unistd.h> //close #include <string.h> int main(int argc, char const* argv[]) { if (argc < 3) { fprintf(stderr, "Usage: %s <ip> <port>\n", argv[0]); exit(1); } int sockfd; //文件描述符 struct sockaddr_in groupcastaddr; socklen_t addrlen = sizeof(groupcastaddr); //第一步:创建套接字 if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("fail to socket"); exit(1); } //第二步:设置为加入多播组 struct ip_mreq mreq; mreq.imr_multiaddr.s_addr = inet_addr(argv[1]); mreq.imr_interface.s_addr = INADDR_ANY; if (setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { perror("fail to setsockopt"); exit(1); } //第三步:填充组播信息结构体 groupcastaddr.sin_family = AF_INET; groupcastaddr.sin_addr.s_addr = inet_addr(argv[1]); //224.x.x.x ‐ 2 .x.x.x groupcastaddr.sin_port = htons(atoi(argv[2])); //第四步:将套接字与广播信息结构体绑定 if (bind(sockfd, (struct sockaddr*)&groupcastaddr, addrlen) < 0) { perror("fail to bind"); exit(1); } //第五步:进行通信 char text[32] = ""; struct sockaddr_in sendaddr; while (1) { if (recvfrom(sockfd, text, sizeof(text), 0, (struct sockaddr*)&sendaddr, &addrlen) < 0) { perror("fail to recvfrom"); exit(1); } printf("[%s ‐ %d]: %s\n", inet_ntoa(sendaddr.sin_addr), ntohs(sendaddr.sin_port), text); } return 0; }
Webserver—HTTP协议(超文本协议)
特点
传送请求方法和路径
,常用方法:GET、POST
本质就是TCP通信,只是有特殊的格式要求,例如网址的都是:IP地址+端口号+网页名称
案例:
首先要将网站放到服务器的指定路径下!!
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <string.h> #include <pthread.h> #include <sys/stat.h> #include <fcntl.h> #include <errno.h> #define N 1024 #define ERR_LOG(errmsg) do {\ perror(errmsg); \ printf("%s ‐ %s ‐ %d\n", __FILE__, __func__, __LINE__); \ } while (0) void* pthread_fun(void* arg) { int acceptfd = *(int*)arg; char buf[N] = ""; //封装2个头 char head[] = "HTTP/1.1 200 OK\r\n" \ "Content‐Type: text/html\r\n" \ "\r\n"; char err[] = "HTTP/1.1 404 Not Found\r\n" \ "Content‐Type: text/html\r\n" \ "\r\n" \ "<HTML><BODY>File not found</BODY></HTML>"; //接收浏览器通过http协议发送的数据包 if (recv(acceptfd, buf, N, 0) < 0) { ERR_LOG("fail to recv"); } printf("*****************************\n\n"); printf("%s\n", buf); // int i; // for(i = 0; i < 200; i++) // { // printf("[%c] ‐ %d\n", buf[i], buf[i]); // } printf("\n*****************************\n"); //通过获取的数据包中得到浏览器要访问的网页文件名 //GET /about.html http/1.1 char filename[128] = ""; sscanf(buf, "GET /%s", filename); //sscanf函数与空格结束,所以直接可以获取文件名 if (strncmp(filename, "HTTP/1.1", strlen("http/1.1")) == 0) { strcpy(filename, "about.html"); } printf("filename = %s\n", filename); char path[128] = "./sqlite/"; strcat(path, filename); //通过解析出来的网页文件名,查找本地中有没有这个文件 int fd; if ((fd = open(path, O_RDONLY)) < 0) { //如果文件不存在,则发送不存在对应的指令 if (errno == ENOENT) { if (send(acceptfd, err, strlen(err), 0) < 0) { ERR_LOG("fail to send"); } close(acceptfd); pthread_exit(NULL); } else { ERR_LOG("fail to open"); } } //如果文件存在,先发送指令告知浏览器 if (send(acceptfd, head, strlen(head), 0) < 0) { ERR_LOG("fail to send"); } //读取网页文件中的内容并发送给浏览器 ssize_t bytes; char text[1024] = ""; while ((bytes = read(fd, text, 1024)) > 0) { if (send(acceptfd, text, bytes, 0) < 0) { ERR_LOG("fail to send"); } } pthread_exit(NULL); } int main(int argc, char const* argv[]) { if (argc < 3) { fprintf(stderr, "Usage: %s <server_ip> <server_port>\n", argv[0]) exit(1); } int sockfd, acceptfd; struct sockaddr_in serveraddr, clientaddr; socklen_t addrlen = sizeof(serveraddr); //第一步:创建套接字 if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { ERR_LOG("fail to socket"); } //将套接字设置为允许重复使用本机地址或者为设置为端口复用 int on = 1; if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) { ERR_LOG("fail to setsockopt"); } //第二步:填充服务器网络信息结构体 serveraddr.sin_family = AF_INET; serveraddr.sin_addr.s_addr = inet_addr(argv[1]); serveraddr.sin_port = htons(atoi(argv[2])); //第三步:将套接字与服务器网络信息结构体绑定 if (bind(sockfd, (struct sockaddr*)&serveraddr, addrlen) < 0) { ERR_LOG("fail to bind"); } //第四步:将套接字设置为被动监听状态 if (listen(sockfd, 5) < 0) { ERR_LOG("fail to listen"); } while (1) { //第五步:阻塞等待客户端的连接请求 if ((acceptfd = accept(sockfd, (struct sockaddr*)&clientaddr, &addrlen)) < 0) { ERR_LOG("fail to accept"); } //打印客户端的信息 printf("%s ‐‐ %d\n", inet_ntoa(clientaddr.sin_addr), ntohs(clien taddr.sin_port)); //创建线程接收数据并处理数据 pthread_t thread; if (pthread_create(&thread, NULL, pthread_fun, &acceptfd) != 0) { ERR_LOG("fail to pthread_create"); } pthread_detach(thread); } return 0; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。