黃石建設(shè)信息網(wǎng)站國(guó)內(nèi)網(wǎng)絡(luò)銷(xiāo)售平臺(tái)有哪些
目錄
1.學(xué)習(xí)網(wǎng)絡(luò)編程前的一些基礎(chǔ)知識(shí)
2.UDP(user datagram protocol)協(xié)議的特點(diǎn)
3.使用有UPD編寫(xiě)套接字
4.使用TCP編寫(xiě)套接字
4.2.TCP客服端?
4.3.TCP服務(wù)器端
4.4.單進(jìn)程版本(沒(méi)有人會(huì)使用)
4.5.多進(jìn)程版本
4.6.多線程版本
5.把套接字封裝
1.學(xué)習(xí)網(wǎng)絡(luò)編程前的一些基礎(chǔ)知識(shí)
1.1.IP地址
- IP地址是在IP協(xié)議中, 用來(lái)標(biāo)識(shí)網(wǎng)絡(luò)中不同主機(jī)的地址(一個(gè)IP地址標(biāo)識(shí)一臺(tái)主機(jī));
- 通常使用 "點(diǎn)分十進(jìn)制" 的字符串表示IP地址, 例如127.0.0.1 ; 用點(diǎn)分割的每一個(gè)數(shù)字表示一個(gè)字節(jié), 范圍是 0 - 255,就是一個(gè)字節(jié)的大小,IP地址剛好是4字節(jié), 32位的整數(shù);
1.2.端口號(hào)(port)?
- 端口號(hào)是一個(gè)2字節(jié)16位的整數(shù)
- 端口號(hào)用來(lái)標(biāo)識(shí)一個(gè)進(jìn)程,那么IP地址+端口號(hào)就可以標(biāo)識(shí)某一臺(tái)主機(jī)的某一個(gè)進(jìn)程
1.3.TCP/IP四層模型?
?
1.4.網(wǎng)絡(luò)字節(jié)序?
- 網(wǎng)絡(luò)字節(jié)序就是大端字節(jié)序(低位放在高地址上),使用相同的字節(jié)序便于網(wǎng)絡(luò)間通信;
有系統(tǒng)提供的接口?
2.UDP(user datagram protocol)協(xié)議的特點(diǎn)
UDP協(xié)議是一個(gè)傳輸層協(xié)議
- 無(wú)連接:沒(méi)有連接,客戶端發(fā)給服務(wù)器端,服務(wù)器端要先保存客戶端的信息,服務(wù)器端再使用這個(gè)信息發(fā)給對(duì)應(yīng)的客戶端(簡(jiǎn)單地說(shuō)就是需要指明發(fā)送給誰(shuí))
- 不可靠傳輸:只是傳遞數(shù)據(jù),成功與否都不會(huì)反饋
- 面向數(shù)據(jù)報(bào):不能向面向字節(jié)流的TCP一樣使用read和write來(lái)讀寫(xiě)
3.使用有UPD編寫(xiě)套接字
3.1.服務(wù)器端
3.1.1.創(chuàng)建套接字
int sock=socket(AF_INET,SOCK_DGRAM,0);//創(chuàng)建套接字if(sock<0){std::cerr<<"socket fail: "<<errno<<std::endl;return 1;}
3.1.2.bind服務(wù)器?
網(wǎng)絡(luò)字節(jié)序就是大端字節(jié)序(低位放在高地址上),使用相同的字節(jié)序便于網(wǎng)絡(luò)間通信;
- 1.需要將人識(shí)別的點(diǎn)分十進(jìn)制,字符串風(fēng)格IP地址,轉(zhuǎn)化為4字節(jié)整數(shù)IP.2.考慮大小端的問(wèn)題
服務(wù)器不用bind一個(gè)固定的IP的原因
- 1.云服務(wù)器,不允許bind公網(wǎng)IP,另外,在一般編寫(xiě)也不會(huì)指明IP.2.一般主機(jī)有多個(gè)IP,如果只是bind一個(gè)IP,發(fā)給其他IP的數(shù)據(jù)就不會(huì)交給主機(jī)處理
- INADDR_ANY只要是發(fā)給這個(gè)主機(jī)的數(shù)據(jù)都會(huì)被處理
struct sockaddr_in local;local.sin_family=AF_INET;local.sin_port=htons(port);//1.需要將人識(shí)別的點(diǎn)分十進(jìn)制,字符串風(fēng)格IP地址,轉(zhuǎn)化為4字節(jié)整數(shù)IP.2.考慮大小端的問(wèn)題//1.云服務(wù)器,不允許bind公網(wǎng)IP,另外,在一般編寫(xiě)也不會(huì)指明IP.2.一般主機(jī)有多個(gè)IP,如果只是bind一個(gè)IP,發(fā)給其他IP的數(shù)據(jù)就不會(huì)交給主機(jī)處理;//INADDR_ANY只要是發(fā)給這個(gè)主機(jī)的數(shù)據(jù)都會(huì)被處理local.sin_addr.s_addr=INADDR_ANY;if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)//bind主機(jī){std::cerr << "bind error : " << errno << std::endl;return 2;}
3.1.3.傳遞接受數(shù)據(jù)
- UDP是無(wú)連接的,需指明發(fā)給誰(shuí),也需要保存發(fā)送端的信息
//業(yè)務(wù)邏輯char message[1024];bool quit=false;while(!quit){//保存發(fā)送端信息struct sockaddr_in peer; socklen_t len=sizeof(peer);//接受數(shù)據(jù)ssize_t s=recvfrom(sock,message,sizeof(message)-1,0,(struct sockaddr*)&peer,&len);if(s>0){message[s]=0;std::cout<<"client# "<<message<<std::endl;}else{std::cerr<<"recvfrom"<<errno<<std::endl;return 2;}//給對(duì)端發(fā)送一個(gè)你好嗎std::string tm="你好嗎?";std::cout<<"server to client: "<<tm<<std::endl;sendto(sock,tm.c_str(),tm.size(),0,(struct sockaddr*)&peer,len);}
?3.2.客戶端
- 客戶端不需要顯示bind,當(dāng)傳輸?shù)谝粋€(gè)數(shù)據(jù)是會(huì)自動(dòng)隨機(jī)bind一個(gè)port(沒(méi)有被使用的port)
#include<iostream>
#include<cerrno>
#include<string>
#include<cstdlib>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>//client serverIP serverPort
int main(int argc,char* argv[])
{if(argc!=3){std::cout<<"請(qǐng)按格式輸入: client serverIP serverPort"<<std::endl;return 2;}int sock=socket(AF_INET,SOCK_DGRAM,0);//創(chuàng)建套接字if(sock<0){std::cout<<"socket create errno: "<<errno<<std::endl;return 1;}//客戶端不用顯示bind,OS會(huì)自動(dòng)bind;//服務(wù)器端會(huì)有規(guī)劃,讓port是沒(méi)有被占用的,讓別人來(lái)訪問(wèn)這個(gè)port;//client正常發(fā)送數(shù)據(jù)的時(shí)候,OS會(huì)自動(dòng)給你bind,采用隨機(jī)端口的方式struct sockaddr_in server;server.sin_family=AF_INET;server.sin_port=htons(atoi(argv[2]));server.sin_addr.s_addr=inet_addr(argv[1]);while(1){std::string message;std::cout<<"請(qǐng)輸入#";std::cin>>message;sendto(sock,message.c_str(),message.size(),0,(struct sockaddr*)&server,sizeof(server));struct sockaddr_in tmp;socklen_t tlen=sizeof(tmp);char buffer[1024];ssize_t s=recvfrom(sock,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&tmp,&tlen);if(s>0){std::cout<<"server say#: "<<buffer<<std::endl;buffer[s]=0; }else{std::cerr<<"recvfrom"<<errno<<std::endl;return 2;}}return 0;
}
4.使用TCP編寫(xiě)套接字
4.1.TCP的特點(diǎn)
TCP是一個(gè)傳輸層協(xié)議,和UDP的特點(diǎn)相反
- 有連接
- 可靠傳輸
- 面向字節(jié)流
4.2.TCP客服端?
先寫(xiě)的客戶端,因?yàn)榉?wù)器端會(huì)寫(xiě)多個(gè)版本
#include<iostream>
#include<stdio.h>
#include<cerrno>
#include<unistd.h>
#include<string>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>void Usage()
{std::cout<<"usage:./client server_IP server_port"<<std::endl;
}
int main(int argc,char* argv[])
{if(argc!=3){Usage();return 1;}//建立套接字int sock=socket(AF_INET,SOCK_STREAM,0);if(sock<0){std::cerr<<"socket"<<errno<<std::endl;return 2;}//自動(dòng)bind//連接服務(wù)器struct sockaddr_in local;local.sin_addr.s_addr=inet_addr(argv[1]);local.sin_port=htons(atoi(argv[2]));local.sin_family=AF_INET;connect(sock,(struct sockaddr*)&local,sizeof(local));//業(yè)務(wù)邏輯while(1){char buffer[1024]={0};std::cout<<"請(qǐng)輸入";fgets(buffer,sizeof(buffer),stdin);write(sock,buffer,sizeof(buffer));char mes[1024]={0};read(sock,mes,sizeof(mes));std::cout<<mes<<std::endl;}return 0;
}
4.3.TCP服務(wù)器端
4.3.1.創(chuàng)建套接字和bind服務(wù)器
- socket的第二個(gè)參數(shù)是SOCK_STREAM,UPD是SOCK_DGRAM(datagram),TCP是SOCK_STREAM(stream,就是字節(jié)流)
//建立套接字int listen_sock=socket(AF_INET,SOCK_STREAM,0);if(listen_sock<0){std::cerr<<"socket"<<errno<<std::endl;return 1;}//bind服務(wù)器struct sockaddr_in local;local.sin_family=AF_INET;local.sin_port=htons(PORT);local.sin_addr.s_addr=INADDR_ANY;//INADDR_ANY會(huì)bind執(zhí)行代碼的主機(jī)if(bind(listen_sock,(struct sockaddr*)&local,sizeof(local))<0){std::cerr<<"bind"<<errno<<std::endl;return 2;}
4.3.2.listen設(shè)為聆聽(tīng)狀態(tài)
//listen狀態(tài)listen(listen_sock,5);
?4.3.3.accept接受客戶端的連接,并返回一個(gè)文件描述符
- 因?yàn)門(mén)CP套接字是有連接的,連接成功后也是使用返回的套接字和對(duì)端進(jìn)行網(wǎng)絡(luò)通信
struct sockaddr_in tmp;
socklen_t tlen=sizeof(tmp);
//建立連接
int fd=accept(listen_sock,(struct sockaddr*)&tmp,&tlen);
4.4.單進(jìn)程版本(沒(méi)有人會(huì)使用)
- 一次只能讓一個(gè)客戶端訪問(wèn)
- 寫(xiě)端關(guān)閉讀端讀到文件結(jié)尾,再讀返回0
int main()
{//...創(chuàng)建套接字、bind、listen都省略了,每次都一樣冗余while(1){struct sockaddr_in tmp;socklen_t tlen=sizeof(tmp);//建立連接int fd=accept(listen_sock,(struct sockaddr*)&tmp,&tlen);if(fd<0){std::cerr<<"accept "<<errno<<std::endl;return 3;}std::cout<<"get a new link "<<std::endl;//1.單進(jìn)程versionwhile(1){char buffer[1024]={0};ssize_t s=read(fd,buffer,sizeof(buffer));if(s>0){buffer[s]=0;std::cout<<"client to server:"<<buffer<<std::endl;std::string message;message+="server to client:你好!";//給連接的客戶端發(fā)一個(gè)你好write(fd,message.c_str(),message.length());}else if(s==0){//寫(xiě)端關(guān)閉讀端讀到文件結(jié)尾,再讀返回0std::cout<<"client quit!"<<std::endl;break;}else{std::cerr<<"read "<<errno<<std::endl;break;}}}
}
4.5.多進(jìn)程版本
4.5.1.父進(jìn)程是一個(gè)循環(huán),他要一直接收新的客服端不能等待子進(jìn)程,解決方法
-
父進(jìn)程等待子進(jìn)程,子進(jìn)程創(chuàng)建后再創(chuàng)建孫子進(jìn)程執(zhí)行后序代碼,子進(jìn)程秒退等待時(shí)間可以忽略不計(jì);下面代碼
pid_t pid = fork();if(pid<0){continue;}else if(pid==0){//子進(jìn)程close(listen_sock);if(fork()>0)exit(0);//子進(jìn)程創(chuàng)建就直接退出,創(chuàng)建的孫子進(jìn)程執(zhí)行后序代碼,孫子進(jìn)程變成孤兒進(jìn)程被1號(hào)進(jìn)程領(lǐng)養(yǎng)serviceIO(fd);close(fd);exit(0);}else{//父進(jìn)程close(fd);//子進(jìn)程的PCB以父進(jìn)程的PCB做模板初始化,不是共享的//waitpid(&pid,NULL,0);//父進(jìn)程等待子進(jìn)程,子進(jìn)程創(chuàng)建后再創(chuàng)建孫子進(jìn)程執(zhí)行后序代碼,子進(jìn)程秒退等待時(shí)間可以忽略不計(jì)}
????????2.signal(SIGCHLD,SIG_IGN);//忽略SIGCHLD信號(hào),子進(jìn)程將自動(dòng)釋放;?
2.關(guān)閉不用的文件描述符,父進(jìn)程關(guān)閉accept返回的文件描述符,子進(jìn)程關(guān)閉socket返回的文件描述符
void serviceIO(const int& fd)
{//1.單進(jìn)程version,做成一個(gè)函數(shù)while(1){char buffer[1024]={0};ssize_t s=read(fd,buffer,sizeof(buffer));if(s>0){buffer[s]=0;std::cout<<"client to server:"<<buffer<<std::endl;std::string message;message+="server to client:你好!";//給連接的客戶端發(fā)一個(gè)你好write(fd,message.c_str(),message.length());}else if(s==0){//寫(xiě)端關(guān)閉讀端讀到文件結(jié)尾,再讀返回0std::cout<<"client quit!"<<std::endl;break;}else{std::cerr<<"read "<<errno<<std::endl;break;}}
}int main()
{//...創(chuàng)建套接字、bind、listen都省略了,每次都一樣冗余signal(SIGCHLD,SIG_IGN);//忽略SIGCHLD信號(hào),子進(jìn)程將自動(dòng)釋放while(1){struct sockaddr_in tmp;socklen_t tlen=sizeof(tmp);//建立連接int fd=accept(listen_sock,(struct sockaddr*)&tmp,&tlen);std::cout<<"get a new link "<<std::endl;if(fd<0){std::cerr<<"accept "<<errno<<std::endl;return 3;}//2.多進(jìn)程versionpid_t pid = fork();if(pid<0){continue;}else if(pid==0){//子進(jìn)程close(listen_sock);serviceIO(fd);close(fd);exit(0);}else{//父進(jìn)程close(fd);//子進(jìn)程的PCB以父進(jìn)程的PCB做模板初始化,不是共享的}}
}
4.6.多線程版本
- 線程是共享PCB的,所以在線程內(nèi)使用完畢關(guān)閉文件描述符即可;
- 不等待線程可以使用線程分離的方法
void serviceIO(const int& fd)
{//1.單進(jìn)程versionwhile(1){char buffer[1024]={0};ssize_t s=read(fd,buffer,sizeof(buffer));if(s>0){buffer[s]=0;std::cout<<"client to server:"<<buffer<<std::endl;std::string message;message+="server to client:你好!";//給連接的客戶端發(fā)一個(gè)你好write(fd,message.c_str(),message.length());}else if(s==0){//寫(xiě)端關(guān)閉讀端讀到文件結(jié)尾,再讀返回0std::cout<<"client quit!"<<std::endl;break;}else{std::cerr<<"read "<<errno<<std::endl;break;}}
}
void* HandlerRequest(void* agrs)
{pthread_detach(pthread_self());int fd=*((int*)agrs);serviceIO(fd);close(fd);//記得關(guān)閉文件描述符
}
int main()
{while(1){struct sockaddr_in tmp;socklen_t tlen=sizeof(tmp);//建立連接int fd=accept(listen_sock,(struct sockaddr*)&tmp,&tlen);std::cout<<"get a new link "<<std::endl;if(fd<0){std::cerr<<"accept "<<errno<<std::endl;return 3;}pthread_t tid;pthread_create(&tid,nullptr,HandlerRequest,(void*)&fd);}return 0;
}
5.把套接字封裝
- 使用靜態(tài)是因?yàn)椴挥脛?chuàng)建對(duì)象就可以使用sock::Socket()等函數(shù);
#pragma once
#include<iostream>
#include<cstdlib>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>namespace ns_socket{class sock{public:static int Socket(){int sock=socket(AF_INET,SOCK_STREAM,0);if(sock<0){std::cerr<<"socket"<<std::endl;exit(1);}return sock;}static void Bind(int sock,char* port){struct sockaddr_in local;local.sin_family=AF_INET;local.sin_addr.s_addr=INADDR_ANY;local.sin_port=htons(atoi(port));if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0){std::cerr<<"bind"<<std::endl;exit(2);}}static void Listen(int sock){if(listen(sock,5)<0){std::cerr<<"listen"<<std::endl;exit(3);}}static int Accept(int sock) {struct sockaddr_in tmp;socklen_t tlen=sizeof(tmp);int new_sock=accept(sock,(struct sockaddr*)&tmp,&tlen);if(new_sock<0){std::cerr<<"accept"<<std::endl;exit(4);}return new_sock; }static void Connect(int sock,char* server_ip,char* server_port){struct sockaddr_in local;local.sin_family=AF_INET;local.sin_addr.s_addr=inet_addr(server_ip);local.sin_port=htons(atoi(server_port));if(connect(sock,(struct sockaddr*)&local,sizeof(local))<0){std::cerr<<"connect"<<std::endl;exit(5);}else{std::cout<<"connet success"<<std::endl;}}};
}