一、并发服务器
1.进程并发服务器
消耗资源大,每连接进来一个客户端,你就要去开辟进程去服务那个客户端
fork()
举例:
if(fork()==0) //子进程模块,不影响主进程中不断接收客户端连接 { zhuanfa(&cfd); }
实例代码如下:
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> // struct sockaddr_in client; // int len = sizeof(client); void *zhuanfa(void *arg) { int ret; int fd = *(int *)arg; char buf[1024];//接信息 char buf1[50] = "猖狂,北伐!"; while(1) { bzero(buf,sizeof(buf)); ret = recv(fd,buf,sizeof(buf),0); if(0 == ret) { printf("客户%d离开了\n",fd); close(fd); return NULL; }else { printf("客户%d:%s\n",fd,buf); // printf("客户%d进来了,IP地址为%s,端口号为%d\n",fd,inet_ntoa(client.sin_addr),ntohs(client.sin_port)); send(fd,buf1,strlen(buf1),0); } } return NULL; } int main(void) { //socket int serfd = socket(AF_INET,SOCK_STREAM,0); if(0>serfd) { perror("socket"); return -1; } //bind struct sockaddr_in ser;//netinet/in.h ser.sin_family = AF_INET; ser.sin_port = htons(8888); ser.sin_addr.s_addr = inet_addr("192.168.10.5"); if(bind(serfd,(struct sockaddr *)&ser,sizeof(ser))<0) { perror("bind"); return -1; } //listen listen(serfd,8); //accept int cfd; pthread_t a; while(1) { //不断接受不同的客户端,并分配一个服务员给客户对接,在线程进行通信 cfd = accept(serfd,NULL,NULL);//accept保存客户信息到client // pthread_create(&a,NULL,zhuanfa,&cfd); // pthread_detach(a); if(fork()==0) { zhuanfa(&cfd); } } return 0; }
2.线程并发服务器
占用资源资源比较小,代码维护起来困难
pthread_create //线程的创建
pthread_detach //给创建线程能自动收尸的能力
不自动:pthread_join
printf("客户%d进来了,IP地址为%s,端口号为%d\n",fd,inet_ntoa(client.sin_addr),ntohs(client.sin_port));
inet_ntoa(client.sin_addr) //网络二进制转回点分十进制
ntohs(client.sin_port) //大端转小端
实例代码如下:
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> struct sockaddr_in client; int len = sizeof(client); void *zhuanfa(void *arg) { int ret; int fd = *(int *)arg; char buf[1024];//接信息 char buf1[50] = "注意绿色上网!"; while(1) { bzero(buf,sizeof(buf)); ret = recv(fd,buf,sizeof(buf),0); if(0 == ret) { printf("客户%d离开了\n",fd); close(fd); return NULL; }else { printf("客户%d:%s\n",fd,buf); printf("客户%d进来了,IP地址为%s,端口号为%d\n",fd,inet_ntoa(client.sin_addr),ntohs(client.sin_port)); send(fd,buf1,strlen(buf1),0); } } return NULL; } int main(void) { //socket int serfd = socket(AF_INET,SOCK_STREAM,0); if(0>serfd) { perror("socket"); return -1; } //bind struct sockaddr_in ser;//netinet/in.h ser.sin_family = AF_INET; ser.sin_port = htons(8888); ser.sin_addr.s_addr = inet_addr("192.168.10.5"); if(bind(serfd,(struct sockaddr *)&ser,sizeof(ser))<0) { perror("bind"); return -1; } //listen listen(serfd,8); //accept int cfd; pthread_t a; while(1) { //不断接受不同的客户端,并分配一个服务员给客户对接,在线程进行通信 cfd = accept(serfd,(struct sockaddr *)&client,&len);//accept保存客户信息到client pthread_create(&a,NULL,zhuanfa,&cfd); pthread_detach(a); } return 0; }
二、域通信
优点:没网情况下照样能用客户端与服务器代码测试,模仿TCP/UDP
局限性:不能跨主机,只用于网络环境苛刻下的代码测试
区别:
域通信:
struct sockaddr_un <sys/un.h>
struct sockaddr_un{
sa_family_t sin_family; //地址族
char sun_path[108]; //s套接字的路径千万要用strcpy赋值
};
s套接字,在bind后运行执行文件它就出现
域通信TCP实例代码如下:
服务器:
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <sys/un.h> #include <sys/select.h> int main(void) { //socket(变动) int sockfd = socket(AF_UNIX,SOCK_STREAM,0);//注意AF_UNIX if(0>sockfd) { perror("socket"); return -1; } //bind(变动) struct sockaddr_un server; server.sun_family = AF_UNIX; strcpy(server.sun_path,"DJ"); bind(sockfd,(struct sockaddr *)&server,sizeof(server)); //listen listen(sockfd,8); //多路复用select int max = 0; int ret,cfd;//标志 fd_set rfds;//读集合 char buf[30]; while(1) { FD_ZERO(&rfds); FD_SET(0,&rfds); FD_SET(sockfd,&rfds); max = sockfd; if(cfd>sockfd)//第一遍还没连接,这个判断没有作用 { max=cfd; FD_SET(cfd,&rfds); } select(max+1,&rfds,NULL,NULL,NULL); if(FD_ISSET(0,&rfds)) { bzero(buf,sizeof(buf)); printf("0文件描述符触发\n"); scanf("%s",buf); printf("键盘输入:%s\n",buf); if(cfd>3)//说明有人连接,改变了cfd一开始的值 { send(cfd,buf,strlen(buf),0); } }else if(FD_ISSET(sockfd,&rfds)) { cfd = accept(sockfd,NULL,NULL); if(0>cfd) { perror("accept"); return -1; } printf("有客户连接进来了!\n"); }else { bzero(buf,sizeof(buf)); ret = recv(cfd,buf,sizeof(buf),0); if(0 == ret) { perror("recv"); return -1; }else { printf("客户说:%s\n",buf); } } } return 0; }
客户端:
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <sys/un.h> void *(recv_msg)(void *arg) { int ret; int fd = *(int *)arg; char buf[50]; while(1) { bzero(buf,sizeof(buf)); ret = recv(fd,buf,sizeof(buf),0); if(0>ret) { perror("recv"); return NULL; }else if(0 == ret) { printf("服务器离开了\n"); return NULL; }else printf("服务器说:%s\n",buf); } return NULL; } int main(int argc,char *argv[]) { //变量区 int clifd,ret; char buf[1024]; pthread_t pid; //1>进行传参错误判断 if(argc<2)//你运行时输入的个数 ./x a b { printf("请输入<./可执行> <S_name> \n"); return -1; } //2>创建socket套接字 clifd = socket(AF_UNIX,SOCK_STREAM,0); if(clifd<0) { perror("socket"); return -1; } printf("创建出的socket的值为%d\n",clifd); //3>声明s套接字 struct sockaddr_un server; server.sun_family = AF_UNIX; strcpy(server.sun_path,(argv[1])); #if 0 if(bind(clifd,(struct sockaddr *)&server,sizeof(server))<0) { perror("bind"); return -1; } //4>监听 if(listen(clifd,8)<0) { perror("listen"); return -1; } printf("监听已启动,保护服务器中^-^\n"); #endif //5>主动连接服务器 if(connect(clifd,(struct sockaddr *)&server,sizeof(server))<0) { perror("connect"); return -1; } printf("成功连接!\n"); //开辟线程 pthread_create(&pid,NULL,recv_msg,&clifd); pthread_detach(pid); //6>收发数据 while(1) { bzero(buf,sizeof(buf)); scanf("%s",buf); send(clifd,buf,strlen(buf),0); } //7>关闭套接字 close(clifd); return 0; }
补充说明:
注意:如果bind的错误提示,说地址已经占用
就用remove();清掉自己绑定的s套接字,再运行就没有
AF_UNIX
进程间通信有七种
早期:
1>无名管道
2>有名管道
3>信号
系统:
4>消息队列
5>共享内存
6>信号量
网络编程:
7>s套接字
正常:
struct sockaddr_in
网络属性(IP地址和端口号)
AF_INET
三、广播与组播(UDP)
1.广播
看图
允许发送的广播的属性怎么设置
#include<sys/types.h>
#include<sys/socket.h>
setsockopt
int setsockopt(int sockfd,int level,int optname,const void * optval,socklen_t optlen);
功能:
设置套接字的属性
参数:
sockfd:套接字
level:等级
optname:属性名字
optval:属性的值
optlen:属性的长度
返回值:
成功为0
失败返回-1,并设置错误码
举例:
1>允许发送的广播的属性
int on = 1;//1>为生效值,0>不生效
setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&on,sizeof(on));
2>允许重复用
int on = 1;//1>为生效值,0>不生效
setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on))
实例代码如下:
sendto:
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <sys/un.h> int main(void) { int sockfd = socket(AF_INET,SOCK_DGRAM,0); if(0>sockfd) { perror("socket"); return -1; } //设置发送广播属性 int on = 1;//1>为生效值,0>不生效 setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&on,sizeof(on)); struct sockaddr_in gb; gb.sin_family = AF_INET; gb.sin_port = htons(10086); gb.sin_addr.s_addr = inet_addr("192.168.10.255"); #if 0 if(bind(sockfd,(struct sockaddr *)&gb,sizeof(gb))<0) { perror("bind"); return -1; } #endif char buf[1024]; // int addrlen = sizeof(gb); while(1) { bzero(buf,sizeof(buf)); scanf("%s",buf); sendto(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&gb,sizeof(gb)); // printf("广播:%s\n",buf); } return 0; }
recvfrom:
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <sys/un.h> int main(void) { int sockfd = socket(AF_INET,SOCK_DGRAM,0); if(0>sockfd) { perror("socket"); return -1; } struct sockaddr_in gb; gb.sin_family = AF_INET; gb.sin_port = htons(10086); gb.sin_addr.s_addr = inet_addr("192.168.10.255"); if(bind(sockfd,(struct sockaddr *)&gb,sizeof(gb))<0) { perror("bind"); return -1; } char buf[1024]; int addrlen = sizeof(gb); while(1) { bzero(buf,sizeof(buf)); recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&gb,&addrlen); printf("广播:%s\n",buf); } return 0; }
2.组播
特定的广播,进一步细化成员
看图
多播组只有一个人,为单播;人多,就多播。
optval,————》ip-mreq{} //与stuct sockaddr_in一个头文件netinet/in.h struct ip_mreq { struct in_addr imr_multiaddr; // 组播地址 struct in_addr imr_interface; //自己linux的ip地址 }; struct in_addr{ In_addr_t s_addr; //32位IPv4地址 }; 举例:加入多播组 struct ip_mreq zb; zb.imr_multiaddr.s_addr = inet_addr("233.233.233.233"); zb.imr_interface.s_addr = inet_addr("192.168.10.5"); setsockopt(sockfd,IPPROTO_IP,SO_ADD_MEMBERSHIP,&zb,sizeof(zb));
实例代码如下:
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <sys/un.h> int main(void) { //1.socket int sockfd = socket(AF_INET,SOCK_DGRAM,0); if(0>sockfd) { perror("socket"); return -1; } //2.运行发送广播 int on = 1;//1>为生效值,0>不生效 setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&on,sizeof(on)); //3.声明组播地址 struct sockaddr_in zb; zb.sin_family = AF_INET; zb.sin_port = htons(9898); zb.sin_addr.s_addr = inet_addr("233.233.233.233"); // if(bind(sockfd,(struct sockaddr *)&zb,sizeof(zb))<0) // { // perror("bind"); // return -1; // } //3.加入多播组 // struct ip_mreq zb; // zb.imr_multiaddr.s_addr = inet_addr("233.233.233.233"); // zb.imr_interface.s_addr = inet_addr("192.168.10.5"); // setsockopt(sockfd,IPPROTO_IP,SO_ADD_MEMBERSHIP,&zb,sizeof(zb)); //4.接收数据 char buf[30]; // int len = sizeof(zb); while(1) { bzero(buf,sizeof(buf)); scanf("%s",buf); sendto(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&zb,sizeof(zb)); // printf("S:%s\n",buf); } return 0; }
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <sys/un.h> int main(void) { //1.socket int sockfd = socket(AF_INET,SOCK_DGRAM,0); if(0>sockfd) { perror("socket"); return -1; } //2.bind组播地址 struct sockaddr_in zb; zb.sin_family = AF_INET; zb.sin_port = htons(9898); zb.sin_addr.s_addr = inet_addr("233.233.233.233"); if(bind(sockfd,(struct sockaddr *)&zb,sizeof(zb))<0) { perror("bind"); return -1; } //3.加入多播组 struct ip_mreq db; db.imr_multiaddr.s_addr = inet_addr("233.233.233.233"); db.imr_interface.s_addr = inet_addr("192.168.10.5"); setsockopt(sockfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&db,sizeof(db)); //4.接收数据 char buf[30]; int len = sizeof(zb); while(1) { bzero(buf,sizeof(buf)); recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&zb,&len); printf("S:%s\n",buf); } return 0; }
四、图解如下
总结
关于C/C++网络编程基础知识超详细讲解第二部分的详解,懒大王就先分享到这里了,如果你认为这篇文章对你有帮助,请给懒大王点个赞点个关注吧,如果发现什么问题,欢迎评论区留言!!💕💕 💕