湖州住房和城乡建设厅网站,做网站后付款,黑龙江省建设教育网站,新公司网站建设方案目录
1.学习网络编程前的一些基础知识
2.UDP(user datagram protocol)协议的特点
3.使用有UPD编写套接字
4.使用TCP编写套接字
4.2.TCP客服端
4.3.TCP服务器端
4.4.单进程版本#xff08;没有人会使用#xff09;
4.5.多进程版本
4.6.多线程版本
5.把套接字封装 1…目录
1.学习网络编程前的一些基础知识
2.UDP(user datagram protocol)协议的特点
3.使用有UPD编写套接字
4.使用TCP编写套接字
4.2.TCP客服端
4.3.TCP服务器端
4.4.单进程版本没有人会使用
4.5.多进程版本
4.6.多线程版本
5.把套接字封装 1.学习网络编程前的一些基础知识
1.1.IP地址
IP地址是在IP协议中, 用来标识网络中不同主机的地址一个IP地址标识一台主机通常使用 点分十进制 的字符串表示IP地址, 例如127.0.0.1 ; 用点分割的每一个数字表示一个字节, 范围是 0 - 255就是一个字节的大小IP地址刚好是4字节, 32位的整数
1.2.端口号port
端口号是一个2字节16位的整数端口号用来标识一个进程那么IP地址端口号就可以标识某一台主机的某一个进程
1.3.TCP/IP四层模型 1.4.网络字节序
网络字节序就是大端字节序低位放在高地址上使用相同的字节序便于网络间通信
有系统提供的接口 2.UDP(user datagram protocol)协议的特点
UDP协议是一个传输层协议
无连接:没有连接客户端发给服务器端服务器端要先保存客户端的信息服务器端再使用这个信息发给对应的客户端简单地说就是需要指明发送给谁不可靠传输只是传递数据成功与否都不会反馈面向数据报不能向面向字节流的TCP一样使用read和write来读写
3.使用有UPD编写套接字
3.1.服务器端
3.1.1.创建套接字 int socksocket(AF_INET,SOCK_DGRAM,0);//创建套接字if(sock0){std::cerrsocket fail: errnostd::endl;return 1;} 3.1.2.bind服务器
网络字节序就是大端字节序低位放在高地址上使用相同的字节序便于网络间通信
1.需要将人识别的点分十进制字符串风格IP地址转化为4字节整数IP.2.考虑大小端的问题
服务器不用bind一个固定的IP的原因
1.云服务器不允许bind公网IP另外在一般编写也不会指明IP.2.一般主机有多个IP如果只是bind一个IP发给其他IP的数据就不会交给主机处理INADDR_ANY只要是发给这个主机的数据都会被处理struct sockaddr_in local;local.sin_familyAF_INET;local.sin_porthtons(port);//1.需要将人识别的点分十进制字符串风格IP地址转化为4字节整数IP.2.考虑大小端的问题//1.云服务器不允许bind公网IP另外在一般编写也不会指明IP.2.一般主机有多个IP如果只是bind一个IP发给其他IP的数据就不会交给主机处理;//INADDR_ANY只要是发给这个主机的数据都会被处理local.sin_addr.s_addrINADDR_ANY;if(bind(sock,(struct sockaddr*)local,sizeof(local))0)//bind主机{std::cerr bind error : errno std::endl;return 2;}
3.1.3.传递接受数据
UDP是无连接的需指明发给谁也需要保存发送端的信息//业务逻辑char message[1024];bool quitfalse;while(!quit){//保存发送端信息struct sockaddr_in peer; socklen_t lensizeof(peer);//接受数据ssize_t srecvfrom(sock,message,sizeof(message)-1,0,(struct sockaddr*)peer,len);if(s0){message[s]0;std::coutclient# messagestd::endl;}else{std::cerrrecvfromerrnostd::endl;return 2;}//给对端发送一个你好吗std::string tm你好吗?;std::coutserver to client: tmstd::endl;sendto(sock,tm.c_str(),tm.size(),0,(struct sockaddr*)peer,len);} 3.2.客户端
客户端不需要显示bind当传输第一个数据是会自动随机bind一个port(没有被使用的port)
#includeiostream
#includecerrno
#includestring
#includecstdlib
#includesys/types.h
#includesys/socket.h
#includenetinet/in.h
#includearpa/inet.h//client serverIP serverPort
int main(int argc,char* argv[])
{if(argc!3){std::cout请按格式输入: client serverIP serverPortstd::endl;return 2;}int socksocket(AF_INET,SOCK_DGRAM,0);//创建套接字if(sock0){std::coutsocket create errno: errnostd::endl;return 1;}//客户端不用显示bindOS会自动bind//服务器端会有规划让port是没有被占用的让别人来访问这个port//client正常发送数据的时候OS会自动给你bind采用随机端口的方式struct sockaddr_in server;server.sin_familyAF_INET;server.sin_porthtons(atoi(argv[2]));server.sin_addr.s_addrinet_addr(argv[1]);while(1){std::string message;std::cout请输入#;std::cinmessage;sendto(sock,message.c_str(),message.size(),0,(struct sockaddr*)server,sizeof(server));struct sockaddr_in tmp;socklen_t tlensizeof(tmp);char buffer[1024];ssize_t srecvfrom(sock,buffer,sizeof(buffer)-1,0,(struct sockaddr*)tmp,tlen);if(s0){std::coutserver say#: bufferstd::endl;buffer[s]0; }else{std::cerrrecvfromerrnostd::endl;return 2;}}return 0;
}
4.使用TCP编写套接字
4.1.TCP的特点
TCP是一个传输层协议和UDP的特点相反
有连接可靠传输 面向字节流
4.2.TCP客服端
先写的客户端因为服务器端会写多个版本 #includeiostream
#includestdio.h
#includecerrno
#includeunistd.h
#includestring
#includesys/types.h
#includesys/socket.h
#includearpa/inet.h
#includenetinet/in.hvoid Usage()
{std::coutusage:./client server_IP server_portstd::endl;
}
int main(int argc,char* argv[])
{if(argc!3){Usage();return 1;}//建立套接字int socksocket(AF_INET,SOCK_STREAM,0);if(sock0){std::cerrsocketerrnostd::endl;return 2;}//自动bind//连接服务器struct sockaddr_in local;local.sin_addr.s_addrinet_addr(argv[1]);local.sin_porthtons(atoi(argv[2]));local.sin_familyAF_INET;connect(sock,(struct sockaddr*)local,sizeof(local));//业务逻辑while(1){char buffer[1024]{0};std::cout请输入;fgets(buffer,sizeof(buffer),stdin);write(sock,buffer,sizeof(buffer));char mes[1024]{0};read(sock,mes,sizeof(mes));std::coutmesstd::endl;}return 0;
}
4.3.TCP服务器端
4.3.1.创建套接字和bind服务器
socket的第二个参数是SOCK_STREAM,UPD是SOCK_DGRAMdatagram,TCP是SOCK_STREAMstream就是字节流
//建立套接字int listen_socksocket(AF_INET,SOCK_STREAM,0);if(listen_sock0){std::cerrsocketerrnostd::endl;return 1;}//bind服务器struct sockaddr_in local;local.sin_familyAF_INET;local.sin_porthtons(PORT);local.sin_addr.s_addrINADDR_ANY;//INADDR_ANY会bind执行代码的主机if(bind(listen_sock,(struct sockaddr*)local,sizeof(local))0){std::cerrbinderrnostd::endl;return 2;}
4.3.2.listen设为聆听状态 //listen状态listen(listen_sock,5); 4.3.3.accept接受客户端的连接并返回一个文件描述符
因为TCP套接字是有连接的连接成功后也是使用返回的套接字和对端进行网络通信struct sockaddr_in tmp;
socklen_t tlensizeof(tmp);
//建立连接
int fdaccept(listen_sock,(struct sockaddr*)tmp,tlen);
4.4.单进程版本没有人会使用
一次只能让一个客户端访问写端关闭读端读到文件结尾再读返回0
int main()
{//...创建套接字、bind、listen都省略了每次都一样冗余while(1){struct sockaddr_in tmp;socklen_t tlensizeof(tmp);//建立连接int fdaccept(listen_sock,(struct sockaddr*)tmp,tlen);if(fd0){std::cerraccept errnostd::endl;return 3;}std::coutget a new link std::endl;//1.单进程versionwhile(1){char buffer[1024]{0};ssize_t sread(fd,buffer,sizeof(buffer));if(s0){buffer[s]0;std::coutclient to server:bufferstd::endl;std::string message;messageserver to client:你好!;//给连接的客户端发一个你好write(fd,message.c_str(),message.length());}else if(s0){//写端关闭读端读到文件结尾再读返回0std::coutclient quit!std::endl;break;}else{std::cerrread errnostd::endl;break;}}}
}
4.5.多进程版本
4.5.1.父进程是一个循环他要一直接收新的客服端不能等待子进程,解决方法 父进程等待子进程子进程创建后再创建孙子进程执行后序代码子进程秒退等待时间可以忽略不计下面代码
pid_t pid fork();if(pid0){continue;}else if(pid0){//子进程close(listen_sock);if(fork()0)exit(0);//子进程创建就直接退出创建的孙子进程执行后序代码孙子进程变成孤儿进程被1号进程领养serviceIO(fd);close(fd);exit(0);}else{//父进程close(fd);//子进程的PCB以父进程的PCB做模板初始化不是共享的//waitpid(pid,NULL,0);//父进程等待子进程子进程创建后再创建孙子进程执行后序代码子进程秒退等待时间可以忽略不计} 2.signal(SIGCHLD,SIG_IGN);//忽略SIGCHLD信号子进程将自动释放
2.关闭不用的文件描述符父进程关闭accept返回的文件描述符子进程关闭socket返回的文件描述符
void serviceIO(const int fd)
{//1.单进程version,做成一个函数while(1){char buffer[1024]{0};ssize_t sread(fd,buffer,sizeof(buffer));if(s0){buffer[s]0;std::coutclient to server:bufferstd::endl;std::string message;messageserver to client:你好!;//给连接的客户端发一个你好write(fd,message.c_str(),message.length());}else if(s0){//写端关闭读端读到文件结尾再读返回0std::coutclient quit!std::endl;break;}else{std::cerrread errnostd::endl;break;}}
}int main()
{//...创建套接字、bind、listen都省略了每次都一样冗余signal(SIGCHLD,SIG_IGN);//忽略SIGCHLD信号子进程将自动释放while(1){struct sockaddr_in tmp;socklen_t tlensizeof(tmp);//建立连接int fdaccept(listen_sock,(struct sockaddr*)tmp,tlen);std::coutget a new link std::endl;if(fd0){std::cerraccept errnostd::endl;return 3;}//2.多进程versionpid_t pid fork();if(pid0){continue;}else if(pid0){//子进程close(listen_sock);serviceIO(fd);close(fd);exit(0);}else{//父进程close(fd);//子进程的PCB以父进程的PCB做模板初始化不是共享的}}
}
4.6.多线程版本
线程是共享PCB的所以在线程内使用完毕关闭文件描述符即可不等待线程可以使用线程分离的方法
void serviceIO(const int fd)
{//1.单进程versionwhile(1){char buffer[1024]{0};ssize_t sread(fd,buffer,sizeof(buffer));if(s0){buffer[s]0;std::coutclient to server:bufferstd::endl;std::string message;messageserver to client:你好!;//给连接的客户端发一个你好write(fd,message.c_str(),message.length());}else if(s0){//写端关闭读端读到文件结尾再读返回0std::coutclient quit!std::endl;break;}else{std::cerrread errnostd::endl;break;}}
}
void* HandlerRequest(void* agrs)
{pthread_detach(pthread_self());int fd*((int*)agrs);serviceIO(fd);close(fd);//记得关闭文件描述符
}
int main()
{while(1){struct sockaddr_in tmp;socklen_t tlensizeof(tmp);//建立连接int fdaccept(listen_sock,(struct sockaddr*)tmp,tlen);std::coutget a new link std::endl;if(fd0){std::cerraccept errnostd::endl;return 3;}pthread_t tid;pthread_create(tid,nullptr,HandlerRequest,(void*)fd);}return 0;
}
5.把套接字封装
使用静态是因为不用创建对象就可以使用sock::Socket()等函数;
#pragma once
#includeiostream
#includecstdlib
#includesys/socket.h
#includesys/types.h
#includenetinet/in.h
#includearpa/inet.hnamespace ns_socket{class sock{public:static int Socket(){int socksocket(AF_INET,SOCK_STREAM,0);if(sock0){std::cerrsocketstd::endl;exit(1);}return sock;}static void Bind(int sock,char* port){struct sockaddr_in local;local.sin_familyAF_INET;local.sin_addr.s_addrINADDR_ANY;local.sin_porthtons(atoi(port));if(bind(sock,(struct sockaddr*)local,sizeof(local))0){std::cerrbindstd::endl;exit(2);}}static void Listen(int sock){if(listen(sock,5)0){std::cerrlistenstd::endl;exit(3);}}static int Accept(int sock) {struct sockaddr_in tmp;socklen_t tlensizeof(tmp);int new_sockaccept(sock,(struct sockaddr*)tmp,tlen);if(new_sock0){std::cerracceptstd::endl;exit(4);}return new_sock; }static void Connect(int sock,char* server_ip,char* server_port){struct sockaddr_in local;local.sin_familyAF_INET;local.sin_addr.s_addrinet_addr(server_ip);local.sin_porthtons(atoi(server_port));if(connect(sock,(struct sockaddr*)local,sizeof(local))0){std::cerrconnectstd::endl;exit(5);}else{std::coutconnet successstd::endl;}}};
}