最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

网络编程

旗下网站admin24浏览0评论

网络编程

网络编程

网络编程 一、TCP1.1、网络基础理论知识1.2、编程实现网络通信(搭建服务器与客户端)(tcp) 二、UDP2.1、UDP编程2.2、广播与组播2.3、setsockopt 的基本用法2.4、广播与组播 三、并发服务器3.1、循环服务器3.2、并发服务器3.3、IO多路复用机制(select、poll、epoll) 四、IO多路复用4.1、Linux下主要有4种i/o模型4.2、IO多路复用机制(select、poll、epoll)4.3、超时检测。 五、数据库操作5.1、数据库操作 六、 unix域套接字编程6.1、unix域套接字编程(本地进程间通信)6.2、抓包分析 七、 项目实训1、项目题目:聊天软件2、项目题目:网络词典3、项目题目:文件服务器(云盘系统)4、项目题目:在线点餐系统 网络编程-调试工具及LinuxC函数手册!跳转:上一篇,IO进线程编程!跳转:下一篇,C++编程总结!

统一声明: 博客转载 声 明 : 本博客部分内容来源于网络、书籍、及各类手册。         内容宗旨为方便查询、总结备份、开源分享。         部分转载内容均有注明出处,如有侵权请联系博客告知并删除,谢谢! 百度云盘提取码:统一提取码: ziyu

个人网站入口:/

一、TCP 1.1、网络基础理论知识 1、早期的ARPAnet使用ncp控制协议2、TCP / IP协议TCP:用来检测网络传输中的差错问题。IP :用于互联不同网络的主机。TCP/IP协议是iniernet网络中的“世界语”3、网络体系结构:网络层次结构和协议的集合。 网络体系结构分为osi模型和tcp/ip模型。*** osi模型7层结构:应用层、表示层、会话层、传输层、网络层、数据链路层、物理层。 tcp/ip体系结构:应用层、 传输层、网络层、网络接口和物理层。 应用层:http 传输层:tcp, udp 网络层: ip, icmp 网络接口层(数据链路与物理层): 以太网 4、TCP/IP是协议是传输控制协议,以称为网络通信协议。tcp/IP协议协议族:TCP:传输控制协议。IP:网间协议UDP:数据报协议SMTP:邮件传输协议HTTP:超文本传输协议5、UDP和TCP ***共同点:同为传输层协议不同点:TCP:面向有连接,可靠(保证数据可靠<数据无失序、无重复、无丢失到达>)UDP:面向无连接,不保证数据可靠TCP:是一种面向连接的传输协议,提供高可靠通信(数据无丢失,数据无误,数据无失序)使用场景:对传输质量要求较高,传输大量数据的通信UDP:用户数据报协议,是不可靠的无连接协议。使用场景: 1.传输小尺寸数据2.应答困难的网络中(无线网络)3.QQ点对点的文件传输和音视频传输4.网络电话、流媒体5.广播、组播 1.2、编程实现网络通信(搭建服务器与客户端)(tcp) 1、socket:是一个编程接口,也是一种(网卡设备文件的)文件面试符。Linux下的网络编程就是套接字编程。2、socket类型:1.流式套接字(SOCK_STREAM):主要用于:TCP传输控制协议2.数据报套接字(SOCK_DGRAM):主要用于:UDP传输控制协议3.原始套接字(SOCK_RAW):较低层协议,如IP,ICMP直接访问3、IP地址:(表示主机)(1)、网络中标识主机。分为32位(IPV4)和128位(IPV6)(2)、以点分形式体现。“192.168.2.6”(3)、分类:以二进制的前8位进行区分A类:0 ~ 127.255.255.255B类:128 ~ 191.255.255.255C类:192 ~ 223.255.255.255D类:224 ~ 239.255.255.255E类:240 ~ 255.255.255.255网络号1字节主机号3字节A类网络号2字节主机号2字节B类网络号3字节主机号1字节C类(4)、将字符串1转换成网络字节序二进制intet_aton:将点分形式的字符串转换成 网络字节序二进制。intet_addr:将点分形式的字符串转换成 网络字节序二进制, 并得到转换后的地址。intet_ntoa:将网络字节序二进制转换成 点分形式的字符串。4、端口号:(区分进程)(1)、处理网络数据的进程号(2)、端口号:2字节(0~65532)5、字节序:主机字节序和网络字节序 字节存储方式分为两类:***小端序:(小端存储)低序字节存储在低地址。(pc机通用小端序)大端序:(大端存储)高序字节存储在低地址。(网络传输大端序)网络字节序属于大端。eg:0x112233 :高序字节为11,低序字节为33(1)、字节序转换:小端序转大端序(主机字节序转网络字节序)htons、htonl大端序转小端序(网络字节序转主机字节序)ntohs、ntohl6.、服务器:也称为下位机软件,目的是为客户端提供1数据交互等服务。 客户端:也称为上位机软件,目的是与用户进行直接交互。7、基于tcp的服务器和客户端编程。***查看本地ip地址:ifconfig(1)、服务器基本框架流程:1.创建套接字、<socket>(打开网卡设备文件,设置传输控制协议,得到文件描述符,也就是套接字)2.绑定套接字、<bind>(将本机ip地址、端口与套接字进行绑定,告诉接收数据发起者的ip和处理数据的进程号)3.启动监听、<listen> (启动监听,设置监听(检测是否有客户端连接),设置服务器同一时刻能接收的最大连接请求数)4.等待接收客户端连接、<accept>(阻塞程序,当有客户端发起连接请求,就从队列中按顺序接收客户端连接)5.数据交互、<read/write、recv/send>6.关闭套接字、<close>(2)、客户端流程框架1.创建套接字、<socket>(打开网卡设备文件,设置传输控制协议,得到文件描述符,也就是套接字)2.绑定套接字(可选)(//填写1 要连接服务器的 ip地址和端口号)3.建立连接(连接到服务器)、<connect> **(发起连接请求,这里需要三次握手)4.数据交互、<read/write、recv/send>5.关闭套接字、<close>

8、tcp服务器和客服端实现:

/*搭建tcp服务器端*/#include <stdio.h>#include <string.h>#include <stdlib.h>#include <strings.h>#include <sys/types.h>#include <sys/socket.h>#include <arpa/inet.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <stdlib.h>int main(int argc, const char *argv[]){int sockfd = 0;//监听套接字int connfd = 0;//连接套接字int ret = 0;/*1.创建套接字(tcp)*/sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建;买手机if(sockfd == -1)//监听套接字{perror("socket");return -1;}/*2.绑定套接字*/struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;//协议族seraddr.sin_port = htons(6666);//端口消息seraddr.sin_addr.s_addr = inet_addr("192.168.7.146");/*绑定地址消息(关联socket + 地址消息)*/ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));//绑定;插卡if(ret == -1){perror("bind");exit(-1);}elseprintf("bind success\n");/*3.启动监听、也就是启动服务器*/listen(sockfd, 32);//监听;开机while(1){printf("wait for the connection: ");/*4.等待接收客户端连接*/connfd = accept(sockfd, NULL, NULL);//等待;接听if(connfd == -1)//连接套接字{perror("accept");exit(-1);}printf("connect a client\n");char buf[512];/*5.数据交互*/while(1){memset(buf, 0, sizeof(buf));//清空bufret = recv(connfd, buf, sizeof(buf), 0);//交互;接收//buf[ret] = '\0';//清空bufprintf("recv: %d bytes, buf: %s\n", ret, buf);if(ret <= 0){printf("client quit\n");break;}}/*6.关闭套接字*/close(connfd);//关闭监听套接字;挂断}close(socket);//关闭连接套接字;挂断return 0;} /*搭建tcp客户端*/#include <stdio.h>#include <string.h>#include <stdlib.h>#include <strings.h>#include <sys/types.h>#include <sys/socket.h>#include <arpa/inet.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <stdlib.h>int main(int argc, const char **argv[]){int sockfd = 0;//监听套接字int connfd = 0;//连接套接字int ret = 0;/*1.创建套接字(tcp)*/sockfd = socket(AF_INET, SOCK_STREAM, 0);if(sockfd == -1)//监听套接字{perror("socket");return -1;}/*2.绑定套接字*/struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;//协议族seraddr.sin_port = htons(6666);//端口消息seraddr.sin_addr.s_addr = inet_addr("192.168.7.146");/*3.建立连接*/ret = connect(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));//连接if(ret == -1){perror("connect");exit(-1);}printf("connect to service\n");/*数据交换*/char buf[512];while(1){gets(buf);if(*buf == 'q'){printf("quit\n");break;}send(sockfd, buf, sizeof(buf), 0);//交换}/*关闭套接字*/close(sockfd);return 0;}

/输出功能:客户端连接服务器,客服端输出什么,服务器端返回什么,输入字符q退出/

9、测试工具:(1)、TcpUdpDebug(2)、telnet 命令telnet 192.168.7.199 7777注意:输入 ctrl + ] + q 退出

练习: 1.实现一个时间服务器,客户端发送time,服务器返回当前时间。

//搭建TCP服务器#include <stdio.h>#include <sys/types.h> /* See NOTES */#include <sys/socket.h>#include <arpa/inet.h>#include <netinet/in.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <time.h>//搭建TCP服务器int main(void){ int sockfd; //监听套接字int connfd; //连接套接字int ret;sockfd = socket(AF_INET,SOCK_STREAM,0); //创建套接字 (TCP) struct sockaddr_in seraddr; seraddr.sin_family = AF_INET; //协议族seraddr.sin_port = htons(2222);//端口 seraddr.sin_addr.s_addr = inet_addr("192.168.7.199");ret = bind(sockfd,(struct sockaddr *)&seraddr,sizeof(seraddr)); //绑定地址信息(关联sockfd + 地址信息)if(ret==-1){perror("bind");exit(-1);}printf("bind success\n");listen(sockfd,32); //监听 while(1){printf("wait for client...\n");connfd = accept(sockfd,NULL,NULL); //建立连接if(connfd==-1){perror("accept");exit(-1);}printf("connect a client\n");char buf[512];while(1){memset(buf,0,sizeof(buf));ret = recv(connfd,buf,sizeof(buf),0); //接收if(ret<=0){printf("client quit\n");break;}printf("recv:%d bytes,buf:%s\n",ret,buf);//解析客户端请求 根据请求给响应数据if(0==strcmp(buf,"get 1.txt")){#if 0char data[512];FILE *fp = fopen("1.txt","r");ret = fread(data,1,sizeof(data),fp);send(connfd,data,ret,0); #endif #if 1time_t val = time(NULL) ;char *ptr = ctime(&val);char data[512];strcpy(data,ptr);send(connfd,data,sizeof(data),0); #endif }}close(connfd); //关闭connfd}close(sockfd); //关闭sockfdreturn 0;} //搭建TCP客户端#include <stdio.h>#include <sys/types.h> /* See NOTES */#include <sys/socket.h>#include <arpa/inet.h>#include <netinet/in.h>#include <stdlib.h>#include <unistd.h>#include <string.h>//搭建TCP服务器int main(int argc,char **argv){ int sockfd; //监听套接字int connfd; //连接套接字int ret;sockfd = socket(AF_INET,SOCK_STREAM,0); //创建套接字 (TCP) struct sockaddr_in seraddr; seraddr.sin_family = AF_INET; //协议族seraddr.sin_port = htons(2222);//端口 seraddr.sin_addr.s_addr = inet_addr("192.168.7.199");ret = connect(sockfd,(struct sockaddr *)&seraddr,sizeof(seraddr)); //连接if(ret == -1){perror("connect");exit(-1);}printf("connect to service\n");char buf[512];char data[512];while(1){gets(buf);if(*buf=='Q'){printf("quit\n");break;}send(sockfd,buf,sizeof(buf),0);if(0==strcmp(buf,"time")){memset(data,0,sizeof(data));ret = recv(sockfd,data,sizeof(data),0);if(ret<=0){break;}printf("client_recv:%s\n",data);}}close(sockfd);return 0;} 10、扩展:0表示本机地址:seraddr.sin_addr.s_addr = inet_addr("0.0.0.0");seraddr.sin_addr.s_addr = inet_addr("0");设置地址重用:setsokopt绑定之前设置:int on = 1;setsokopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));记录对方地址信息:struct sockaddr cliaddr;int len = sizeof(cliaddr);connfd = accept(sockfd, (struct sockaddr *)&cliaddr, &len);//等待;接听printf("connect a client ip:%s,port:%u \n",inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port) );

11、tcp客户和服务器端架构:

//搭建TCP服务器#include <stdio.h>#include <sys/types.h> /* See NOTES */#include <sys/socket.h>#include <arpa/inet.h>#include <netinet/in.h>#include <stdlib.h>#include <unistd.h>#include <string.h>//搭建TCP服务器int main(void){ int sockfd; //监听套接字int connfd; //连接套接字int ret;sockfd = socket(AF_INET,SOCK_STREAM,0); //创建套接字 (TCP) struct sockaddr_in seraddr; //服务器地址信息 struct sockaddr_in cliaddr; //记录客户端地址seraddr.sin_family = AF_INET; //协议族seraddr.sin_port = htons(2222);//端口 seraddr.sin_addr.s_addr = inet_addr("0"); //0地址代表本机int on = 1;setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));/* 设置地址重用 */ret = bind(sockfd,(struct sockaddr *)&seraddr,sizeof(seraddr)); //绑定地址信息(关联sockfd + 地址信息)if(ret==-1){perror("bind");exit(-1);}printf("bind success\n");listen(sockfd,32); //监听int len = sizeof(cliaddr); while(1){printf("wait for client...\n");connfd = accept(sockfd,(struct sockaddr *)&cliaddr,&len); //建立连接if(connfd==-1){perror("accept");exit(-1);}printf("connect a client ip:%s,port:%u \n",inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port) );//解析出 客户端的 ip与端口char buf[512];while(1){memset(buf,0,sizeof(buf));ret = recv(connfd,buf,sizeof(buf),0); //接收if(ret<=0){printf("client quit\n");break;}printf("recv:%d bytes,buf:%s\n",ret,buf);//业务逻辑//解析客户端请求 根据请求给响应数据}close(connfd); //关闭connfd}close(sockfd); //关闭sockfdreturn 0;} //搭建TCP客户端#include <stdio.h>#include <sys/types.h> /* See NOTES */#include <sys/socket.h>#include <arpa/inet.h>#include <netinet/in.h>#include <stdlib.h>#include <unistd.h>#include <string.h>//搭建TCP服务器int main(int argc,char **argv){ int sockfd; //监听套接字int connfd; //连接套接字int ret;sockfd = socket(AF_INET,SOCK_STREAM,0); //创建套接字 (TCP) struct sockaddr_in seraddr; seraddr.sin_family = AF_INET; //协议族seraddr.sin_port = htons(5555);//端口 seraddr.sin_addr.s_addr = inet_addr("192.168.7.172");ret = connect(sockfd,(struct sockaddr *)&seraddr,sizeof(seraddr)); //连接if(ret == -1){perror("connect");exit(-1);}printf("connect to service\n");char buf[512];while(1){gets(buf);if(*buf=='Q'){printf("quit\n");break;}send(sockfd,buf,sizeof(buf),0);}close(sockfd);return 0;} 二、UDP 2.1、UDP编程 1、 搭建udp服务器socket-->bind-->recvfrom/sendto-->close2、搭建udp客户端socket-->bind(可选)-->recvfrom/sendto-->close3、相关工具编译:nc -u 192.168.7.199 7777 // 测试udp服务器nc -u 244.10.10.10 7777 // 发送组播消息 //搭建:udp服务器#include <stdio.h>#include <sys/types.h> /* See NOTES */#include <sys/socket.h>#include <arpa/inet.h>#include <netinet/in.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <time.h>int main(void){int sockfd;int ret;sockfd = socket(AF_INET,SOCK_DGRAM,0); //创建套接字(udp)struct sockaddr_in seraddr,cliaddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(7777);seraddr.sin_addr.s_addr = inet_addr("0"); //0地址代表本机int on=1;setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)); //设置地址重用ret = bind(sockfd,(struct sockaddr *)&seraddr,sizeof(seraddr)); //绑定地址信息( sockfd + addr )if(ret==-1){perror("bind");exit(-1);}char buf[512];int len = sizeof(cliaddr);while(1){memset(buf,0,sizeof(buf));ret = recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&cliaddr,&len); //接收数据if(ret==-1){break;}printf("recv:%d bytes,buf:%s\n",ret,buf);if(0==strcmp(buf,"time")){time_t val = time(NULL);char *ptr = ctime(&val);char data[512];strcpy(data,ptr);sendto(sockfd,data,sizeof(data),0,(struct sockaddr *)&cliaddr,len); }//sendto();}close(sockfd); //关闭return 0;} //搭建:udp客户端#include <stdio.h>#include <sys/types.h> /* See NOTES */#include <sys/socket.h>#include <arpa/inet.h>#include <netinet/in.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <time.h>//搭建:udp客户端int main(int argc,char **argv){if(argc!=2){printf("usage:%s + ip\n",argv[0]);exit(-1);}int sockfd;int ret;sockfd = socket(AF_INET,SOCK_DGRAM,0); //创建套接字(udp)struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(7777);seraddr.sin_addr.s_addr = inet_addr(argv[1]);char buf[512];while(1){gets(buf);if(*buf == 'Q'){printf("quit\n");break;}sendto(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&seraddr,sizeof(seraddr));}close(sockfd);return 0;} 2.2、广播与组播 1 、广播发送广播消息:1.向广播地址发送 //192.168.7.2552.开广播权限接收广播消息:1.绑定0地址 或 广播地址2.端口一致 //接收广播消息#include <stdio.h>#include <sys/types.h> /* See NOTES */#include <sys/socket.h>#include <arpa/inet.h>#include <netinet/in.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <time.h>//接收广播消息//1.绑定0地址 或 广播地址//2.端口一致int main(void){int sockfd;int ret;sockfd = socket(AF_INET,SOCK_DGRAM,0); //创建套接字(udp)struct sockaddr_in seraddr,cliaddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(7777);seraddr.sin_addr.s_addr = inet_addr("0"); //0地址代表本机int on=1;setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)); //设置地址重用ret = bind(sockfd,(struct sockaddr *)&seraddr,sizeof(seraddr)); //绑定地址信息( sockfd + addr )if(ret==-1){perror("bind");exit(-1);}char buf[512];int len = sizeof(cliaddr);while(1){memset(buf,0,sizeof(buf));ret = recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&cliaddr,&len); //接收数据if(ret==-1){break;}printf("recv:%d bytes,buf:%s\n",ret,buf);if(0==strcmp(buf,"time")){time_t val = time(NULL);char *ptr = ctime(&val);char data[512];strcpy(data,ptr);sendto(sockfd,data,sizeof(data),0,(struct sockaddr *)&cliaddr,len); }//sendto();}close(sockfd); //关闭return 0;} //发送广播消息#include <stdio.h>#include <sys/types.h> /* See NOTES */#include <sys/socket.h>#include <arpa/inet.h>#include <netinet/in.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <time.h>//发送广播消息//1.向广播地址发送 //2.开广播权限int main(int argc,char **argv){if(argc!=2){printf("usage:%s + ip\n",argv[0]);exit(-1);}int sockfd;int ret;sockfd = socket(AF_INET,SOCK_DGRAM,0); //创建套接字(udp)struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(7777);seraddr.sin_addr.s_addr = inet_addr(argv[1]);/* 打开广播权限 */int on = 1;setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&on,sizeof(on));char buf[512];while(1){gets(buf);if(*buf == 'Q'){printf("quit\n");break;}ret = sendto(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&seraddr,sizeof(seraddr));if(ret==-1){perror("sendto");exit(-1);}}close(sockfd);return 0;} 2、组播组播的地址范围224.0.0.0 到 239.255.255.255发送组播消息:1.向组播地址发送 //224.10.10.10接收组播消息:1.绑定0地址 或 组播地址2.加入多播组3.端口一致 //接收组播消息#include <stdio.h>#include <sys/types.h> /* See NOTES */#include <sys/socket.h>#include <arpa/inet.h>#include <netinet/in.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <time.h>//接收组播消息//1.绑定0地址 或 组播地址//2.加入多播组//3.端口一致int main(void){int sockfd;int ret;sockfd = socket(AF_INET,SOCK_DGRAM,0); //创建套接字(udp)struct sockaddr_in seraddr,cliaddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(7777);seraddr.sin_addr.s_addr = inet_addr("0"); //0地址代表本机//加入多播组 224.10.10.10#if 0struct ip_mreqn {struct in_addr imr_multiaddr; /* IP multicast groupaddress */struct in_addr imr_address; /* IP address of localinterface */int imr_ifindex; /* interface index */};#endifstruct ip_mreqn mreq;memset(&mreq,0,sizeof(mreq));mreq.imr_multiaddr.s_addr = inet_addr("224.10.10.10"); //组播地址mreq.imr_address.s_addr = inet_addr("0"); //本机地址setsockopt(sockfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq));int on=1;setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)); //设置地址重用ret = bind(sockfd,(struct sockaddr *)&seraddr,sizeof(seraddr)); //绑定地址信息( sockfd + addr )if(ret==-1){perror("bind");exit(-1);}char buf[512];int len = sizeof(cliaddr);while(1){memset(buf,0,sizeof(buf));ret = recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&cliaddr,&len); //接收数据if(ret==-1){break;}printf("recv:%d bytes,buf:%s\n",ret,buf);if(0==strcmp(buf,"time")){time_t val = time(NULL);char *ptr = ctime(&val);char data[512];strcpy(data,ptr);sendto(sockfd,data,sizeof(data),0,(struct sockaddr *)&cliaddr,len); }//sendto();}close(sockfd); //关闭return 0;} //发送组播消息#include <stdio.h>#include <sys/types.h> /* See NOTES */#include <sys/socket.h>#include <arpa/inet.h>#include <netinet/in.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <time.h>//发送组播消息//1.向组播地址发送 //224.10.10.10int main(int argc,char **argv){if(argc!=2){printf("usage:%s + ip\n",argv[0]);exit(-1);}int sockfd;int ret;sockfd = socket(AF_INET,SOCK_DGRAM,0); //创建套接字(udp)struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(7777);seraddr.sin_addr.s_addr = inet_addr(argv[1]);char buf[512];while(1){gets(buf);if(*buf == 'Q'){printf("quit\n");break;}ret = sendto(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&seraddr,sizeof(seraddr));if(ret==-1){perror("sendto");exit(-1);}}close(sockfd);return 0;}

练习: 1.完成网络实验二 2.熟悉udp编程,组播,广播 3.复习进程,线程

2.3、setsockopt 的基本用法 /*********************** setsockopt 的基本用法****************//* 设置地址重用 防止异常退出 */int on = 1;setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));/* 打开广播权限 创建广播就下面两句 */int on = 1;setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&on,sizeof(on));/* 加入多播组 创建多播就下面五句 */struct ip_mreqn mreq;memset(&mreq,0,sizeof(mreq));mreq.imr_multiaddr.s_addr = inet_addr("224.10.10.10"); //组播地址mreq.imr_address.s_addr = inet_addr("0"); //本机地址 setsockopt(sockfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq));//退出组播/*********************** setsockopt 的基本用法****************/ 2.4、广播与组播 1、广播与组播只能用udp协议.tcp与udptcp:面向连接(三次握手),保证数据可靠,完整性udp:无连接(没有三次握手)2、广播广播地址:在局域网内部,都是主机号全为1的形式例如: 192.168.7.122. 对应的广播地址: 192.168.7.255广播是一对多的服务,从广播服务器发送的数据,所有该网段的主机都能收到.发送端:{创建套接字. ---socket() //SOCK_DGRAM绑定地址信息. bind(). struct sockaddr_in;设置广播地址信息. struct sockaddr_in.开启广播服务. setsockopt()发送广播数据sendto().}接收端:{创建套接字. ---socket() //SOCK_DGRAM绑定地址信息. bind(). struct sockaddr_in; (绑定的端口要和发送端指定的发送端口一致才能接收解析数据)(绑定的目的是为了显示自己的端口以及ip信息)接受广播数据.}3、组播(多播)由于广播默认发给所有局域网内的主机,因此容易造成网络风暴.解决该问题可以用组播: 只给加入到组播组内的成员发送消息.D类ip地址通常被用于组播地址:224.0.0.1 ~ 239.255.255.255但是有的是被预留了,有的是用于互联网,所以这些不能直接使用.用户可用: 224.0.2.0 ~ 238.255.255.255.(临时使用,全网有效) 239.0.0.0 ~ 239.255.255.255.(本地常用,局域网)组播发送端{创建套接字. ---socket() //SOCK_DGRAM绑定地址信息. bind(). struct sockaddr_in;设置组播地址信息. struct sockaddr_in.开启组播服务. setsockopt() //允许开启组播服务:IP_MULTICAST_IF发送广播数据sendto()}组播接收端{创建套接字. ---socket() //SOCK_DGRAM绑定地址信息. bind(). struct sockaddr_in;加入组播组当中. setsockopt(). //IP_ADD_MEMBERSHIP 接收数据.}            SOL_SOCKET ----------------------------------------------------- 参数optname 宏的作用 对应参数optaval的类型 SO_BROADCAST     允许发送广播数据    intSO_DEBUG        允许调试       int SO_DONTROUTE     不查找路由       int SO_ERROR        获得套接字错误    intSO_KEEPALIVE      保持连接       int SO_LINGER        延迟关闭连接    struct linger SO_OOBINLINE      带外数据放入正常数据流  int SO_RCVBUF       接收缓冲区大小       intSO_SNDBUF        发送缓冲区大小      intSO_RCVLOWAT      接收缓冲区下限       intSO_SNDLOWAT      发送缓冲区下限       int SO_RCVTIMEO      接收超时        struct timevalSO_SNDTIMEO      发送超时      struct timeval SO_REUSEADDR    允许重用本地地址和端口    int SO_TYPE         获得套接字类型     intSO_BSDCOMPAT     与BSD系统兼容       int======================================================IPPROTO_IP------------------------------------------------------IP_ADD_MEMBERSHIP 加入到组播组中 struct ip_mreq IP_MULTICAST_IF 允许开启组播报文的接口 struct ip_mreq1. select(): 针对所有文件描述符表中的io操作, 检测一定时间内是否有任何相关的io操作2.setsockopt(listenfd): 检测监听套接字,在一定时间内是否有客户端连接3.setsockopt(connfd): 检测通信套接字,在一定时间内是否有客户端发送消息 三、并发服务器 3.1、循环服务器 1、循环服务器:同一时间,只能处理一个客户,通过轮询来处理多个客户的请求2、并发服务器:同一时间,能够处理多个客户 3.2、并发服务器 1、多进程并发服务器基本原理: 每连接一个客户端,创建一个子进程,子进程负责处理connfd(客户请求)父进程处理sockfd(连接请求)。//注意:子进程结束时,需要进行资源回收 /*多进程并发服务器*/#include <stdio.h>#include <sys/types.h> /* See NOTES */#include <sys/socket.h>#include <arpa/inet.h>#include <netinet/in.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <sys/types.h>#include <sys/wait.h>#include <errno.h>//多进程并发服务器int tcpser_init(const char *ip,int port);int do_client(int connfd);void signal_handler(int sig);int main(void){ int connfd; //连接套接字int sockfd; //监听套接字struct sockaddr_in cliaddr; //记录客户端地址int len = sizeof(cliaddr);sockfd = tcpser_init("0",6666); //调用初始化函数 signal(SIGCHLD,signal_handler); //1.注册SIGCHIL信号,设定好信号处理函数 while(1){printf("wait for client...\n");connfd = accept(sockfd,(struct sockaddr *)&cliaddr,&len); //建立连接if(connfd==-1){if(errno == EINTR){//错误码判断,如果是接继续执行不退出continue;}else{perror("accept");exit(-1);}}printf("connect a client ip:%s,port:%u \n",inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port) );//每连接一个客户,创建一个子进程pid_t pid = fork();if(pid==0){close(sockfd); //关闭监听 do_client(connfd); //子进程去处(connfd)理客户请求exit(0); }else{close(connfd);//关闭连接 continue; //父进程去处(sockfd)理客户连接 }}return 0;}/*tcp初始化函数*/int tcpser_init(const char *ip,int port){int sockfd; //监听套接字int connfd; //连接套接字int ret;sockfd = socket(AF_INET,SOCK_STREAM,0); //创建套接字 (TCP) struct sockaddr_in seraddr; //服务器地址信息 seraddr.sin_family = AF_INET; //协议族seraddr.sin_port = htons(port);//端口 seraddr.sin_addr.s_addr = inet_addr(ip); //0地址代表本机int on = 1;setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));/* 设置地址重用 */ret = bind(sockfd,(struct sockaddr *)&seraddr,sizeof(seraddr)); //绑定地址信息(关联sockfd + 地址信息)if(ret==-1){perror("bind");exit(-1);}printf("bind success\n");listen(sockfd,32); //监听return sockfd;}/*用户处理函数*/int do_client(int connfd){//处理客户的 请求与响应char buf[512];int ret;while(1){memset(buf,0,sizeof(buf));ret = recv(connfd,buf,sizeof(buf),0); //接收if(ret<=0){printf("client quit\n");break;}printf("recv:%d bytes,buf:%s\n",ret,buf);//业务逻辑}}/*信号处理函数*/void signal_handler(int sig){while(waitpid(-1,NULL,WNOHANG)>0);//当产生晓得僵尸进程时,需要while清理} 2、多线性并发服务器基本原理: 每连接一个客户端,创建一个子线程,子线程负责处理connfd(客户请求)主线程处理sockfd(连接请求)。//注意:子线程结束时,需要进行资源回收 /*多线程并发服务器*/#include <stdio.h>#include <sys/types.h> /* See NOTES */#include <sys/socket.h>#include <arpa/inet.h>#include <netinet/in.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <sys/types.h>#include <sys/wait.h>#include <errno.h>#include <pthread.h>//多线程并发服务器int tcpser_init(const char *ip,int port);void * do_client(void *arg);int main(void){ int connfd; //连接套接字int sockfd; //监听套接字struct sockaddr_in cliaddr; //记录客户端地址int len = sizeof(cliaddr);sockfd = tcpser_init("0",6666); while(1){printf("wait for client...\n");connfd = accept(sockfd,(struct sockaddr *)&cliaddr,&len); //建立连接if(connfd==-1){perror("accept");exit(-1);}printf("connect id:%d a client ip:%s,port:%u \n",connfd,inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port) );//每连接一个客户,创建一个子线程pthread_t tid;pthread_create(&tid, NULL, do_client, (void *)connfd);}return 0;}int tcpser_init(const char *ip,int port){int sockfd; //监听套接字int connfd; //连接套接字int ret;sockfd = socket(AF_INET,SOCK_STREAM,0); //创建套接字 (TCP) struct sockaddr_in seraddr; //服务器地址信息 seraddr.sin_family = AF_INET; //协议族seraddr.sin_port = htons(port);//端口 seraddr.sin_addr.s_addr = inet_addr(ip); //0地址代表本机int on = 1;setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));/* 设置地址重用 */ret = bind(sockfd,(struct sockaddr *)&seraddr,sizeof(seraddr)); //绑定地址信息(关联sockfd + 地址信息)if(ret==-1){perror("bind");exit(-1);}printf("bind success\n");listen(sockfd,32); //监听return sockfd;}void * do_client(void *arg){pthread_detach( pthread_self() ); //子线程 资源回收int connfd = (int)arg;//处理客户的 请求与响应char buf[512];int ret;while(1){memset(buf,0,sizeof(buf));ret = recv(connfd,buf,sizeof(buf),0); //接收if(ret<=0){printf("client quit id:%d\n",connfd);close(connfd);pthread_exit(NULL);}printf("recv:%d bytes,buf:%s\n",ret,buf);//业务逻辑}} 3.3、IO多路复用机制(select、poll、epoll) 基本原理:先构造一张有关描述符的表,然后调用一个函数。当这些文件描述符中的一个或多个已准备好进行I/O时函数才返回。函数返回时告诉进程那个描述符已就绪,可以进行I/O操作。//相关博客//https://wwwblogs/shengguorui/p/11949282.html///p/397449cadc9aselect函数int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);/********************************************************************************* * Description: select机制* Input: nfds: 最大文件描述符值+1 readfds: 读事件 writefds: 写事件 exceptfds:异常事件 timeout:超时值 * Return: 成功返回 准备好的文件描述符个数 0:超时返回* Others: -1:出错**********************************************************************************/void FD_CLR(int fd, fd_set *set); //删除 (清除)int FD_ISSET(int fd, fd_set *set); //是否准备好void FD_SET(int fd, fd_set *set); //添加 void FD_ZERO(fd_set *set); //清空 /*通过io多路复用: 实现了 "同时" 读3根管道*/#include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>#include <string.h>//通过io多路复用: 实现了 "同时" 读3根管道 int main(void){char buf[128];int fd1 = open("f1",O_RDWR);int fd2 = open("f2",O_RDWR);int fd3 = open("f3",O_RDWR);//1.准备一张表(读事件)fd_set readfds;fd_set tmpfds;//2.把文件描述符添加到 表中FD_ZERO(&readfds); //清空表FD_SET(fd1,&readfds); //添加fd1FD_SET(fd2,&readfds); //添加fd2 FD_SET(fd3,&readfds); //添加fd3tmpfds = readfds;int maxfd = fd3;int ret;int i;while(1){readfds = tmpfds ; //修正readfdsret = select(maxfd+1,&readfds,NULL,NULL,NULL); //阻塞 轮询检测//循环 找到准备好的管道for(i=fd1; i<maxfd+1;i++){if(FD_ISSET(i,&readfds)){ //读管道数据memset(buf,0,sizeof(buf));read(i,buf,sizeof(buf));printf("%s\n",buf);}}}return 0;}

练习: 1.熟练掌握并发服务器 2.熟悉select函数的基本使用

四、IO多路复用 IO多路复用优点:节约资源缺点:代码麻烦,不是并发,而是轮询 4.1、Linux下主要有4种i/o模型 1.1、阻塞I/O:最常用、最简单、效率低1.2、非阻塞I/O:可防止非阻塞在i/o操作上,需要轮询1.3、I/O多路复用:允许同时对多个I/O进行控制1.4、信号驱动I/O:一种同步IO,一般通过注册,回调实现1.5.异步IO:真正的异步IO///link?url=EnsmiY8o3v_xZXyFR_APQOqxRHCZrqGY9Zkm4W0uJ_ItTFFy_GuagkPxgBlaBKQXLP79QumrEVGYe-rD2g5I7qK-iOYgGLelWKyM2n5Lot_&wd=&eqid=ec1bce8b000017230000000660efae81 4.2、IO多路复用机制(select、poll、epoll) 基本原理:先构造一张有关描述符的表,然后调用一个函数。当这些文件描述符中的一个或多个已准备好进行I/O时函数才返回。函数返回时告诉进程那个描述符已就绪,可以进行I/O操作。//https://wwwblogs/Anker/p/3265058.html

1、采用select实现 IO多路复用服务器

/*采用select实现 IO多路复用服务器*/#include <stdio.h>#include <sys/types.h> /* See NOTES */#include <sys/socket.h>#include <arpa/inet.h>#include <netinet/in.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <sys/types.h>#include <sys/wait.h>#include <errno.h>#include <sys/time.h>#include <sys/types.h>#include <unistd.h>//采用select实现 IO多路复用服务器int tcpser_init(const char *ip,int port);int do_client(int connfd);int main(void){ int connfd; //连接套接字int sockfd; //监听套接字char buf[256];struct sockaddr_in cliaddr; //记录客户端地址int len = sizeof(cliaddr);int ret;sockfd = tcpser_init("0",6666); /*准备一张表*/fd_set readfds,tmpfds;/*向表中 添加文件描述符 */FD_ZERO(&readfds);FD_SET(sockfd,&readfds);tmpfds = readfds;int maxfd = sockfd;int i;//select 轮询检测while(1){readfds = tmpfds; //修正readfdsret = select(maxfd+1,&readfds,NULL,NULL,NULL);if(ret==-1){perror("select");exit(-1);}for(i=sockfd;i<maxfd+1;i++){if(FD_ISSET(i,&readfds)){if(i==sockfd){/* 建立连接 (sockfd准备好了) */connfd = accept(sockfd,NULL,NULL); printf("connect a client :%d\n",connfd);/* 把connfd 添加到表中 修正maxfd */FD_SET(connfd,&tmpfds);if(maxfd<connfd){maxfd = connfd; //修正maxfd }}else{printf("connfd:%d 准备好了\n",i);/* 处理客户请求 (connfd准备好了)*/memset(buf,0,sizeof(buf));ret = recv(i,buf,sizeof(buf),0);if(ret<=0){printf("client quit %d\n",i);close(i);FD_CLR(i,&tmpfds);}else{printf("recv:%s\n",buf);}}}}}return 0;}#if 0while(1){printf("wait for client...\n");connfd = accept(sockfd,(struct sockaddr *)&cliaddr,&len); //建立连接if(connfd==-1){perror("accept");exit(-1);}printf("connect a client id:%d ip:%s,port:%u \n",connfd,inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port) );#endif int tcpser_init(const char *ip,int port){int sockfd; //监听套接字int connfd; //连接套接字int ret;sockfd = socket(AF_INET,SOCK_STREAM,0); //创建套接字 (TCP) struct sockaddr_in seraddr; //服务器地址信息 seraddr.sin_family = AF_INET; //协议族seraddr.sin_port = htons(port);//端口 seraddr.sin_addr.s_addr = inet_addr(ip); //0地址代表本机int on = 1;setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));/* 设置地址重用 */ret = bind(sockfd,(struct sockaddr *)&seraddr,sizeof(seraddr)); //绑定地址信息(关联sockfd + 地址信息)if(ret==-1){perror("bind");exit(-1);}printf("bind success\n");listen(sockfd,32); //监听return sockfd;}}

2、通过io多路复用: 实现了 “同时” 读3根管道 采用poll机制

/*通过io多路复用: 实现了 "同时" 读3根管道 采用poll机制*/#include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>#include <string.h>#include <poll.h>//通过io多路复用: 实现了 "同时" 读3根管道 采用poll机制int main(void){char buf[128];int fd1 = open("f1",O_RDWR);int fd2 = open("f2",O_RDWR);int fd3 = open("f3",O_RDWR);//1.准备一张表 (结构体)struct pollfd events[3]; //2.把文件描述符添加到 表中memset(events,0,sizeof(events)); //清空表events[0].fd = fd1; //添加fd1events[0].events = POLLIN; //读事件events[1].fd = fd2; //添加fd2events[1].events = POLLIN; //读事件events[2].fd = fd3; //添加fd3events[2].events = POLLIN; //读事件int ret;int i;while(1){ ret = poll(events,3,-1);for(i=0;i<3;i++){if(events[i].revents & POLLIN ){ int fd = events[i].fd;printf("%d 准备好了\n",fd);memset(buf,0,sizeof(buf));read(fd,buf,sizeof(buf));printf("%s\n",buf);}}}return 0;} 4.3、超时检测。 方法1:设置套接字属性、设置超时值。 /*方法1:设置套接字属性*/#include <stdio.h>#include <sys/types.h> /* See NOTES */#include <sys/socket.h>#include <arpa/inet.h>#include <netinet/in.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <errno.h>//超时检测//方法1:设置套接字属性int main(void){ int sockfd; //监听套接字int connfd; //连接套接字int ret;sockfd = socket(AF_INET,SOCK_STREAM,0); //创建套接字 (TCP) struct sockaddr_in seraddr; //服务器地址信息 struct sockaddr_in cliaddr; //记录客户端地址seraddr.sin_family = AF_INET; //协议族seraddr.sin_port = htons(6666);//端口 seraddr.sin_addr.s_addr = inet_addr("0"); //0地址代表本机int on = 1;setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));/* 设置地址重用 */ret = bind(sockfd,(struct sockaddr *)&seraddr,sizeof(seraddr)); //绑定地址信息(关联sockfd + 地址信息)if(ret==-1){perror("bind");exit(-1);}printf("bind success\n");listen(sockfd,50); //监听int len = sizeof(cliaddr); while(1){printf("wait for client...\n");connfd = accept(sockfd,(struct sockaddr *)&cliaddr,&len); //建立连接if(connfd==-1){perror("accept");exit(-1);}printf("connect a client ip:%s,port:%u \n",inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port) );//解析出 客户端的 ip与端口struct timeval tv={5,0}; //5s//****setsockopt(connfd,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv));//****//设置接收超时char buf[512];while(1){memset(buf,0,sizeof(buf));ret = recv(connfd,buf,sizeof(buf),0); //接收if(ret<=0){if((errno== EAGAIN )|| (errno == EWOULDBLOCK)){//****printf("超时返回\n");//****//continue;//****break;//****}printf( "client quit\n");break;}printf("recv:%d bytes,buf:%s\n",ret,buf);//业务逻辑}close(connfd); //关闭connfd}close(sockfd); //关闭sockfdreturn 0;} 方法2:设置闹钟 /*方法2:设置闹钟*/#include <stdio.h>#include <sys/types.h> /* See NOTES */#include <sys/socket.h>#include <arpa/inet.h>#include <netinet/in.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <errno.h>#include <signal.h>//超时检测//方法2:设置闹钟void alarm_handler(int sig)//****{printf("time out\n");alarm(5);}int main(void){ int sockfd; //监听套接字int connfd; //连接套接字int ret;signal(SIGALRM,alarm_handler);//****sockfd = socket(AF_INET,SOCK_STREAM,0); //创建套接字 (TCP) struct sockaddr_in seraddr; //服务器地址信息 struct sockaddr_in cliaddr; //记录客户端地址seraddr.sin_family = AF_INET; //协议族seraddr.sin_port = htons(6666);//端口 seraddr.sin_addr.s_addr = inet_addr("0"); //0地址代表本机int on = 1;setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));/* 设置地址重用 */ret = bind(sockfd,(struct sockaddr *)&seraddr,sizeof(seraddr)); //绑定地址信息(关联sockfd + 地址信息)if(ret==-1){perror("bind");exit(-1);}printf("bind success\n");listen(sockfd,50); //监听int len = sizeof(cliaddr); while(1){printf("wait for client...\n");connfd = accept(sockfd,(struct sockaddr *)&cliaddr,&len); //建立连接if(connfd==-1){perror("accept");exit(-1);}printf("connect a client ip:%s,port:%u \n",inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port) );//解析出 客户端的 ip与端口char buf[512];while(1){alarm(5);//****memset(buf,0,sizeof(buf));ret = recv(connfd,buf,sizeof(buf),0); //接收if(ret<=0){printf( "client quit\n");break;}printf("recv:%d bytes,buf:%s\n",ret,buf);//业务逻辑 }close(connfd); //关闭connfd}close(sockfd); //关闭sockfdreturn 0;} 方法3:在select函数 或poll函数中设置超时值。 /*方法3:在select函数中 设置超时值*/#include <stdio.h>#include <sys/types.h> /* See NOTES */#include <sys/socket.h>#include <arpa/inet.h>#include <netinet/in.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <sys/types.h>#include <sys/wait.h>#include <errno.h>#include <sys/time.h>#include <sys/types.h>#include <unistd.h>//超时检测//方法3:在select函数中 设置超时值int tcpser_init(const char *ip,int port);int main(void){ int connfd; //连接套接字int sockfd; //监听套接字char buf[256];struct sockaddr_in cliaddr; //记录客户端地址int len = sizeof(cliaddr);int ret;sockfd = tcpser_init("0",6666); /*准备一张表*/fd_set readfds,tmpfds;/*向表中 添加文件描述符 */FD_ZERO(&readfds);FD_SET(sockfd,&readfds);tmpfds = readfds;int maxfd = sockfd;int i;//select 轮询检测while(1){struct timeval tv = {5,0};readfds = tmpfds; //修正readfdsret = select(maxfd+1,&readfds,NULL,NULL,&tv);//****if(ret==0){printf("time out\n");continue;}else if(ret==-1){perror("select");exit(-1);}for(i=sockfd;i<maxfd+1;i++){if(FD_ISSET(i,&readfds)){ if(i==sockfd){/* 建立连接 (sockfd准备好了) */connfd = accept(sockfd,NULL,NULL); // 从监听队列中取出一个连接printf("connect a client :%d\n",connfd);/* 把connfd 添加到表中 修正maxfd */FD_SET(connfd,&tmpfds);if(maxfd<connfd){maxfd = connfd; //修正maxfd }}else{printf("connfd:%d 准备好了\n",i);/* 处理客户请求 (connfd准备好了)*/memset(buf,0,sizeof(buf));ret = recv(i,buf,sizeof(buf),0);if(ret<=0){printf("client quit %d\n",i);close(i);FD_CLR(i,&tmpfds);}else{printf("recv:%s\n",buf);} } }}}return 0;}int tcpser_init(const char *ip,int port){int sockfd; //监听套接字int connfd; //连接套接字int ret;sockfd = socket(AF_INET,SOCK_STREAM,0); //创建套接字 (TCP) struct sockaddr_in seraddr; //服务器地址信息 seraddr.sin_family = AF_INET; //协议族seraddr.sin_port = htons(port);//端口 seraddr.sin_addr.s_addr = inet_addr(ip); //0地址代表本机int on = 1;setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));/* 设置地址重用 */ret = bind(sockfd,(struct sockaddr *)&seraddr,sizeof(seraddr)); //绑定地址信息(关联sockfd + 地址信息)if(ret==-1){perror("bind");exit(-1);}printf("bind success\n");listen(sockfd,32); //监听return sockfd;} 方法2和3只是模拟方法1效果,三者不能等同

练习: 熟悉网络超时检测与io多路复用机制

五、数据库操作 7/16 11:00结构体3个变量、成员、密码、请求响应文件拷贝gcc sqlite3 -lsqlite37/16 15:16创建插入数据库表7/17 9:20查看本地ip地址:ifconfig 5.1、数据库操作 1、 数据简介SQLite的源代码是C,其源代码完全开放。SQLite第一个Alpha版本诞生于2000年5月。他是一个轻量级的嵌入式数据库。2、 sqlite3基本命令 //命令以.开头 sqlite3 my.db //打开my.db 不存在则创建.help.database .tables.schema tablename //查看表的结构.quit//退出.exit //退出3 、sqlite3基本语句(SQL语句)//sql语句以;结束//注意常见的数据类型:int, float,text(char)create table usr (name text, passwd text); //创建表create table if not exists stuinfo(id int,name text); //不存在则创建表create table usr (name text primary key, passwd text); //创建表 以name作为主键insert into usr values ("jack","ps666"); //向表中插入数据select * from usr; //查询表中数据 select * from usr where name="rose" and passwd="ps777"; //按规则查找delete from usr where name="rose"; //删除指定记录项update usr set passwd="abc123xyz" where name="lilei"; //更新表drop table usr; //删除表4 、sqlite3接口函数//注意编译时 指定库文件 -lsqlite3sqlite3_open();//打开sqlite3_close();//关闭sqlite3_exec();//执行sql语句sqlite3_get_table();//查询 /* 1.sqlite3 */#include <stdio.h>#include <sqlite3.h>#include <stdlib.h>int main(void){sqlite3 *db;int ret;ret = sqlite3_open("my.db",&db); //打开数据库if(ret!=SQLITE_OK){printf("open my.db failed\n");exit(-1);}printf("open my.db success\n");#if 1//在数据库中 创建表char sql[256]="create table if not exists stuinfo(id int,name text);";char *errmsg;ret = sqlite3_exec(db,sql,NULL,NULL,&errmsg);if(ret!=SQLITE_OK){printf("exec failed:%s\n",errmsg);exit(-1);}#endif #if 0//向数据库 插入一条记录printf("输入id与名字\n");int id;char name[32];scanf("%d %s",&id,name);//拼凑sql语句char sql[256];snprintf(sql,sizeof(sql),"insert into stuinfo values('%d','%s');",id,name);char *errmsg;ret = sqlite3_exec(db,sql,NULL,NULL,&errmsg);if(ret!=SQLITE_OK){printf("exec failed:%s\n",errmsg);exit(-1);}#endif sqlite3_close(db); //关闭数据return 0;} /* 2.sqlite3 */#include <stdio.h>#include <sqlite3.h>#include <stdlib.h>//通过回调实现查询int call_back(void *para, int f_num, char **f_value, char **f_name){//f_num : 字段数目 //f_value : 结果//f_name : 字段名int i;for(i=0;i<f_num;i++){printf("%s ",f_value[i]);}printf("\n");return 0;}int main(void){sqlite3 *db;int ret;ret = sqlite3_open("my.db",&db); //打开数据库if(ret!=SQLITE_OK){printf("open my.db failed\n");exit(-1);}printf("open my.db success\n");#if 1//在数据库中 创建表char sql[256]="select * from stuinfo;";char *errmsg;ret = sqlite3_exec(db,sql,call_back,NULL,&errmsg);if(ret!=SQLITE_OK){printf("exec failed:%s\n",errmsg);exit(-1);}#endif sqlite3_close(db); //关闭数据return 0;} /* 3.sqlite3 */#include <stdio.h>#include <sqlite3.h>#include <stdlib.h>//通过get_table实现查询int main(void){sqlite3 *db;int ret;ret = sqlite3_open("my.db",&db); //打开数据库if(ret!=SQLITE_OK){printf("open my.db failed\n");exit(-1);}printf("open my.db success\n");#if 1//在数据库中 创建表//char sql[256]="select * from stuinfo where id=2 and name='lily';";char sql[256]="select * from stuinfo;";char *errmsg;char **resultp;//结果 int nrow; //行数 int ncolumn; //列数ret = sqlite3_get_table(db, sql,&resultp, &nrow, &ncolumn, &errmsg);if(ret!=SQLITE_OK){printf("sqlite3_get_table failed:%s\n",errmsg);exit(-1);}printf("nrow:%d,ncolumn:%d\n",nrow,ncolumn);int i;int index=2;//循环遍历 查看结果for(i=0;i<nrow;i++){printf("%s : %s \n",resultp[index],resultp[index+1]);index = index+2;}#endif sqlite3_close(db); //关闭数据return 0;}

练习: 1.掌握常用的sql语句 2.熟悉sqlite3编程接口

六、 unix域套接字编程 区分流式与数据报式 6.1、unix域套接字编程(本地进程间通信) 1、UNIX域(流式)套接字服务器端:socket-->bind-->listen-->accept-->recv/send-->close;UNIX域(流式)套接字客户端:socket-->bind(可选)-->connect-->send/recv-->close;2、UNIX域(数据报)套接字服务器端:socket-->bind-->recvfrom/sendto-->closeUNIX域(数据报)套接字客户端:socket-->bind(可选)-->recvfrom/sendto-->close注意地址信息:struct sockaddr_un // <sys/un.h>{sa_family_t sun_family; //协议族 char sun_path[108]; //套接字文件的路径}; /*搭建 unix域 流式 服务器*/#include <stdio.h>#include <sys/types.h> /* See NOTES */#include <sys/socket.h>#include <arpa/inet.h>#include <netinet/in.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <sys/un.h>//搭建 unix域 流式 服务器int main(void){ int sockfd; //监听套接字int connfd; //连接套接字int ret;sockfd = socket(AF_UNIX,SOCK_STREAM,0); //创建套接字 (本地) struct sockaddr_un seraddr; //服务器地址信息 remove("socket");seraddr.sun_family = AF_UNIX; //协议族 本地通信strcpy(seraddr.sun_path,"./socket"); //地址 本地文件ret = bind(sockfd,(struct sockaddr *)&seraddr,sizeof(seraddr)); //绑定地址信息(关联sockfd + 地址信息)if(ret==-1){perror("bind");exit(-1);}printf("bind success\n");listen(sockfd,50); //监听while(1){printf("wait for client...\n");connfd = accept(sockfd,NULL,NULL); //建立连接if(connfd==-1){perror("accept");exit(-1);}printf("connect a client\n");char buf[512];while(1){memset(buf,0,sizeof(buf));ret = recv(connfd,buf,sizeof(buf),0); //接收if(ret<=0){printf("client quit\n");break;}printf("recv:%d bytes,buf:%s\n",ret,buf);}close(connfd); //关闭connfd}close(sockfd); //关闭sockfdreturn 0;} /*搭建 unix 流式套接字 客户端*/#include <stdio.h>#include <sys/types.h> /* See NOTES */#include <sys/socket.h>#include <arpa/inet.h>#include <netinet/in.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <sys/un.h>//搭建 unix 流式套接字 客户端int main(int argc,char **argv){ int sockfd; //监听套接字int connfd; //连接套接字int ret;sockfd = socket(AF_UNIX,SOCK_STREAM,0); //创建套接字 (本地) struct sockaddr_un seraddr;seraddr.sun_family = AF_UNIX; //协议族 本地 strcpy(seraddr.sun_path,"./socket"); //地址 本地文件ret = connect(sockfd,(struct sockaddr *)&seraddr,sizeof(seraddr)); //连接if(ret == -1){perror("connect");exit(-1);}printf("connect to service\n");char buf[512];while(1){gets(buf);if(*buf=='Q'){printf("quit\n");break;}send(sockfd,buf,sizeof(buf),0);}close(sockfd);return 0;} 6.2、抓包分析

抓包分析链接

七、 项目实训 1、项目题目:聊天软件 项目要求:1.登录注册功能2.获取在线用户3.私聊4.群聊5.发送离线消息6.记录聊天数据涉及知识:1.Tcp编程或UDP编程2.Udp广播与组播3.多进程多线程4.数据库5.数据结构6.进程间通信 2、项目题目:网络词典 项目要求:1.登录注册功能2.单词查询3.历史记录4.支持多客户端连接涉及知识:1.Tcp编程2.多进程多线程3.数据库4.文件操作 3、项目题目:文件服务器(云盘系统) 项目要求:1.登录注册功能2.获取服务器上文件列表3.上传文件与下载文件4.支持多用户同时登录5.记录用户上传下载信息6.支持多客户端连接涉及知识:1.TCP编程2.文件操作3.多进程多线程4.数据库 4、项目题目:在线点餐系统 项目要求:1.用户端实现菜品浏览、点餐、买单、查询等功能2.管理端实现查看点餐信息、修改、添加新菜品等功能涉及知识:1.TCP或UDP编程2.数据库3.多进程多线程 网络编程-调试工具及LinuxC函数手册!

网络编程-调试工具及LinuxC函数手册! 链接:/s/1jXWeGyPuFlV3WW5p3wezcg 提取码:t9m4

跳转:上一篇,IO进线程编程!

跳转:上一篇,IO进线程编程!

跳转:下一篇,C++编程总结!

跳转:下一篇,C++ 编程!

跳转:开头

网络编程

发布评论

评论列表(0)

  1. 暂无评论