网络编程篇

网络编程

OSI模型与TCP/IP协议体系结构

协议:通信双方所遵守的通信规则

网络体系结构:指网络的层次结构和每层所使用的协议的集合

OSI模型共有七层:

高层:应用层、表示层、会话层

低层:传输层、网络层、数据链路层、物理层

TCP/IP协议族的体系结构

四层:

​ 应用层 传输层 网络层 网络接口和物理层

五层:

​ 应用层 传输层 网络层 数据链路层 物理层

TCP/IP协议族常用协议:

TCP:传输控制协议–》传输层

day2

客户端找服务器

sendto()

#include <sys/types.h>
#include <sys/socket.h>
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
	const struct sockaddr *dest_addr, socklen_t addrlen);

recvfrom()

#include <sys/types.h>
#include <sys/socket.h>
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
     struct sockaddr *src_addr, socklen_t *addrlen);

IO模型

IO多路复用:

允许同时对多个I/O进行控制

I/O多路复用。其基本思想是:

​ 先构造一张有关文件描述符(Linux中一切皆文件,IO操作就是文件操作)的表,然后调用一个函数(检查有无IO准备好,如果准备好,对应的位保持不变,否则清0,初始值为0)。当这些文件描述符中的一个或多个已准备好进行I/O时函数才返回。
​ 函数返回时告诉进程那个描述符已就绪,可以进行I/O操作。

先构造一张有关文件描述符(Linux中一切皆文件,IO操作就是文件操作)的表:

​ 是一个有1024位(128个字节,假设是32位的系统,元素类型是long,也就是32个元素)的数组,1024位的每一个位都代表一个文件描述符,也就是说这个表一共可以表示1024个文件描述符(1024路IO),第0位就是文件描述符0,第一位就是文件描述符1……

为什么用1个位表示一个文件描述符?

​ 1个位只有两种状态:0和1

​ 1个文件描述符就是一路IO,也就是IO有两种状态:准备好和没有准备好

​ 准备好就处理,没准备好就等待

day3

1、并发服务器

TCP循环服务器:

​ 不推荐使用,只能等待一个客户端退出后下一个才能开始链接,在同一时间只能相应一个客户端

UDP可以使用循环服务器

TCP并发服务器:

​ 客户端请求:连接(服务器listenFd)、数据传输(服务器connFd)

​ connFd可以有多个,listenFd只有一个

并发服务器模型

多进程并发服务器

​ 基本思想:建立连接和通信要分开,分别使用不同的进程完成

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <signal.h>
#include <pthread.h>


void *thread_func(void *arg)
{
	char buf[100] = {0};
	int connFd = *((int *)arg);
	int ret;
	//子线程
	//只用来和客户端通信
	//通信结束线程退出
	while(1)
	{
		memset(buf, 0, sizeof(buf));
		//接收消息并打印
		ret = recv(connFd, buf, sizeof(buf), 0);
		if(ret < 0)
		{
			perror("recv error!");
			close(connFd);
			return (void *)-1;
		}
		else if(0 == ret)
		{
			printf("client quit!\n");
			//子线程退出
			pthread_exit(NULL);
		}
		printf("recv:%s\n", buf);
		memset(buf, 0, sizeof(buf));
		strcat(buf, " play.....");

		int i;
		for(i = 0; i < 3; i++)
		{
			//发送消息
			ret = send(connFd, buf, sizeof(buf), 0);
			if(ret < 0)
			{
				perror("send error!");
				close(connFd);
				return (void *)-1;
			}
			printf("send ok!\n");
			sleep(1);
		}
		strcpy(buf, "end");
		send(connFd, buf, sizeof(buf), 0);
	}
    return NULL;
}




int main()
{
	//监听套接字文件描述符
	int listenFd = -1;
	//连接套接字的文件描述符
	int connFd = -1;
	//服务器的地址结构
	struct sockaddr_in servAddr = {0};
	//客户端的地址结构
	struct sockaddr_in cliAddr = {0};
	socklen_t len = sizeof(cliAddr);
	int ret;
	pthread_t tid;

	//创建套接字
	listenFd = socket(PF_INET, SOCK_STREAM, 0);
	if(listenFd < 0)
	{
		perror("socket error!\n");
		return -1;
	}
	printf("socket ok!\n");
	//绑定ip地址和端口号
	servAddr.sin_family = PF_INET;
	servAddr.sin_port = htons(10086);
	servAddr.sin_addr.s_addr = inet_addr("0.0.0.0");
	ret = bind(listenFd, (struct sockaddr *)&servAddr, sizeof(servAddr));
	if(ret < 0)
	{
		perror("bind error!");
		close(listenFd);
		return -1;
	}
	printf("bind ok!\n");
	//创建监听队列(使得主动的套接字变成可以被连接的被动套接字)
	ret = listen(listenFd, 1);
	if(ret < 0)
	{
		perror("listen error");
		close(listenFd);
		return -1;
	}

	while(1)
	{
		//主线程
		printf("listening....\n");
		//等待并建立连接
		connFd = accept(listenFd, (struct sockaddr *)&cliAddr, &len);
		if(connFd < 0)
		{
			perror("accept error!");
			close(listenFd);
			return -1;
		}
		printf("IP:%s,PORT:%d accept ok!\n", inet_ntoa(cliAddr.sin_addr), ntohs(cliAddr.sin_port));

		//创建子线程
		while(0 != pthread_create(&tid, NULL, thread_func, &connFd));

		//将子线程设置为分离态
		//分离态的线程在终止时资源会自动被系统回收
		pthread_detach(tid);
	}
	//关闭套接字
	//子进程执行不到这里就退出了
	close(listenFd);
	return 0;
}
```
    
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <string.h>

int main()
{
	int sockFd = -1;
	//服务器的地址结构
	struct sockaddr_in servAddr = {0};
	int ret;
	char buf[100] = "aaaaaaaaa";

	//创建套接字
	sockFd = socket(PF_INET, SOCK_STREAM, 0);
	if(sockFd < 0)
	{
		perror("socket error!\n");
		return -1;
	}
	printf("socket ok!\n");
	//按照服务器的ip地址和端口号连接服务器
	servAddr.sin_family = PF_INET;
	servAddr.sin_port = htons(10086);
	servAddr.sin_addr.s_addr = inet_addr("192.168.9.55");
	ret = connect(sockFd, (struct sockaddr *)&servAddr, sizeof(servAddr));
	if(ret < 0)
	{
		perror("connect error!");
		close(sockFd);
		return -1;
	}
	printf("connect ok!\n");
	while(1)
	{
		memset(buf, 0, sizeof(buf));
		printf("input:");
		gets(buf);
		if(0 == strcmp(buf, "quit"))
		{
			break;
		}
		//发送消息
		ret = send(sockFd, buf, sizeof(buf), 0);
		if(ret < 0)
		{
			perror("send error!");
			close(sockFd);
			return -1;
		}
		printf("send ok!\n");

		while(1)
		{
			memset(buf, 0, sizeof(buf));
			//接收消息并打印
			ret = recv(sockFd, buf, sizeof(buf), 0);
			if(ret < 0)
			{
				perror("recv error!");
				close(sockFd);
				return -1;
			}
			if(0 == strcmp(buf, "end"))
			{
				break;
			}
			printf("recv:%s\n", buf);
		}
	}
	//关闭套接字
	close(sockFd);
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值