网络编程
学习笔记(五)之网络编程:
协议:公共的约定
OSI七层 理想模型
应用层 http:超文本传输协议 telnet:远程登录协议 SMTP: FTP:文本传输协议
表示层 正文格式约定 GB2312 UTF_8
会话层 负责建立和断开通信的时机
传输层 TCP UDP port
网络层 ip ipv4 ipv6
数据链路层 mac地址 交换机 arp地址解析协议 rarp逆向地址解析协议
物理层 网线 中继器 集线器
TCP/IP四层 工业标准
应用层
传输层
网络层
网络接口和物理层
网关:实现内外网隔离
路由器:主干道上的路由器 有路由功能(从哪来到哪去)
家用路由器: 网关功能 路由功能 DNS映射表 wifi 交换机功能 DHCP(动态ip地址分配)
DNS:域名解析服务 域名-->ip地址
经典DNS:114.114.114.114 电信 8.8.8.8谷歌 223.5.5.5 223.6.6.6 阿里云
浏览器直接解析
DDNS(动态域名解析)
经典域名:www.baidu.com www.360buy.com
port(端口号):16bit位组成 0~65535
用于计算机中区分不同进程
TCP和UDP各有一套自己的端口号(0~65535)
0~1023:知名端口
1024~49151:注册端口
49152~65535:私有端口 开发和测试时自己使用
mac地址(硬件地址): 12位16进制数组成 一共48个bit位
ip地址: 点分十进制 192.168.2.111 以8bit为一组划分成四组,以'.'隔开
ipv4:32bit组成
网络号:
主机号: 全0全1不可用 全0就代表网络号,全1表示广播地址
主机号为1一般代表网关
mask:子网掩码 子网划分使用 ip & mask == 网络号
四类ip
A:网络号占8位 ,主机号24位 2^24 - 2
网络号以 0b 0 开始 : 0b 0 000 0000~0b 0 111 1111 0~127
B:网络号占16位,主机号16位 2^16 - 2
网络号以0b 10 开始 : 0b 10 00 0000.0000 0000 128.0
0b 10 11 1111.1111 1111 191.255
C:网络号占24位,主机号8位 2^8-2
网络号以0b 110 开始 : 0b 110 0 0000.0000 0000.0000 0000 192.0.0
0b 110 1 1111.1111 1111.1111 1111 223.255.255
D:不做网络号和主机号划分
0b 1110 开始 可以用于尝试组播
E:剩余部分 网络测试使用
特殊ip地址:
127.0.0.1 本机回环地址
局域网地址:
A:10.xxx.xxx.xxx
B:172.16.xxx.xxx ~ 172.31.xxx.xxx
C:192.168.xxx.xxx
网络编程: TCP UDP
TCP(类比:打电话):有连接 可靠的 无乱序 无出错 无丢失
UDP(类比:发短信):无连接的 不可靠的 有乱序 有出错 有丢失
UDP编程:
服务器 客户端
socket:编程接口 man 2
ip地址:
port端口号:
字节序: 大端序 低地址存放高字节 网络设备
小端序 低地址存放低字节 个人PC
UDP服务器:
socket man 7 ip
int socket(int domain, int type, int protocol);
功能:创建一个编程接口,并返回文件描述符
参数:1.协议族 AF_INET ipv4;2.套接字类型 SOCK_DGRAM 数据报(UDP) SOCK_STREAM 流式(TCP) SOCK_RAM 原始;3.协议号 填0自动匹配
返回值:成功返回文件描述符,失败返回-1,并设置errno号
bind
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
功能:绑定ip和port
参数:1.socket返回的文件描述符 2.地址结构体 (填充新的地址结构体并强转) 3.地址结构体的长度
返回值:成功返回0,失败返回-1,并设置errno号
老的地址结构体: man 2 bind
struct sockaddr {
sa_family_t sa_family;
char sa_data[14];
}
新的地址结构体: man 7 ip Address Format
struct sockaddr_in {
sa_family_t sin_family; /* address family: AF_INET */ 协议族
in_port_t sin_port; /* port in network byte order */ 大端序的port
struct in_addr sin_addr; /* internet address */ 大端序的ip地址
};
/* Internet address. */
struct in_addr {
uint32_t s_addr; /* address in network byte order */
};
字节序转换函数:
htons host to network short
uint16_t htons(uint16_t hostshort);
参数:小端序的port
返回值:大端序的port
htonl host to network long
uint32_t htonl(uint32_t hostlong);
参数:小端序的ip数值
返回值:大端序的ip数值
ntohs network to host short
uint16_t ntohs(uint16_t netshort);
参数:大端序port
返回值:小端序的port
inet_addr: ip字符串-->小端ip数值-->大端ip数值
in_addr_t inet_addr(const char *cp);
参数:ip地址字符串
返回值:大端序的ip地址数值
inet_ntoa network to ascii 大端ip数值-->小端ip数值-->ip字符串
char *inet_ntoa(struct in_addr in);
参数:大端的ip的结构体!!!
返回值:ip字符串
注:
recvfrom就是read二次封装
read
ssize_t read(int fd, void *buf, size_t count);
recvfrom
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr,socklen_t *addrlen);
参数:1.socket返回的文件描述符 2.应用层准备的buf(接收从内核中获取的数据) 3.buf的大小 4.填0 默认阻塞
5.客户端的地址结构体(不关心填NULL) 6.客户端的地址结构体的长度的地址(不关心填NULL)
返回值:
>0 真实获取的数据字节数
==0 数据获取完毕
-1 失败,并设置errno号
UDP客户端
socket
地址结构体需求:服务器的地址结构体
bind? 可有可无
while(1)
{
sendto
}
sendto:
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
参数:1.socket返回的文件描述符 2.即将发送的数据 3.真实写入数据的长度 4.0
5.服务器的地址结构体 6.服务器的地址结构体长度
1 #include <stdio.h> 2 #include <sys/types.h> /* See NOTES */ 3 #include <sys/socket.h> 4 #include <arpa/inet.h> 5 #include <netinet/in.h> 6 #include <netinet/ip.h> /* superset of previous */ 7 #include <stdlib.h> 8 #include <strings.h> 9 10 int main(int argc, const char *argv[]) 11 { 12 char buf[128]; 13 //打开编程接口 socket 14 int udp_socket; 15 udp_socket = socket(AF_INET, SOCK_DGRAM, 0); 16 if(udp_socket == -1) 17 { 18 perror("fail to socket"); 19 exit(1); 20 } 21 22 //填充地址结构体 23 struct sockaddr_in ser_addr,cli_addr; 24 25 ser_addr.sin_family = AF_INET; 26 ser_addr.sin_port = htons(50000); 27 ser_addr.sin_addr.s_addr = inet_addr("192.168.2.192"); 28 29 //绑定ser ip和port bind 30 if(-1 == bind(udp_socket,(struct sockaddr *)&ser_addr,sizeof(ser_addr))) 31 { 32 perror("fail to bind"); 33 exit(1); 34 } 35 36 socklen_t len = sizeof(cli_addr); 37 38 while(1) 39 { 40 bzero(buf,sizeof(buf)); 41 //recvfrom 42 if(-1 == recvfrom(udp_socket,buf,sizeof(buf),0,(struct sockaddr*)&cli_addr,&len)) 43 { 44 perror("fail to recvfrom"); 45 exit(1); 46 } 47 if(strncmp(buf,"quit",4) == 0) 48 { 49 printf("peer is shutdown!\n"); 50 continue; 51 } 52 53 printf("cli_ip:%s cli_port:%d recv:%s\n",inet_ntoa(cli_addr.sin_addr),ntohs(cli_addr.sin_port),buf); 54 } 55 56 return 0; 57 }
1 #include <stdio.h> 2 #include <sys/types.h> /* See NOTES */ 3 #include <sys/socket.h> 4 #include <arpa/inet.h> 5 #include <netinet/in.h> 6 #include <netinet/ip.h> /* superset of previous */ 7 #include <stdlib.h> 8 #include <strings.h> 9 #include <string.h> 10 11 int main(int argc, const char *argv[]) 12 { 13 char buf[128]; 14 //打开编程接口 socket 15 int udp_socket; 16 udp_socket = socket(AF_INET, SOCK_DGRAM, 0); 17 if(udp_socket == -1) 18 { 19 perror("fail to socket"); 20 exit(1); 21 } 22 23 //填充地址结构体 24 struct sockaddr_in ser_addr; 25 26 ser_addr.sin_family = AF_INET; 27 ser_addr.sin_port = htons(50000); 28 ser_addr.sin_addr.s_addr = inet_addr("192.168.2.192"); 29 30 //bind可有可无 31 32 while(1) 33 { 34 bzero(buf,sizeof(buf)); 35 //sendto 36 fgets(buf,sizeof(buf),stdin); 37 38 sendto(udp_socket,buf,strlen(buf),0,(struct sockaddr*)&ser_addr,sizeof(ser_addr)); 39 40 if(strncmp(buf,"quit",4) == 0) 41 { 42 break; 43 } 44 } 45 46 return 0; 47 }
nc命令:模拟客户端和服务器
-l:代表服务器 不加代表客户端
-u:代表udp 不加代表tcp
格式:
nc 选项 ip地址 port
例如:
udp客户端
nc -u 服务器ip 服务器port
udp服务器
nc -l -u 服务器ip 服务器port
UDP练习:
1.echo回声服务器
1 #include <stdio.h> 2 #include <sys/socket.h> 3 #include <sys/types.h> /* See NOTES */ 4 #include <netinet/in.h> 5 #include <netinet/ip.h> /* superset of previous */ 6 #include <arpa/inet.h> 7 #include <stdlib.h> 8 #include <string.h> 9 10 int main(int argc, const char *argv[]) 11 { 12 char recv_buf[128]; 13 char send_buf[128]; 14 //socket 15 int udp_socket; 16 udp_socket = socket(AF_INET, SOCK_DGRAM, 0); 17 if(udp_socket == -1) 18 { 19 perror("fail to socket"); 20 exit(1); 21 } 22 23 //struct sockaddr_in 24 struct sockaddr_in ser_addr,cli_addr,peeraddr; 25 26 ser_addr.sin_family = AF_INET; 27 ser_addr.sin_port = htons(50000); 28 ser_addr.sin_addr.s_addr = inet_addr("192.168.2.192"); 29 30 //bind 31 if(-1 == bind(udp_socket,(struct sockaddr*)&ser_addr,sizeof(ser_addr))) 32 { 33 perror("fail to open"); 34 exit(1); 35 } 36 37 socklen_t len = sizeof(cli_addr); 38 39 while(1) 40 { 41 memset(recv_buf,0,sizeof(recv_buf)); 42 if(-1 == recvfrom(udp_socket,recv_buf,sizeof(recv_buf),0,(struct sockaddr*)&cli_addr,&len)) 43 { 44 perror("fail to recvfrom"); 45 exit(1); 46 } 47 48 memset(send_buf,0,sizeof(send_buf)); 49 sprintf(send_buf,"ip:%s port:%d say:%s\n",inet_ntoa(cli_addr.sin_addr),ntohs(cli_addr.sin_port),recv_buf); 50 51 peeraddr.sin_family = AF_INET; 52 peeraddr.sin_port = htons(60000); //客户端都默认绑定60000端口号 53 int i; 54 for(i = 2;i <= 254;i++) 55 { 56 peeraddr.sin_addr.s_addr = htonl((192 << 24) + (168 << 16) + (2 << 8) + i); 57 sendto(udp_socket,send_buf,strlen(send_buf),0,(struct sockaddr*)&peeraddr,sizeof(peeraddr)); 58 } 59 60 } 61 return 0; 62 }
2.UDP的伪广播
1 #include <stdio.h> 2 #include <sys/types.h> /* See NOTES */ 3 #include <sys/socket.h> 4 #include <arpa/inet.h> 5 #include <netinet/in.h> 6 #include <netinet/ip.h> /* superset of previous */ 7 #include <stdlib.h> 8 #include <strings.h> 9 #include <string.h> 10 11 int main(int argc, const char *argv[]) 12 { 13 char buf[128]; 14 //打开编程接口 socket 15 int udp_socket; 16 udp_socket = socket(AF_INET, SOCK_DGRAM, 0); 17 if(udp_socket == -1) 18 { 19 perror("fail to socket"); 20 exit(1); 21 } 22 23 struct sockaddr_in ser_addr; 24 ser_addr.sin_family = AF_INET; 25 ser_addr.sin_port = htons(50000); 26 ser_addr.sin_addr.s_addr = inet_addr("192.168.2.255"); 27 28 int optval = 1; 29 if(-1 == setsockopt(udp_socket,SOL_SOCKET,SO_BROADCAST,&optval,sizeof(optval))) 30 { 31 perror("fail to broadcast"); 32 exit(1); 33 } 34 35 while(1) 36 { 37 bzero(buf,sizeof(buf)); 38 //sendto 39 fgets(buf,sizeof(buf),stdin); 40 41 sendto(udp_socket,buf,strlen(buf),0,(struct sockaddr*)&ser_addr,sizeof(ser_addr)); 42 43 } 44 45 return 0; 46 }
模拟客户端
发送:nc -u 服务器ip 服务器port
nc -u 192.168.2.192 50000
接收:nc -u -l 自己的ip 自己的端口60000
sprintf
int sprintf(char *str, const char *format, ...);
参数:1.准备的buf 2.输出格式控制串 3.输出表
功能:将内容按指定格式输出到指定的buf中
TCP编程:
TCP服务器: man 7 tcp 描述服务器和客户端的框架函数 先开后关
socket
bind绑定服务器ip和port
listen :将socket编程监听套接字 用于监听即将到来的连接请求
int listen(int sockfd, int backlog);
参数:1.socket返回的文件描述
2.等待队列的长度 5 高版本内核对该数值仅做参考值,会自动扩展 当内核很忙,且等待队列全满时,客户端可能
会收到一个连接失败的错误 ECONNREFUSED /* Connection refused */
返回值:成功返回0,失败返回-1,并设置errno号
while(1) 循环建立连接
{
accept :
1.从等待队列中抽取第一个成员建立连接(依赖于listen的监听套接字)
2.建立连接后创建了一个新的连接套接字(用于数据交换),并返回该套接字的文件描述符
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数:1.socket返回的处于监听模式的套接字的文件描述 (目前是监听模式) 2.客户端的地址结构体 3.对方地址结构体长度的指针
返回值:成功返回连接套接字的文件描述符 ,失败返回-1,并设置errno号
while(1) 建立连接后循环接收数据 利用recv返回值退出
{
recv :
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
参数:1.accept返回的连接套接字文件描述符 2.应用层准备的buf 3.buf的长度 4.填0 默认阻塞
返回值:
<0 出错设置errno
==0 数据读取完毕
>0 真实读取数据的个数
}
}
1 #include <stdio.h> 2 #include <sys/socket.h> 3 #include <sys/types.h> /* See NOTES */ 4 #include <netinet/in.h> 5 #include <netinet/ip.h> /* superset of previous */ 6 #include <stdlib.h> 7 #include <arpa/inet.h> 8 #include <string.h> 9 10 int main(int argc, const char *argv[]) 11 { 12 char buf[128]; 13 int recv_ret; 14 //socket 使用流式套接字 15 int tcp_socket,con_socket; 16 tcp_socket = socket(AF_INET, SOCK_STREAM, 0); 17 if(tcp_socket == -1) 18 { 19 perror("fail to socket"); 20 exit(1); 21 } 22 23 int optval = 1; 24 //setsockopt 设置套接字属性 25 if(-1 == setsockopt(tcp_socket,SOL_SOCKET,SO_REUSEADDR,&optval,sizeof(optval))) 26 { 27 perror("fail to setsockopt"); 28 exit(1); 29 } 30 31 //struct sockaddr_in 32 struct sockaddr_in ser_addr,cli_addr; 33 34 ser_addr.sin_family = AF_INET; 35 ser_addr.sin_port = htons(50000); 36 ser_addr.sin_addr.s_addr = inet_addr("0.0.0.0"); 37 //0.0.0.0 本机ip地址自动适配 38 39 //bind 服务器ip和port 40 if(-1 == bind(tcp_socket,(struct sockaddr*)&ser_addr,sizeof(ser_addr))) 41 { 42 perror("fail to bind"); 43 exit(1); 44 } 45 46 //listen 将socket变成监听模式 47 if(-1 == listen(tcp_socket,5)) 48 { 49 perror("fail to listen"); 50 exit(1); 51 } 52 53 socklen_t len = sizeof(cli_addr); 54 55 while(1) 56 { 57 //accept 建立连接 58 con_socket = accept(tcp_socket,(struct sockaddr*)&cli_addr,&len); 59 if(con_socket == -1) 60 { 61 perror("fail to accept"); 62 exit(1); 63 } 64 65 printf("%s %d is connnecting!\n",inet_ntoa(cli_addr.sin_addr),ntohs(cli_addr.sin_port)); 66 67 while(1) 68 { 69 memset(buf,0,sizeof(buf)); 70 //recv 接收消息 71 recv_ret = recv(con_socket,buf,sizeof(buf),0); 72 if(recv_ret < 0) 73 { 74 perror("fail to recv"); 75 exit(1); 76 } 77 else if(recv_ret == 0) //数据接收完毕 78 { 79 printf("%s %d is shutdown!\n",inet_ntoa(cli_addr.sin_addr),ntohs(cli_addr.sin_port)); 80 break; 81 } 82 else 83 { 84 printf("recv:%s\n",buf); 85 } 86 } 87 } 88 return 0; 89 }
TCP客户端:
socket
服务器的地址结构体 ip和port
bind?可有可无
connect :发送连接请求
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
参数:1.socket返回的文件描述符 2.服务器的地址结构体 3.服务器的地址结构体长度
返回值:成功返回0,失败返回-1,并设置errno号
while(1)
{
send
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
参数:1.socket返回的文件描述符 2.将要发送的数据buf 3.buf的大小(真实发送的数据长度) 4.0
返回值:成功返回真实发送的字节数,失败返回-1,并设置errno号
}
1 #include <stdio.h> 2 #include <sys/socket.h> 3 #include <sys/types.h> /* See NOTES */ 4 #include <netinet/in.h> 5 #include <netinet/ip.h> /* superset of previous */ 6 #include <arpa/inet.h> 7 #include <stdlib.h> 8 #include <string.h> 9 10 int main(int argc, const char *argv[]) 11 { 12 if(argc != 3) 13 { 14 fprintf(stderr,"please input port and ip!\n"); 15 exit(1); 16 } 17 char buf[128]; 18 //socket 19 int tcp_socket; 20 tcp_socket = socket(AF_INET, SOCK_STREAM, 0); 21 if(tcp_socket == -1) 22 { 23 perror("fail to socket"); 24 exit(1); 25 } 26 27 //服务器地址结构体 28 struct sockaddr_in ser_addr; 29 30 ser_addr.sin_family = AF_INET; 31 ser_addr.sin_port = htons(atoi(argv[1])); 32 ser_addr.sin_addr.s_addr = inet_addr(argv[2]); 33 34 //connect 35 if(-1 == connect(tcp_socket,(struct sockaddr*)&ser_addr,sizeof(ser_addr))) 36 { 37 perror("fail to connect"); 38 exit(1); 39 } 40 41 while(1) 42 { 43 memset(buf,0,sizeof(buf)); 44 45 fgets(buf,sizeof(buf),stdin); 46 47 if(strncmp(buf,"quit",4) == 0) 48 break; 49 //send 50 send(tcp_socket,buf,strlen(buf),0); 51 } 52 53 54 55 return 0; 56 }
错误现象:客户端有链接时,服务器却异常退出,再次登录服务器会出现port冲突的情况
1.等一段时间就行了
2.修改端口号
3.使用函数setsockopt
setsockopt :根据文件描述符设置socket属性
int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);
参数:1.socket返回的文件描述符 2.填写SOL_SOCKET
3.SO_REUSEADDR(bind时会强制绑定port) man 7 socket Socket Options
4.决定功能是否开启 int a = 1 开启 0关闭 取地址放入 5.第四个参数的长度
返回值:成功返回0,失败返回-1,并设置errno号
1 #include <stdio.h> 2 #include <sys/socket.h> 3 #include <sys/types.h> /* See NOTES */ 4 #include <netinet/in.h> 5 #include <netinet/ip.h> /* superset of previous */ 6 #include <stdlib.h> 7 #include <arpa/inet.h> 8 #include <string.h> 9 10 int main(int argc, const char *argv[]) 11 { 12 char buf[128]; 13 int recv_ret; 14 //socket 使用流式套接字 15 int tcp_socket,con_socket; 16 tcp_socket = socket(AF_INET, SOCK_STREAM, 0); 17 if(tcp_socket == -1) 18 { 19 perror("fail to socket"); 20 exit(1); 21 } 22 23 int optval = 1; 24 //setsockopt 设置套接字属性 25 if(-1 == setsockopt(tcp_socket,SOL_SOCKET,SO_REUSEADDR,&optval,sizeof(optval))) 26 { 27 perror("fail to setsockopt"); 28 exit(1); 29 } 30 31 //struct sockaddr_in 32 struct sockaddr_in ser_addr,cli_addr; 33 34 ser_addr.sin_family = AF_INET; 35 ser_addr.sin_port = htons(50000); 36 ser_addr.sin_addr.s_addr = inet_addr("0.0.0.0"); 37 //0.0.0.0 本机ip地址自动适配 38 39 //bind 服务器ip和port 40 if(-1 == bind(tcp_socket,(struct sockaddr*)&ser_addr,sizeof(ser_addr))) 41 { 42 perror("fail to bind"); 43 exit(1); 44 } 45 46 //listen 将socket变成监听模式 47 if(-1 == listen(tcp_socket,5)) 48 { 49 perror("fail to listen"); 50 exit(1); 51 } 52 53 socklen_t len = sizeof(cli_addr); 54 55 while(1) 56 { 57 //accept 建立连接 58 con_socket = accept(tcp_socket,(struct sockaddr*)&cli_addr,&len); 59 if(con_socket == -1) 60 { 61 perror("fail to accept"); 62 exit(1); 63 } 64 65 printf("%s %d is connnecting!\n",inet_ntoa(cli_addr.sin_addr),ntohs(cli_addr.sin_port)); 66 67 while(1) 68 { 69 memset(buf,0,sizeof(buf)); 70 //recv 接收消息 71 recv_ret = recv(con_socket,buf,sizeof(buf),0); 72 if(recv_ret < 0) 73 { 74 perror("fail to recv"); 75 exit(1); 76 } 77 else if(recv_ret == 0) //数据接收完毕 78 { 79 printf("%s %d is shutdown!\n",inet_ntoa(cli_addr.sin_addr),ntohs(cli_addr.sin_port)); 80 break; 81 } 82 else 83 { 84 printf("recv:%s\n",buf); 85 } 86 } 87 } 88 return 0; 89 }
TCP建立连接和断开连接的方式:
安装wireshark:
sudo apt-get install wireshark
使用方法:
运行sudo wireshark
选择网卡any 输入例如: udp.port == 50000 进行筛选
三次握手:
客户端发送一个同部位包裹 SYN=1 服务器收到后返回应答包裹ACK=1并发送SYN = 1请求 客户端收到后回复ACk=1
四次挥手:
客户端发送一个结束位包裹 FIN=1 服务器收到后返回应答包裹ACK=1并发送结束为包裹FIN=1 客户端收到后回复ACK=1
练习:文件传输
1.按字节传输
1 #include <stdio.h> 2 #include <sys/socket.h> 3 #include <sys/types.h> /* See NOTES */ 4 #include <netinet/in.h> 5 #include <sys/stat.h> 6 #include <netinet/ip.h> /* superset of previous */ 7 #include <stdlib.h> 8 #include <fcntl.h> 9 #include <arpa/inet.h> 10 #include <string.h> 11 12 int main(int argc, const char *argv[]) 13 { 14 char buf; 15 int recv_ret; 16 17 //打开文件 open 18 int fd; 19 fd = open("./recvfile.txt",O_WRONLY | O_CREAT | O_TRUNC,0666); 20 if(fd == -1) 21 { 22 perror("fail to open"); 23 exit(1); 24 } 25 26 //socket 使用流式套接字 27 int tcp_socket,con_socket; 28 tcp_socket = socket(AF_INET, SOCK_STREAM, 0); 29 if(tcp_socket == -1) 30 { 31 perror("fail to socket"); 32 exit(1); 33 } 34 35 int optval = 1; 36 //setsockopt 设置套接字属性 37 if(-1 == setsockopt(tcp_socket,SOL_SOCKET,SO_REUSEADDR,&optval,sizeof(optval))) 38 { 39 perror("fail to setsockopt"); 40 exit(1); 41 } 42 43 //struct sockaddr_in 44 struct sockaddr_in ser_addr,cli_addr; 45 46 ser_addr.sin_family = AF_INET; 47 ser_addr.sin_port = htons(50000); 48 ser_addr.sin_addr.s_addr = inet_addr("0.0.0.0"); 49 //0.0.0.0 本机ip地址自动适配 50 51 //bind 服务器ip和port 52 if(-1 == bind(tcp_socket,(struct sockaddr*)&ser_addr,sizeof(ser_addr))) 53 { 54 perror("fail to bind"); 55 exit(1); 56 } 57 58 //listen 将socket变成监听模式 59 if(-1 == listen(tcp_socket,5)) 60 { 61 perror("fail to listen"); 62 exit(1); 63 } 64 65 socklen_t len = sizeof(cli_addr); 66 67 //accept 建立连接 68 con_socket = accept(tcp_socket,(struct sockaddr*)&cli_addr,&len); 69 if(con_socket == -1) 70 { 71 perror("fail to accept"); 72 exit(1); 73 } 74 75 76 while(1) 77 { 78 79 //recv 接收消息 80 recv_ret = recv(con_socket,&buf,1,0); 81 if(recv_ret < 0) 82 { 83 perror("fail to recv"); 84 exit(1); 85 } 86 else if(recv_ret == 0) //数据接收完毕 87 { 88 break; 89 } 90 else 91 { 92 write(fd,&buf,1); 93 } 94 } 95 96 close(fd); 97 close(tcp_socket); 98 close(con_socket); 99 return 0; 100 }
1 #include <stdio.h> 2 #include <sys/socket.h> 3 #include <sys/types.h> /* See NOTES */ 4 #include <netinet/in.h> 5 #include <netinet/ip.h> /* superset of previous */ 6 #include <arpa/inet.h> 7 #include <stdlib.h> 8 #include <string.h> 9 #include <sys/stat.h> 10 #include <fcntl.h> 11 12 int main(int argc, const char *argv[]) 13 { 14 if(argc != 3) 15 { 16 fprintf(stderr,"please input port and ip!\n"); 17 exit(1); 18 } 19 20 //打开文件 open 21 int fd; 22 fd = open("./1.txt",O_RDONLY); 23 if(fd == -1) 24 { 25 perror("fail to open"); 26 exit(1); 27 } 28 29 30 char buf; 31 //socket 32 int tcp_socket; 33 tcp_socket = socket(AF_INET, SOCK_STREAM, 0); 34 if(tcp_socket == -1) 35 { 36 perror("fail to socket"); 37 exit(1); 38 } 39 40 //服务器地址结构体 41 struct sockaddr_in ser_addr; 42 43 ser_addr.sin_family = AF_INET; 44 ser_addr.sin_port = htons(atoi(argv[1])); 45 ser_addr.sin_addr.s_addr = inet_addr(argv[2]); 46 47 //connect 48 if(-1 == connect(tcp_socket,(struct sockaddr*)&ser_addr,sizeof(ser_addr))) 49 { 50 perror("fail to connect"); 51 exit(1); 52 } 53 54 int ret_read; 55 while(1) 56 { 57 58 ret_read = read(fd,&buf,1); 59 if(ret_read == -1) 60 { 61 perror("fail to read"); 62 exit(1); 63 } 64 else if(ret_read == 0) 65 { 66 break; 67 } 68 else 69 { 70 //send 71 send(tcp_socket,&buf,1,0); 72 } 73 } 74 75 close(fd); 76 close(tcp_socket); 77 78 return 0; 79 }
2.按块方式传递
1 #include <stdio.h> 2 #include <sys/socket.h> 3 #include <sys/types.h> /* See NOTES */ 4 #include <sys/stat.h> 5 #include <fcntl.h> 6 #include <netinet/in.h> 7 #include <netinet/ip.h> /* superset of previous */ 8 #include <stdlib.h> 9 #include <arpa/inet.h> 10 #include <string.h> 11 12 int main(int argc, const char *argv[]) 13 { 14 //open 15 int fd; 16 fd = open("./5.jpg",O_WRONLY | O_CREAT | O_TRUNC,0666); 17 if(fd == -1) 18 { 19 perror("fail to open"); 20 exit(1); 21 } 22 23 char buf[128]; 24 int recv_ret; 25 //socket 使用流式套接字 26 int tcp_socket,con_socket; 27 tcp_socket = socket(AF_INET, SOCK_STREAM, 0); 28 if(tcp_socket == -1) 29 { 30 perror("fail to socket"); 31 exit(1); 32 } 33 34 35 //struct sockaddr_in 36 struct sockaddr_in ser_addr,cli_addr; 37 38 ser_addr.sin_family = AF_INET; 39 ser_addr.sin_port = htons(50000); 40 ser_addr.sin_addr.s_addr = inet_addr("0.0.0.0"); 41 //0.0.0.0 本机ip地址自动适配 42 43 //bind 服务器ip和port 44 if(-1 == bind(tcp_socket,(struct sockaddr*)&ser_addr,sizeof(ser_addr))) 45 { 46 perror("fail to bind"); 47 exit(1); 48 } 49 50 //listen 将socket变成监听模式 51 if(-1 == listen(tcp_socket,5)) 52 { 53 perror("fail to listen"); 54 exit(1); 55 } 56 57 socklen_t len = sizeof(cli_addr); 58 59 //accept 建立连接 60 con_socket = accept(tcp_socket,(struct sockaddr*)&cli_addr,&len); 61 if(con_socket == -1) 62 { 63 perror("fail to accept"); 64 exit(1); 65 } 66 67 68 while(1) 69 { 70 memset(buf,0,sizeof(buf)); 71 //recv 接收消息 72 recv_ret = recv(con_socket,buf,sizeof(buf),0); 73 if(recv_ret < 0) 74 { 75 perror("fail to recv"); 76 exit(1); 77 } 78 else if(recv_ret == 0) //数据接收完毕 79 { 80 81 break; 82 } 83 else 84 { 85 write(fd,buf,recv_ret); 86 } 87 } 88 return 0; 89 }
1 #include <stdio.h> 2 #include <sys/socket.h> 3 #include <sys/types.h> /* See NOTES */ 4 #include <sys/stat.h> 5 #include <fcntl.h> 6 #include <netinet/in.h> 7 #include <netinet/ip.h> /* superset of previous */ 8 #include <arpa/inet.h> 9 #include <stdlib.h> 10 #include <string.h> 11 12 int main(int argc, const char *argv[]) 13 { 14 if(argc != 3) 15 { 16 fprintf(stderr,"please input port and ip!\n"); 17 exit(1); 18 } 19 20 //open read_only 21 int fd; 22 fd = open("./3.jpg",O_RDONLY); 23 if(fd == -1) 24 { 25 perror("fail to open"); 26 exit(1); 27 } 28 29 30 31 char buf[128]; 32 //socket 33 int tcp_socket; 34 tcp_socket = socket(AF_INET, SOCK_STREAM, 0); 35 if(tcp_socket == -1) 36 { 37 perror("fail to socket"); 38 exit(1); 39 } 40 41 //服务器地址结构体 42 struct sockaddr_in ser_addr; 43 44 ser_addr.sin_family = AF_INET; 45 ser_addr.sin_port = htons(atoi(argv[1])); 46 ser_addr.sin_addr.s_addr = inet_addr(argv[2]); 47 48 //connect 49 if(-1 == connect(tcp_socket,(struct sockaddr*)&ser_addr,sizeof(ser_addr))) 50 { 51 perror("fail to connect"); 52 exit(1); 53 } 54 55 int ret_read; 56 57 while(1) 58 { 59 memset(buf,0,sizeof(buf)); 60 61 ret_read = read(fd,buf,sizeof(buf)); 62 if(ret_read < 0) 63 { 64 perror("fail to read"); 65 exit(1); 66 } 67 else if(ret_read == 0) 68 { 69 break; 70 } 71 else 72 { 73 //send 读多少写多少 74 send(tcp_socket,buf,ret_read,0); 75 } 76 } 77 78 close(fd); 79 close(tcp_socket); 80 81 return 0; 82 }
3.按结构体方式传递(要求文件名一同传递)
1 #define HQ_FILENAME 1 2 #define HQ_FILEDATA 2 3 4 5 typedef struct a 6 { 7 int type; //内容类型 8 int length;//真实发送的数据长度 9 char buf[128];//正文内容 10 }node_t;
1 #include <stdio.h> 2 #include <sys/socket.h> 3 #include <sys/types.h> /* See NOTES */ 4 #include <netinet/in.h> 5 #include <netinet/ip.h> /* superset of previous */ 6 #include <stdlib.h> 7 #include <sys/stat.h> 8 #include <fcntl.h> 9 #include <arpa/inet.h> 10 #include <string.h> 11 12 #include "file.h" 13 14 15 int main(int argc, const char *argv[]) 16 { 17 int fd; 18 node_t *p = malloc(sizeof(node_t)); 19 int recv_ret; 20 //socket 使用流式套接字 21 int tcp_socket,con_socket; 22 tcp_socket = socket(AF_INET, SOCK_STREAM, 0); 23 if(tcp_socket == -1) 24 { 25 perror("fail to socket"); 26 exit(1); 27 } 28 29 int optval = 1; 30 //setsockopt 设置套接字属性 31 if(-1 == setsockopt(tcp_socket,SOL_SOCKET,SO_REUSEADDR,&optval,sizeof(optval))) 32 { 33 perror("fail to setsockopt"); 34 exit(1); 35 } 36 37 //struct sockaddr_in 38 struct sockaddr_in ser_addr,cli_addr; 39 40 ser_addr.sin_family = AF_INET; 41 ser_addr.sin_port = htons(50000); 42 ser_addr.sin_addr.s_addr = inet_addr("0.0.0.0"); 43 //0.0.0.0 本机ip地址自动适配 44 45 //bind 服务器ip和port 46 if(-1 == bind(tcp_socket,(struct sockaddr*)&ser_addr,sizeof(ser_addr))) 47 { 48 perror("fail to bind"); 49 exit(1); 50 } 51 52 //listen 将socket变成监听模式 53 if(-1 == listen(tcp_socket,5)) 54 { 55 perror("fail to listen"); 56 exit(1); 57 } 58 59 socklen_t len = sizeof(cli_addr); 60 61 //accept 建立连接 62 con_socket = accept(tcp_socket,(struct sockaddr*)&cli_addr,&len); 63 if(con_socket == -1) 64 { 65 perror("fail to accept"); 66 exit(1); 67 } 68 69 while(1) 70 { 71 memset(p,0,sizeof(node_t)); 72 //recv 接收消息 73 recv_ret = recv(con_socket,p,sizeof(node_t),0); 74 if(recv_ret < 0) 75 { 76 perror("fail to recv"); 77 exit(1); 78 } 79 else if(recv_ret == 0) //数据接收完毕 80 { 81 break; 82 } 83 else 84 { 85 //open 86 if(p->type == HQ_FILENAME) 87 { 88 char pathname[128]; 89 memset(pathname,0,sizeof(pathname)); 90 sprintf(pathname,"./%s",p->buf); 91 92 fd = open(pathname,O_WRONLY | O_CREAT | O_TRUNC,0666); 93 if(fd == -1) 94 { 95 perror("fail to open"); 96 exit(1); 97 } 98 } 99 else if(p->type == HQ_FILEDATA) 100 { 101 write(fd,p->buf,p->length); 102 } 103 104 } 105 } 106 107 close(fd); 108 close(tcp_socket); 109 close(con_socket); 110 111 112 return 0; 113 }
1 #include <stdio.h> 2 #include <sys/socket.h> 3 #include <sys/types.h> /* See NOTES */ 4 #include <netinet/in.h> 5 #include <netinet/ip.h> /* superset of previous */ 6 #include <arpa/inet.h> 7 #include <stdlib.h> 8 #include <string.h> 9 #include <sys/stat.h> 10 #include <fcntl.h> 11 12 #include "file.h" 13 14 15 int main(int argc, const char *argv[]) 16 { 17 if(argc != 4) 18 { 19 fprintf(stderr,"please input port ip and filename!\n"); 20 exit(1); 21 } 22 23 //拼接路径 24 char pathname[128]; 25 memset(pathname,0,sizeof(pathname)); 26 sprintf(pathname,"/home/xyx/teach/18072/net_tcp/file_2/%s",argv[3]); 27 28 //open 29 int fd; 30 fd = open(pathname,O_RDONLY); 31 if(fd == -1) 32 { 33 perror("fail to open"); 34 exit(1); 35 } 36 37 node_t *p = malloc(sizeof(node_t)); 38 39 //socket 40 int tcp_socket; 41 tcp_socket = socket(AF_INET, SOCK_STREAM, 0); 42 if(tcp_socket == -1) 43 { 44 perror("fail to socket"); 45 exit(1); 46 } 47 48 //服务器地址结构体 49 struct sockaddr_in ser_addr; 50 51 ser_addr.sin_family = AF_INET; 52 ser_addr.sin_port = htons(atoi(argv[1])); 53 ser_addr.sin_addr.s_addr = inet_addr(argv[2]); 54 55 //connect 56 if(-1 == connect(tcp_socket,(struct sockaddr*)&ser_addr,sizeof(ser_addr))) 57 { 58 perror("fail to connect"); 59 exit(1); 60 } 61 62 //发送文件名 63 memset(p,0,sizeof(node_t)); 64 p->type = HQ_FILENAME; 65 strcpy(p->buf,argv[3]); 66 p->length = strlen(p->buf); 67 68 send(tcp_socket,p,sizeof(node_t),0); 69 70 //发送文件内容 71 int ret_read; 72 while(1) 73 { 74 memset(p,0,sizeof(node_t)); 75 76 p->type = HQ_FILEDATA; 77 78 ret_read = read(fd,p->buf,sizeof(p->buf)); 79 if(ret_read == -1) 80 { 81 perror("fail to read"); 82 exit(1); 83 } 84 else if(ret_read == 0) 85 { 86 break; 87 } 88 else 89 { 90 p->length = ret_read; 91 //send 92 send(tcp_socket,p,sizeof(node_t),0); 93 } 94 } 95 96 close(fd); 97 close(tcp_socket); 98 99 return 0; 100 }
TCP并发服务器: 多进程 僵尸进程务必解决!!
socket
bind
listen
while(1)
accept
fork
while(1)
recv
1.信号忽略
1 #include <stdio.h> 2 #include <sys/socket.h> 3 #include <netinet/in.h> 4 #include <sys/types.h> /* See NOTES */ 5 #include <netinet/ip.h> /* superset of previous */ 6 #include <stdlib.h> 7 #include <unistd.h> 8 #include <arpa/inet.h> 9 #include <string.h> 10 #include <signal.h> 11 12 int main(int argc, const char *argv[]) 13 { 14 signal(SIGCHLD,SIG_IGN); 15 char buf[128]; 16 pid_t pid; 17 //socket 18 int tcp_socket,con_socket; 19 tcp_socket = socket(AF_INET, SOCK_STREAM, 0); 20 if(tcp_socket == -1) 21 { 22 perror("fail to socket"); 23 exit(1); 24 } 25 26 int optval = 1; 27 if(-1 == setsockopt(tcp_socket,SOL_SOCKET,SO_REUSEADDR,&optval,sizeof(optval))) 28 { 29 perror("fail to setsockopt"); 30 exit(1); 31 } 32 33 struct sockaddr_in ser_addr,cli_addr; 34 35 ser_addr.sin_family = AF_INET; 36 ser_addr.sin_port = htons(50000); 37 ser_addr.sin_addr.s_addr = inet_addr("0.0.0.0"); 38 39 //bind 40 if(-1 == bind(tcp_socket,(struct sockaddr*)&ser_addr,sizeof(ser_addr))) 41 { 42 perror("fail to bind"); 43 exit(1); 44 } 45 46 //listen 47 if(-1 == listen(tcp_socket,5)) 48 { 49 perror("fail to listen"); 50 exit(1); 51 } 52 53 socklen_t len = sizeof(cli_addr); 54 55 while(1) 56 { 57 //accept 58 con_socket = accept(tcp_socket,(struct sockaddr*)&cli_addr,&len); 59 if(con_socket == -1) 60 { 61 perror("fail to accept"); 62 exit(1); 63 } 64 65 printf("%s %d is connecting!\n",inet_ntoa(cli_addr.sin_addr),ntohs(cli_addr.sin_port)); 66 67 //fork 68 pid = fork(); 69 if(pid < 0) 70 { 71 perror("fail to fork"); 72 exit(1); 73 } 74 else if(pid == 0) //子进程 处理recv 75 { 76 close(tcp_socket); 77 while(1) 78 { 79 memset(buf,0,sizeof(buf)); 80 //recv 81 int ret_recv; 82 ret_recv = recv(con_socket,buf,sizeof(buf),0); 83 if(ret_recv == -1) 84 { 85 perror("fail to recv"); 86 exit(1); 87 } 88 else if(ret_recv == 0) //数据传输完毕 退出!! 89 { 90 printf("%s %d is shutdown!\n",inet_ntoa(cli_addr.sin_addr),ntohs(cli_addr.sin_port)); 91 close(con_socket); 92 exit(0); 93 } 94 else 95 { 96 printf("ip:%s port:%d say:%s!\n",inet_ntoa(cli_addr.sin_addr),ntohs(cli_addr.sin_port),buf); 97 } 98 99 } 100 101 } 102 else //父建立连接 103 { 104 // wait(NULL); 不能用阻塞了父进程 105 close(con_socket); 106 } 107 108 109 } 110 return 0; 111 }
2.父子孙
1 #include <stdio.h> 2 #include <sys/socket.h> 3 #include <netinet/in.h> 4 #include <sys/types.h> /* See NOTES */ 5 #include <netinet/ip.h> /* superset of previous */ 6 #include <stdlib.h> 7 #include <unistd.h> 8 #include <arpa/inet.h> 9 #include <string.h> 10 11 12 int main(int argc, const char *argv[]) 13 { 14 15 char buf[128]; 16 pid_t pid1,pid2; 17 //socket 18 int tcp_socket,con_socket; 19 tcp_socket = socket(AF_INET, SOCK_STREAM, 0); 20 if(tcp_socket == -1) 21 { 22 perror("fail to socket"); 23 exit(1); 24 } 25 26 int optval = 1; 27 if(-1 == setsockopt(tcp_socket,SOL_SOCKET,SO_REUSEADDR,&optval,sizeof(optval))) 28 { 29 perror("fail to setsockopt"); 30 exit(1); 31 } 32 33 struct sockaddr_in ser_addr,cli_addr; 34 35 ser_addr.sin_family = AF_INET; 36 ser_addr.sin_port = htons(50000); 37 ser_addr.sin_addr.s_addr = inet_addr("0.0.0.0"); 38 39 //bind 40 if(-1 == bind(tcp_socket,(struct sockaddr*)&ser_addr,sizeof(ser_addr))) 41 { 42 perror("fail to bind"); 43 exit(1); 44 } 45 46 //listen 47 if(-1 == listen(tcp_socket,5)) 48 { 49 perror("fail to listen"); 50 exit(1); 51 } 52 53 socklen_t len = sizeof(cli_addr); 54 55 while(1) 56 { 57 //accept 58 con_socket = accept(tcp_socket,(struct sockaddr*)&cli_addr,&len); 59 if(con_socket == -1) 60 { 61 perror("fail to accept"); 62 exit(1); 63 } 64 65 printf("%s %d is connecting!\n",inet_ntoa(cli_addr.sin_addr),ntohs(cli_addr.sin_port)); 66 67 //fork 68 pid1 = fork(); 69 if(pid1 < 0) 70 { 71 perror("fail to fork1"); 72 exit(1); 73 } 74 else if(pid1 == 0) //子进程 进行第二次fork产生孙进程 75 { 76 pid2 = fork(); 77 if(pid2 < 0) 78 { 79 perror("fail to fork2"); 80 exit(1); 81 } 82 else if(pid2 == 0) //孙进程 83 { 84 close(tcp_socket); 85 while(1) 86 { 87 memset(buf,0,sizeof(buf)); 88 //recv 89 int ret_recv; 90 ret_recv = recv(con_socket,buf,sizeof(buf),0); 91 if(ret_recv == -1) 92 { 93 perror("fail to recv"); 94 exit(1); 95 } 96 else if(ret_recv == 0) //数据传输完毕 退出!! 97 { 98 printf("%s %d is shutdown!\n",inet_ntoa(cli_addr.sin_addr),ntohs(cli_addr.sin_port)); 99 close(con_socket); 100 exit(0); 101 } 102 else 103 { 104 printf("ip:%s port:%d say:%s!\n",inet_ntoa(cli_addr.sin_addr),ntohs(cli_addr.sin_port),buf); 105 } 106 107 } 108 } 109 else //子进程 自杀 110 { 111 exit(0); 112 } 113 } 114 115 116 else //父建立连接 回收子进程 117 { 118 wait(NULL); //不能用阻塞了父进程 119 close(con_socket); 120 } 121 122 123 } 124 return 0; 125 }
I/O模型:
阻塞
非阻塞
1.函数自带
recvfrom
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
参数4:int flags是选项 填0默认阻塞状态 填MSG_DONTWAIT表示非阻塞
使用非阻塞选项时会出现错误 EAGAIN EWOULDBLOCK 这两个errno号在当前版本一致
1 #include <stdio.h> 2 #include <sys/types.h> /* See NOTES */ 3 #include <sys/socket.h> 4 #include <arpa/inet.h> 5 #include <netinet/in.h> 6 #include <netinet/ip.h> /* superset of previous */ 7 #include <stdlib.h> 8 #include <strings.h> 9 #include <unistd.h> 10 #include <errno.h> 11 12 13 int main(int argc, const char *argv[]) 14 { 15 char buf[128]; 16 //打开编程接口 socket 17 int udp_socket; 18 udp_socket = socket(AF_INET, SOCK_DGRAM, 0); 19 if(udp_socket == -1) 20 { 21 perror("fail to socket"); 22 exit(1); 23 } 24 25 //填充地址结构体 26 struct sockaddr_in ser_addr,cli_addr; 27 28 ser_addr.sin_family = AF_INET; 29 ser_addr.sin_port = htons(50000); 30 ser_addr.sin_addr.s_addr = inet_addr("192.168.2.192"); 31 32 //绑定ser ip和port bind 33 if(-1 == bind(udp_socket,(struct sockaddr *)&ser_addr,sizeof(ser_addr))) 34 { 35 perror("fail to bind"); 36 exit(1); 37 } 38 39 socklen_t len = sizeof(cli_addr); 40 41 while(1) 42 { 43 bzero(buf,sizeof(buf)); 44 //recvfrom 45 if(-1 == recvfrom(udp_socket,buf,sizeof(buf),MSG_DONTWAIT,(struct sockaddr*)&cli_addr,&len)) 46 { 47 if(errno != EAGAIN || errno != EWOULDBLOCK) 48 { 49 perror("fail to recvfrom"); 50 exit(1); 51 } 52 else 53 { 54 fprintf(stderr,"no data!\n"); 55 } 56 sleep(1); 57 } 58 else 59 { 60 printf("cli_ip:%s cli_port:%d recv:%s\n",inet_ntoa(cli_addr.sin_addr),ntohs(cli_addr.sin_port),buf); 61 } 62 } 63 64 return 0; 65 }
2.fcntl 本质就是对文件描述符进行修改
int fcntl(int fd, int cmd, ... /* arg */ );
参数:
1.文件描述符
2.指令
对文件状态进行操作时使用的宏
F_GETFL 获取文件描述符状态和访问权限 arg忽略 使用后返回文件描述符的状态属性
F_SETFL 利用arg对文件描述符进行状态设置
3.可选参数arg 根据cmd看情况使用
man 2 open
创建旗 O_CREAT O_TRUNC O_EXCL
访问旗 O_RDONLY O_WRONLY O_RDWR
状态旗 O_NONBLOCK 非阻塞
1 #include <stdio.h> 2 #include <sys/types.h> /* See NOTES */ 3 #include <sys/socket.h> 4 #include <arpa/inet.h> 5 #include <netinet/in.h> 6 #include <netinet/ip.h> /* superset of previous */ 7 #include <stdlib.h> 8 #include <strings.h> 9 #include <unistd.h> 10 #include <fcntl.h> 11 #include <errno.h> 12 13 int main(int argc, const char *argv[]) 14 { 15 char buf[128]; 16 //打开编程接口 socket 17 int udp_socket; 18 udp_socket = socket(AF_INET, SOCK_DGRAM, 0); 19 if(udp_socket == -1) 20 { 21 perror("fail to socket"); 22 exit(1); 23 } 24 25 //填充地址结构体 26 struct sockaddr_in ser_addr,cli_addr; 27 28 ser_addr.sin_family = AF_INET; 29 ser_addr.sin_port = htons(50000); 30 ser_addr.sin_addr.s_addr = inet_addr("192.168.2.192"); 31 32 //绑定ser ip和port bind 33 if(-1 == bind(udp_socket,(struct sockaddr *)&ser_addr,sizeof(ser_addr))) 34 { 35 perror("fail to bind"); 36 exit(1); 37 } 38 39 socklen_t len = sizeof(cli_addr); 40 41 int flag,flag1; 42 //1.获取文件描述符状态属性 43 flag = fcntl(udp_socket,F_GETFL); 44 45 //2.添加O_NONBLOCK属性 46 flag = flag | O_NONBLOCK; 47 48 //将flag设置回去 49 fcntl(udp_socket,F_SETFL,flag); 50 51 52 flag1 = fcntl(0,F_GETFL); 53 54 flag1 |= O_NONBLOCK; 55 56 fcntl(0,F_SETFL,flag1); 57 58 while(1) 59 { 60 bzero(buf,sizeof(buf)); 61 62 if(NULL == fgets(buf,sizeof(buf),stdin)) 63 { 64 printf("fgets:%s\n",buf); 65 } 66 67 //recvfrom 68 if(-1 == recvfrom(udp_socket,buf,sizeof(buf),0,(struct sockaddr*)&cli_addr,&len)) 69 { 70 if(errno != EAGAIN || errno != EWOULDBLOCK) 71 { 72 perror("fail to recvfrom"); 73 exit(1); 74 } 75 else 76 { 77 fprintf(stderr,"no data!\n"); 78 } 79 sleep(1); 80 } 81 else 82 { 83 printf("cli_ip:%s cli_port:%d recv:%s\n",inet_ntoa(cli_addr.sin_addr),ntohs(cli_addr.sin_port),buf); 84 85 } 86 } 87 88 return 0; 89 }
3.利用open函数 一般不用
异步IO 由内核监视IO资源,当资源到达时发送SIGIO的信号给执行进程,执行对应操作
1.开启异步功能
fcntl F_GETFL F_SETFL
O_ASYNC man 2 open 状态旗 异步IO
2.将当前进程PID交给内核
fcntl F_SETOWN 需要arg参数的支持
3.捕获SIGIO信号,执行对应操作
signal
1 #include <stdio.h> 2 #include <sys/types.h> /* See NOTES */ 3 #include <sys/socket.h> 4 #include <arpa/inet.h> 5 #include <netinet/in.h> 6 #include <unistd.h> 7 #include <fcntl.h> 8 #include <netinet/ip.h> /* superset of previous */ 9 #include <stdlib.h> 10 #include <strings.h> 11 #include <signal.h> 12 13 char buf[128]; 14 int udp_socket; 15 struct sockaddr_in ser_addr,cli_addr; 16 socklen_t len = sizeof(cli_addr); 17 void handler(int signum) 18 { 19 bzero(buf,sizeof(buf)); 20 //recvfrom 21 if(-1 == recvfrom(udp_socket,buf,sizeof(buf),0,(struct sockaddr*)&cli_addr,&len)) 22 { 23 perror("fail to recvfrom"); 24 exit(1); 25 } 26 27 printf("cli_ip:%s cli_port:%d recv:%s\n",inet_ntoa(cli_addr.sin_addr),ntohs(cli_addr.sin_port),buf); 28 29 } 30 31 32 33 int main(int argc, const char *argv[]) 34 { 35 36 signal(SIGIO,handler); 37 //打开编程接口 socket 38 udp_socket = socket(AF_INET, SOCK_DGRAM, 0); 39 if(udp_socket == -1) 40 { 41 perror("fail to socket"); 42 exit(1); 43 } 44 45 //填充地址结构体 46 47 ser_addr.sin_family = AF_INET; 48 ser_addr.sin_port = htons(50000); 49 ser_addr.sin_addr.s_addr = inet_addr("192.168.2.192"); 50 51 //绑定ser ip和port bind 52 if(-1 == bind(udp_socket,(struct sockaddr *)&ser_addr,sizeof(ser_addr))) 53 { 54 perror("fail to bind"); 55 exit(1); 56 } 57 58 59 //开启异步功能 60 int flag; 61 flag = fcntl(udp_socket,F_GETFL); 62 63 flag |= O_ASYNC; 64 65 fcntl(udp_socket,F_SETFL,flag); 66 67 //将当前进程PID交给内核 68 fcntl(udp_socket,F_SETOWN,getpid()); 69 70 while(1) 71 { 72 sleep(2); 73 printf("no data!\n"); 74 } 75 76 close(udp_socket); 77 return 0; 78 }
多路复用 : 在网络中实现一个进程处理多路IO操作
select
int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
参数:1.最大文件描述符个数 2.读资源表 3.写资源表(填NULL) 4.异常资源表(填NULL)
5.超时结构体(设置阻塞的时间)
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
返回值:成功返回就绪的文件描述符个数 2.失败返回-1 3.0代表超时
功能:1.讲关心的文件描述符传递给内核 2.阻塞等待关心的文件描述符就绪 3.就绪的文件描述符返回在参数中
select编程思考线路:
1.制作表单
表单的形式:fd_set
对表单操作的宏函数
void FD_CLR(int fd, fd_set *set); //从表单中清楚指定文件描述符(不关心)
int FD_ISSET(int fd, fd_set *set); //校验文件描述符是否还在表内存在 1存在 0不存在
void FD_SET(int fd, fd_set *set); //往表单内设置成员(关系内的文件描述符)
void FD_ZERO(fd_set *set); //清0表单
2.将表单传递给内核 select传递
3.内核轮询
4.当有资源就绪时,返回进程 select接收
5.遍历表格,执行对应操作
1 #include <stdio.h> 2 #include <sys/socket.h> 3 #include <sys/types.h> /* See NOTES */ 4 #include <sys/select.h> 5 #include <netinet/in.h> 6 #include <unistd.h> 7 #include <netinet/ip.h> /* superset of previous */ 8 #include <sys/time.h> 9 #include <stdlib.h> 10 #include <arpa/inet.h> 11 #include <string.h> 12 13 int main(int argc, const char *argv[]) 14 { 15 char buf[128]; 16 int recv_ret; 17 //socket 使用流式套接字 18 int tcp_socket,con_socket; 19 tcp_socket = socket(AF_INET, SOCK_STREAM, 0); 20 if(tcp_socket == -1) 21 { 22 perror("fail to socket"); 23 exit(1); 24 } 25 26 int optval = 1; 27 //setsockopt 设置套接字属性 28 if(-1 == setsockopt(tcp_socket,SOL_SOCKET,SO_REUSEADDR,&optval,sizeof(optval))) 29 { 30 perror("fail to setsockopt"); 31 exit(1); 32 } 33 34 //struct sockaddr_in 35 struct sockaddr_in ser_addr,cli_addr; 36 37 ser_addr.sin_family = AF_INET; 38 ser_addr.sin_port = htons(50000); 39 ser_addr.sin_addr.s_addr = inet_addr("0.0.0.0"); 40 //0.0.0.0 本机ip地址自动适配 41 42 //bind 服务器ip和port 43 if(-1 == bind(tcp_socket,(struct sockaddr*)&ser_addr,sizeof(ser_addr))) 44 { 45 perror("fail to bind"); 46 exit(1); 47 } 48 49 //listen 将socket变成监听模式 50 if(-1 == listen(tcp_socket,5)) 51 { 52 perror("fail to listen"); 53 exit(1); 54 } 55 56 socklen_t len = sizeof(cli_addr); 57 58 //表 59 fd_set global,current; 60 61 //添加成员到全局表中(0,监听套接字) 62 FD_ZERO(&global); 63 FD_SET(0,&global); 64 FD_SET(tcp_socket,&global); 65 66 struct timeval tv; 67 int ret_select; 68 int maxfd = tcp_socket; //maxfd是指当前最大的文件描述符的值 69 70 while(1) 71 { 72 FD_ZERO(¤t); 73 current = global; 74 75 tv.tv_sec = 3; 76 tv.tv_usec = 0; 77 //select 78 ret_select = select(maxfd+1,¤t,NULL,NULL,&tv); 79 if(ret_select == -1) 80 { 81 perror("fail to select"); 82 exit(1); 83 } 84 else if(ret_select == 0) 85 { 86 printf("timeout!\n"); 87 continue; 88 } 89 else 90 { 91 if(FD_ISSET(0,¤t)) 92 { 93 memset(buf,0,sizeof(buf)); 94 fgets(buf,sizeof(buf),stdin); 95 printf("fgets:%s\n",buf); 96 } 97 if(FD_ISSET(tcp_socket,¤t)) 98 { 99 memset(&cli_addr,0,len); 100 //accept 建立连接 101 con_socket = accept(tcp_socket,(struct sockaddr*)&cli_addr,&len); 102 if(con_socket == -1) 103 { 104 perror("fail to accept"); 105 exit(1); 106 } 107 108 printf("%s %d is connnecting!\n",inet_ntoa(cli_addr.sin_addr),ntohs(cli_addr.sin_port)); 109 printf("con_socket:%d is add!\n",con_socket); 110 //添加新的文件描述符到global表中 111 FD_SET(con_socket,&global); 112 113 //更新最大文件描述符 114 if(maxfd < con_socket) 115 { 116 maxfd = con_socket; 117 } 118 119 } 120 int i; 121 //循环判断con_socket们中就绪的成员 122 for(i = 3;i <= maxfd;i++) 123 { 124 if(i == tcp_socket) 125 { 126 continue; 127 } 128 129 if(FD_ISSET(i,¤t)) 130 { 131 memset(buf,0,sizeof(buf)); 132 //recv 接收消息 133 recv_ret = recv(i,buf,sizeof(buf),0); 134 if(recv_ret < 0) 135 { 136 perror("fail to recv"); 137 exit(1); 138 } 139 else if(recv_ret == 0) //数据接收完毕 140 { 141 printf("con_socket:%d is del!\n",i); 142 close(i); 143 FD_CLR(i,&global); 144 //优化代码 145 if(maxfd == i) 146 { 147 while(--maxfd) 148 { 149 if(FD_ISSET(maxfd,&global)) 150 break; 151 } 152 } 153 } 154 else 155 { 156 printf("recv:%s\n",buf); 157 } 158 159 } 160 161 } 162 163 164 165 } 166 } 167 168 169 return 0; 170 }
epoll
epoll_create:创建epoll事件 并申请表格大小
int epoll_create(int size);
返回值:成功返回epoll事件的文件描述符 失败返回-1,并设置errno号
epoll_ctl 类似select中的FD_SET FD_CLR
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
参数:
1.epoll_create返回的文件描述符
2. 选项 添加或删除关心的文件描述符 EPOLL_CTL_ADD添加 EPOLL_CTL_DEL删除
3.文件描述符 关心的文件描述符
4.epoll事件结构体
typedef union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
struct epoll_event {
uint32_t events; /* Epoll events */ EPOLLIN 读资源 EPOLLOUT 写资源
epoll_data_t data; /* User data variable */
};
epoll_wait 阻塞等待一个或者多个资源就绪
int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);
参数:1.epoll_create返回的文件描述符 2.struct epoll_event 的数组 (用于接收来自内核的就绪的成员)
3.数组大小 4.超时 -1代表阻塞
返回值:成功返回就绪的文件描述符个数,失败返回-1,0代表没有成员就绪
1 #include <stdio.h> 2 #include <sys/socket.h> 3 #include <sys/types.h> /* See NOTES */ 4 #include <netinet/in.h> 5 #include <netinet/ip.h> /* superset of previous */ 6 #include <stdlib.h> 7 #include <sys/epoll.h> 8 #include <arpa/inet.h> 9 #include <string.h> 10 11 int main(int argc, const char *argv[]) 12 { 13 char buf[128]; 14 int recv_ret; 15 //socket 使用流式套接字 16 int tcp_socket,con_socket; 17 tcp_socket = socket(AF_INET, SOCK_STREAM, 0); 18 if(tcp_socket == -1) 19 { 20 perror("fail to socket"); 21 exit(1); 22 } 23 24 int optval = 1; 25 //setsockopt 设置套接字属性 26 if(-1 == setsockopt(tcp_socket,SOL_SOCKET,SO_REUSEADDR,&optval,sizeof(optval))) 27 { 28 perror("fail to setsockopt"); 29 exit(1); 30 } 31 32 //struct sockaddr_in 33 struct sockaddr_in ser_addr,cli_addr; 34 35 ser_addr.sin_family = AF_INET; 36 ser_addr.sin_port = htons(50000); 37 ser_addr.sin_addr.s_addr = inet_addr("0.0.0.0"); 38 //0.0.0.0 本机ip地址自动适配 39 40 //bind 服务器ip和port 41 if(-1 == bind(tcp_socket,(struct sockaddr*)&ser_addr,sizeof(ser_addr))) 42 { 43 perror("fail to bind"); 44 exit(1); 45 } 46 47 //listen 将socket变成监听模式 48 if(-1 == listen(tcp_socket,5)) 49 { 50 perror("fail to listen"); 51 exit(1); 52 } 53 54 socklen_t len = sizeof(cli_addr); 55 56 //epoll_create 57 int epoll_fd; 58 epoll_fd = epoll_create(50); 59 if(epoll_fd == -1) 60 { 61 perror("fail to epoll_create"); 62 exit(1); 63 } 64 65 //epoll_ctl 添加0和tcp_socket 66 struct epoll_event ev; 67 ev.events = EPOLLIN; 68 ev.data.fd = 0; 69 70 if(-1 == epoll_ctl(epoll_fd,EPOLL_CTL_ADD,0,&ev)) 71 { 72 perror("fail to epoll_ctl 0"); 73 exit(1); 74 } 75 76 ev.events = EPOLLIN; 77 ev.data.fd = tcp_socket; 78 79 if(-1 == epoll_ctl(epoll_fd,EPOLL_CTL_ADD,tcp_socket,&ev)) 80 { 81 perror("fail to epoll_ctl tcp_socket"); 82 exit(1); 83 } 84 85 int ret_epoll; 86 struct epoll_event array[50]; 87 88 while(1) 89 { 90 ret_epoll = epoll_wait(epoll_fd,array,50,-1); 91 if(ret_epoll == -1) 92 { 93 perror("fail to epoll_wait"); 94 exit(1); 95 } 96 97 int i; 98 for(i = 0;i < ret_epoll;i++) 99 { 100 if(array[i].data.fd == 0) 101 { 102 memset(buf,0,sizeof(buf)); 103 fgets(buf,sizeof(buf),stdin); 104 printf("fgets:%s\n",buf); 105 } 106 else if(array[i].data.fd == tcp_socket) 107 { 108 //accept 建立连接 109 con_socket = accept(tcp_socket,(struct sockaddr*)&cli_addr,&len); 110 if(con_socket == -1) 111 { 112 perror("fail to accept"); 113 exit(1); 114 } 115 116 printf("%s %d is connnecting!\n",inet_ntoa(cli_addr.sin_addr),ntohs(cli_addr.sin_port)); 117 printf("con_socket:%d is add!\n",con_socket); 118 119 ev.events = EPOLLIN; 120 ev.data.fd = con_socket; 121 122 if(-1 == epoll_ctl(epoll_fd,EPOLL_CTL_ADD,con_socket,&ev)) 123 { 124 perror("fail to epoll_ctl con_socket"); 125 exit(1); 126 } 127 128 129 } 130 else 131 { 132 memset(buf,0,sizeof(buf)); 133 //recv 接收消息 134 recv_ret = recv(array[i].data.fd,buf,sizeof(buf),0); 135 if(recv_ret < 0) 136 { 137 perror("fail to recv"); 138 exit(1); 139 } 140 else if(recv_ret == 0) //数据接收完毕 141 { 142 printf("con_socket:%d is del!\n",array[i].data.fd); 143 //先删后关 144 if(-1 == epoll_ctl(epoll_fd,EPOLL_CTL_DEL,array[i].data.fd,NULL)) 145 { 146 perror("fail to epoll_ctl del"); 147 exit(1); 148 } 149 close(array[i].data.fd); 150 } 151 else 152 { 153 printf("recv:%s\n",buf); 154 } 155 } 156 } 157 158 } 159 return 0; 160 }
Sqlite3
安装:
sudo dpkg -i *.deb
数据库基本命令:
sqlite3 数据库名.db
.quit 退出数据库
.database 查看数据库文件信息
.table 查看数据库中的表单
.schema 查看表单属性
SQL语句
创建表单:
create table 表单名(列1 列1属性,列2 列2属性,列3 列3属性);
例如:
create table stu(name char[20],id int,sex char[10],score int);
增加数据:
insert into 表单名 values(值1,值2,值3);
例如:
insert into stu values("xm",1,"man",90);
insert into stu values("xh",2," ",80); 省略了一列
查找数据:
select 列名 from 表单名 where + 条件;
例如:
select name from stu where score>=80; 查找分数>=80的人名
select name from stu where score>=80 and score<=90;
全表查询:select * from 表单名;
删除数据:
delete from 表单名 where + 条件;
例如:
delete from stu where name=="xm";
修改数据:
update 表单名 set 列名 = 新的值 where + 条件;
例如:
update stu set sex="girl" where id==2;
删除表单:
drop table 表单名;
sqlite官网
www.sqlite.org
sqlite3的C接口:
1.添加头文件#include<sqlite3.h>
2.编译时链接数据库 -lsqlite3
sqlite3_open 打开数据库文件
int sqlite3_open(const char *filename, /* Database filename (UTF-8) */sqlite3 **ppDb /* OUT: SQLite db handle */);
参数:1.数据库文件名 2.打开数据库成功时返回sqlite3 *类型的指针用于对数据库文件操作(类似FILE*)
返回值:成功返回SQLITE_OK
sqlite3_exec 通过sql语句操作数据库文件
int sqlite3_exec(sqlite3*, /* An open database */const char *sql, /* SQL to be evaluated */int (*callback)(void*,int,char**,char**), /* Callback function */
void *, /* 1st argument to callback */char **errmsg /* Error msg written here */);
参数:1.sqlite3_open打开时返回的sqlite3*的指针;
2.指定的sql语句;
3.callback函数的函数指针 不使用填NULL int (void*,int,char**,char**); 获取数据时通过callback获取;
4.callback函数传参使用 (双向的) 填NULL不使用
5.返回的时错误信息的首地址
返回值:成功返回SQLITE_OK,失败通过参数errmsg返回 记得使用sqlite3_free释放空间
callback:根据查询结果调用,一行数据执行一次,没有数据不执行,按类char *型数组返回
int callback(void *,int,char **,char **);
参数:
1.void * 双向传参 callback->main main(sqlite3_exec)->callback
2.获取到数据的个数
3.该数据存放的数组首地址
4.每一列名字存放的数组首地址
sqlite3_close 关闭数据库文件
int sqlite3_close(sqlite3*);
参数:1.sqlite3_open打开时返回的sqlite3 *的指针
1 #include <stdio.h> 2 #include <sqlite3.h> 3 #include <stdlib.h> 4 #include <string.h> 5 6 int callback1(void *s,int count,char **colval,char **colname); 7 int callback2(void *s,int count,char **colval,char **colname); 8 9 int main(int argc, const char *argv[]) 10 { 11 //sqlite3_open 12 sqlite3 * db; 13 char *errmsg; 14 int ret; 15 ret = sqlite3_open("./test.db",&db); 16 if(ret != SQLITE_OK) 17 { 18 fprintf(stderr,"fail to open db!\n"); 19 exit(1); 20 } 21 22 //对数据库操作 23 //sql语句和sqlite3_exec 24 char *sql = "create table stu(name char[10],sex char[10],score int)"; 25 26 ret = sqlite3_exec(db,sql,NULL,NULL,&errmsg); 27 if(ret != SQLITE_OK) 28 { 29 fprintf(stderr,"fail to sqlite3_exec:%s\n",errmsg); 30 sqlite3_free(errmsg); 31 exit(1); 32 } 33 34 sql = "insert into stu values('xiaom','boy',90)"; 35 36 ret = sqlite3_exec(db,sql,NULL,NULL,&errmsg); 37 if(ret != SQLITE_OK) 38 { 39 fprintf(stderr,"fail to sqlite3_exec:%s\n",errmsg); 40 sqlite3_free(errmsg); 41 exit(1); 42 } 43 44 sql = "insert into stu values('xiaoh','girl',80)"; 45 46 ret = sqlite3_exec(db,sql,NULL,NULL,&errmsg); 47 if(ret != SQLITE_OK) 48 { 49 fprintf(stderr,"fail to sqlite3_exec:%s\n",errmsg); 50 sqlite3_free(errmsg); 51 exit(1); 52 } 53 54 //callback1 55 sql = "select * from stu"; 56 char *s1 = "heiheihei"; 57 58 ret = sqlite3_exec(db,sql,callback1,s1,&errmsg); 59 if(ret != SQLITE_OK) 60 { 61 fprintf(stderr,"fail to sqlite3_exec:%s\n",errmsg); 62 sqlite3_free(errmsg); 63 exit(1); 64 } 65 66 //callback2 67 char recv[20]; 68 ret = sqlite3_exec(db,sql,callback2,recv,&errmsg); 69 if(ret != SQLITE_OK) 70 { 71 fprintf(stderr,"fail to sqlite3_exec:%s\n",errmsg); 72 sqlite3_free(errmsg); 73 exit(1); 74 } 75 76 printf("recv:%s\n",recv); 77 78 //sqlite3_close 79 sqlite3_close(db); 80 return 0; 81 } 82 83 int callback1(void *s,int count,char **colval,char **colname) 84 { 85 if(s != NULL) 86 { 87 printf("callback1:%s\n",(char *)s); 88 } 89 90 int i; 91 for(i = 0;i < count;i++) 92 { 93 printf("%s|%s\n",colname[i],colval[i]); 94 } 95 printf("\n"); 96 97 return 0; 98 99 } 100 101 102 int callback2(void *s,int count,char **colval,char **colname) 103 { 104 int i; 105 for(i = 0;i < count;i++) 106 { 107 printf("%s|",colval[i]); 108 } 109 printf("\n"); 110 111 if(s != NULL) 112 { 113 strcpy((char *)s,"heiheihei2"); 114 } 115 116 return 0; 117 }