C/C++网络编程基础知识超详细讲解第三部分(系统性学习day13)

懒大王感谢大家的关注和三连支持~

目录

前言

一、并发服务器

1.进程并发服务器

实例代码如下:

2.线程并发服务器

实例代码如下:

二、域通信

域通信TCP实例代码如下:

三、广播与组播(UDP)

1.广播

实例代码如下:

2.组播

实例代码如下:

四、图解如下

总结


前言

作者简介: 懒大王敲代码,正在学习嵌入式方向有关课程stm32,网络编程,数据结构C/C++等

今天给大家继续详细讲解网络编程基础知识,希望能够帮到大家!

欢迎大家点赞 👍 收藏 ⭐ 加关注哦!💕 💕 💕


一、并发服务器

1.进程并发服务器

消耗资源大,每连接进来一个客户端,你就要去开辟进程去服务那个客户端

fork()

举例:

if(fork()==0) //子进程模块,不影响主进程中不断接收客户端连接

{

zhuanfa(&cfd);

}

实例代码如下:

cs 复制代码
#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) //大端转小端

实例代码如下:

cs 复制代码
#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实例代码如下:

服务器:

cs 复制代码
#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;
}

客户端:

cs 复制代码
#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:

cs 复制代码
#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:

cs 复制代码
#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));

实例代码如下:

cs 复制代码
#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;
}
cs 复制代码
#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++网络编程基础知识超详细讲解第二部分的详解,懒大王就先分享到这里了,如果你认为这篇文章对你有帮助,请给懒大王点个赞点个关注吧,如果发现什么问题,欢迎评论区留言!!💕💕 💕

相关推荐
虾球xz3 分钟前
游戏引擎学习第59天
学习·游戏引擎
枫零NET31 分钟前
学习思考:一日三问(学习篇)之匹配VLAN
网络·学习·交换机
沐泽Mu1 小时前
嵌入式学习-QT-Day07
c++·qt·学习·命令模式
沐泽Mu1 小时前
嵌入式学习-QT-Day09
开发语言·qt·学习
炸毛的飞鼠1 小时前
汇编语言学习
笔记·学习
egekm_sefg2 小时前
webrtc学习----前端推流拉流,局域网socket版,一对多
前端·学习·webrtc
m0_748241702 小时前
前端学习:从零开始做一个前端开源项目
前端·学习·开源
啊哈哈哈哈哈啊哈哈4 小时前
P7——pytorch马铃薯病害识别
人工智能·深度学习·学习
sensen_kiss4 小时前
CAN201 Introduction to Networking(计算机网络)Pt.2 传输层
网络·学习·计算机网络
橘子遇见BUG4 小时前
Unity Shader学习日记 part 3 线性代数--矩阵变换
学习·线性代数·unity·矩阵·图形渲染