Linux下多线程网络通讯--单线程网络通讯

涉及知识点

  1. 客户端-服务器模式
  2. 计算机网络协议簇
  3. Socket 和文件描述符
  4. ipv4/ipv6
  5. IP地址和端口
  6. 大小端转换
  7. 通讯的整体流程

整体流程

服务器这边:

1.创建套接字的文件描述符
2.指定服务器IP地址和端口
3.绑定ip地址和端口
4.设置监听
5.等待客户端连接
6.创建通信套接字的文件描述符
7.通信:处理接收的信息, 向客户端发送消息
8.通信结束关闭文件描述符

客户端这边

1.创建用于通信的套接字文件描述符
2.连接服务器(指定服务器地址和端口)
3.通信:发送数据和接收数据
4.关闭文件描述符

源代码如下

服务器端:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>

int main()
{

	// 1.创建监听用的套接字文件描述符
	int fd = socket(AF_INET, SOCK_STREAM, 0);
	// socket()函数用于创建套接字, 
	// 第一个参数表示使用的协议簇, AF_INET 为IPv4格式,AF_INET6为IPv6格式
	// 第二个参数表示 使用哪一种传输方式,分为流式和报文方式.SOCK_STREAM为流式,SOCK_DGRAM为报文式
	// 第三个参数表示网络数据交换规则, 根据使用的传送方式不同, 交换规则也不同, 服务器这边指定了某种数据交换规则,那么客户端也需要使用服务器这边的规则才能进行通信,默认写0 流式写0默认式使用tcp 报文式的默认写0使用udp
	
	if(fd == -1)
	{
		perror("创建监听的socket失败");
		return -1;
	}

	// 2.绑定IP地址和端口
	struct sockaddr_in saddr;
	// Init the sockaddr_in
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(9999);
	saddr.sin_addr.s_addr = INADDR_ANY;	// 0 = 0.0.0.0
	// 将文件描述符和ip与端口进行绑定
	int ret = bind(fd, (struct sockaddr*)&saddr, sizeof(saddr));
	// 绑定函数
	// 第一个参数 文件描述符
	// 第二个参数 IP与端口组成的结构体
	// 指定IP和端口组成的结构体的内存大小
	if(ret == -1)
	{
		perror("绑定错误");
		return -1;
	}

	// 3.设置监听
	ret = listen(fd, 128);
	// 监听函数
	// 第一个参数为 监听套接字的文件描述符
	// 第二个参数为 最大监听多少个客户端
	if(ret == -1)
	{
		perror("监听错误");
		return -1;
	}

	// 4.等待客户端的连接
		// 创建用于通信的套接字
	struct sockaddr_in caddr;
	int addrlen = sizeof(caddr);
	// 接受客户端的连接
	int cfd = accept(fd, (struct sockaddr*)&caddr, &addrlen);
	// 连接函数
	// 第一个参数 监听文件的文件描述符
	// 第一个参数 客户端的ip地址和端口组成的结构体
	// 第三个参数 分配客户端信息内存的大小(ip地址 + 端口 组成的通信套接字的大小)
	if(cfd == -1)
	{
		perror("连接错误");
		return -1;
		
	// 如果连接成功, 输出客户端的ip地址和端口号
	char IP[32];
	printf("客户端ip地址为: %s, 端口为: %d\n",
	     inet_ntop(AF_INET, &caddr.sin_addr.s_addr, IP, sizeof(IP)),
	     ntohs(caddr.sin_port));
	     // 注意大小端的转换

	// const char *inet_ntop(int af, const void *src ,char *dst, socklen_t size);
	}

	// 5.通信
	while(1)
	{
		char Buff[1024];
		int len = recv(cfd, Buff, sizeof(Buff), 0);
		// 接收数据函数
		// 第一个参数 通信套接字的文件描述符
		// 第二个参数 通信的数据包,一块有效的内存
		// 第三个参数 通信数据包的大小或者长度
		// 第四个参数 标志, 一般不用,默认填0
		if(len > 0)
		{
			printf("客户端的数据: %s\n",Buff);
			send(cfd, Buff, len, 0);
			// 发送数据函数
			// 第一个参数 通信的套接字的文件描述符
			// 第二个参数 发送的数据包
			// 第三个参数 发送数据包的长度
			// 第四个参数 还是默认填0
		}else if(len == 0)
		{
			printf("客户端断开连接\n");
			break;
		}
		else
		{
			perror("接收错误");
			break;
		}
	}

	// 6.关闭文件描述符
	close(fd);
	close(cfd);
	return 0;
}
// 所以 服务器这边创建两个套接字
// 一个用来连接客户端
// 另一个用来通信

客户端:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>

int main()
{

	// 1.创建通信的套接字
	int fd = socket(AF_INET, SOCK_STREAM, 0);
	if(fd == -1)
	{
		perror("创建通信socket失败");
		return -1;
	}

	// 2.连接服务器  绑定服务器的ip和端口
	struct sockaddr_in saddr;
	// 初始化绑定的ip与端口的结构体
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(9999);
	inet_pton(AF_INET, "192.168.1.1", &saddr.sin_addr.s_addr);
	int ret = connect(fd, (struct sockaddr*)&saddr, sizeof(saddr));
	if(ret == -1)
	{
		perror("连接错误");
		return -1;
	}
	
	int number = 0;
	while(1)
	{	
		// 3.通信
		char Buff[1024];
		sprintf(Buff, "通信中 :, %d \n", number++);
		// 发送数据,通信的套接字  数据  实际长度 0 
		send(fd, Buff, strlen(Buff)+1, 0);
		// 这里+1 应为strlen读取不了字符串结尾的符号\0

		// 4. 接收数据前先清空数据
		memset(Buff, 0 , sizeof(Buff));
		// 接收数据
		int len = recv(fd, Buff, sizeof(Buff), 0);
		if(len > 0)
		{
			printf("Server say: %s\n", Buff);	
		}else if(len == 0)
		{
			printf("连接断开");
			break;
		}else
		{
			perror("接收失败");
			break;
		}
		sleep(1);
	}

	// 5. 关闭文件描述符;
	close(fd);
	return 0;
}
// 所以客户端只有一个文件描述符,只用来通信

对应的函数

  1. socket的创建:
int socket(int domain, int type, int protocol);
  1. 文件描述符与ip端口绑定函数
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  1. 服务器端监听客户端函数
 int listen(int sockfd, int backlog);
  1. 等待并连接客户端函数
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
  1. 接收数据的函数
ssize_t recv(int sockfd, void *buf, size_t size, int flags);
  1. 发送数据的函数
ssize_t send(int fd, const void *buf, size_t len, int flags);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值