网络编程 tcp udp http编程流程 网络基础知识

网络基础知识

OSI分层:应用层 表示层 会话层 传输层 网络层 数据链路层 物理层
tcp/ip: 应用层 传输层 网络层 数据链路
ip地址:唯一标识一台主机
ipv4 32位 ipv6 128位 寻址 可以反映物理上的一个变化
MAC地址:48 固化在计算机中
ip地址又两部分构成:网络号+主机号
端口号:标识一个应用程序的代号 短整型
协议:共同遵守的约定 tcp协议 网络中的规则
ip , http/https,/ftp, tcp/
ipv4头部结构
在这里插入图片描述
4位头部长度:表示15行 每行4字节就是60字节 减去 基本的20字节 剩余的选项 40字节
TCP头部结构
在这里插入图片描述

剩余的选项仍然是 40字节
应用程序传递数据的一个过程
tcp协议

在这里插入图片描述
这四层:应用层 传输层 网络层 数据链路

在这里插入图片描述
ip地址由网络号和主机号共同构成的

"129.168.1.1"点分十进制转化为
无符号整型:
unsignal int inet_addr();
无符号整型转化为点分十进制:
inet_ntoa();
在这里插入图片描述
每一个字节8位 都是十进制转化的,最后将四个字节组合在一起,变成一个无符号整型

大端:网络字节序列 htons()
地址:ip+port

表示Ipv4的地址结构

struct socketaddr_in ipv4;

通用的套接字地址

struct socketaddr;

套接字
像手机一样进行数据的收发

需要让服务器先运行起来,客户端主动连接服务器,所以服务器需要把自己的ip 和端口告诉客户端

在Windows下查看ip地址

ipconfig

在这里插入图片描述

Linux下查看虚拟机ip地址

ifconfig

在这里插入图片描述
判断两个主机是否连通

ping 

在这里插入图片描述
无法访问目标主机 ,说明该网络下没有这个主机
请求超时,可能有防火墙 没有成功

网络编程

tcp编程流程

图示理解

tcp服务器 ,客户端编程流程
在这里插入图片描述

bind和accept函数理解

bind就是看看saddr得ip地址端口有没有问题,ip地址是不是写错了,端口是不是被占用了,如果没有问题,就将表示符合该地址绑定,取名成功

int c = accept(sockfd, (struct sockaddr *)&caddr, &len);

服务器会在该行阻塞,等待用户端的连接,一旦connect成功,阻塞结束,得到新的描述符c,该c对应刚才的客户端,类似一个链接,每个c都对应一个客户端,表示一条链接。

监视套接字和链接套接字理解

监听套接字 都是这一个 类似文件描述符 在一个进程中,fd都是3 不变
链接套接字:一个链接 由于服务器012 被占用 监视套接字是3 所以链接套接字从4开始,说明一个客户端和服务器链接上了,如果另一个客户端链接,那就是5 …
如果不理解,可以看这篇文章,讲的挺通俗易懂的:
监视套接字和链接套接字

int c = accept(sockfd,(struct sockaddr*) &caddr,&len);//阻塞
服务器刚开始是不知道客户端的地址的,所以先 &caddr 把结构体放这里,等客户端根据服务器给的ip端口找到connect,此时accept接收,这个时候就知道了客户端的地址和ip,存储在caddr

为什么c变成了4 原来的sockfd是3 而客户端的sockfd一直是3
类似用你的手机3给10086电话,你的电话号不变,一直是3 服务器类似一个服务中心,服务器刚开始也是10086 即3号手机接收,接听到了一个用户,就转接到另一个人工客服接听,即4 号手机,原来的3号手机继续接听客户的电话。一次类推。

linux和window下的编程实现

ser.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main()
{
    int sockfd = socket (AF_INET,SOCK_STREAM,0);
    if(sockfd == -1)
    {
        exit(1);
    }

    struct sockaddr_in saddr, caddr;
    memset(&saddr,0,sizeof(saddr));
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(6000);
    saddr.sin_addr.s_addr = inet_addr("192.168.1.49");//“127.0.0.1”

    int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
    if ( res == -1)
    {
        printf("bind err\n");
        exit(1);
    }

    res = listen(sockfd,5);
    if ( res == -1 )
    {
        exit(1);
    }

    while( 1 )
    {
        socklen_t len = sizeof(caddr);
        int c = accept(sockfd,(struct sockaddr*) &caddr,&len);//阻塞
        if ( c < 0 )
        {
            continue;
        }

        printf("accept c=%d\n",c);

        char buff[128] = {0};
        int n = recv(c,buff,127,0);//阻塞
        printf("buff=%s\n",buff);
        send(c,"ok",2,0);
        close(c);
    }
}

cli.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main()
{
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    if ( sockfd == -1)
    {
        exit(1);
    }

    struct sockaddr_in saddr;
    memset(&saddr,0,sizeof(saddr));
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(6000);
    saddr.sin_addr.s_addr = inet_addr("192.168.1.49");

    int res = connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
    if ( res == -1)
    {
        printf("connect err\n");
        exit(1);
    }

    send(sockfd,"hello",5,0);
    char buff[128] = {0};
    recv(sockfd,buff,127,0);//ok
    printf("buff=%s\n",buff);

    close(sockfd);
}

Windows系统上的服务器和客户端

//window servier
#if 0
//ConsoleApptcp.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <WinSock2.h>   //网络头文件
#include <string.h>
#include <ws2tcpip.h>  //socklen_t
#include <iostream>


using namespace std;
#pragma comment(lib, "ws2_32.lib")   //网络库文件


//初始化网络库
void InitNetwork() {
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;
	wVersionRequested = MAKEWORD(2, 2);

	err = WSAStartup(wVersionRequested, &wsaData);
	if (err != 0)
	{
		return;
	}

}


int main()
{
	InitNetwork();
	SOCKET sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (sockfd == INVALID_SOCKET)
	{
		cout << "socket err" << endl;
		return 0;
	}

	struct sockaddr_in saddr;
	memset(&saddr, 0, sizeof(saddr));
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(6000);
	//saddr.sin_addr.S_un.S_addr = INADDR_ANY;
	saddr.sin_addr.S_un.S_addr = inet_addr("192.168.1.32");//这个要用需要加宏定义
	//saddr.sin_addr.S_un.S_addr = inet_pton(AF_INET,"192.168.1.50",NULL);

	int res = bind(sockfd, (SOCKADDR*)&saddr, sizeof(saddr));
	if (res == SOCKET_ERROR)
	{
		cout << "bind err" << endl;
		return 0;
	}

	if (listen(sockfd, 5) == SOCKET_ERROR)
	{
		cout << "listen err" << endl;
		return 0;
	}

	while (true)
	{
		//struct sockaddr_in caddr;
		SOCKADDR_IN caddr;
		socklen_t len = sizeof(caddr);

		int c = accept(sockfd, (SOCKADDR*)&caddr, &len);
		if (c == INVALID_SOCKET)
		{
			continue;
		}
		cout << "accept c=" << c << endl;

		char buff[128] = { 0 };

		while (true)
		{
			if (recv(c, buff, 127, 0) <= 0)
			{
				break;
			}

			cout << buff << endl;
			send(c, "ok", 2, 0);
		}

		closesocket(c);

	}

}
#endif



//windows client 
#if 0
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <WinSock2.h>   //网络头文件
#include <string.h>
#include <ws2tcpip.h>  //socklen_t
#include <iostream>


using namespace std;
#pragma comment(lib, "ws2_32.lib")   //网络库文件


//初始化网络库
void InitNetwork() {
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;
	wVersionRequested = MAKEWORD(2, 2);

	err = WSAStartup(wVersionRequested, &wsaData);
	if (err != 0)
	{
		return;
	}

}

int main()
{
	InitNetwork();
	SOCKET sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (sockfd == INVALID_SOCKET)
	{
		cout << "socket err" << endl;
		return 0;
	}

	struct sockaddr_in saddr;
	memset(&saddr, 0, sizeof(saddr));
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(6000);
	saddr.sin_addr.S_un.S_addr = inet_addr("192.168.1.49");

	int res = connect(sockfd, (struct sockaddr*)&saddr, sizeof(saddr));
	if (res == -1)
	{
		cout << "connect err" << endl;
		closesocket(sockfd);
		WSACleanup();
	}

	while (true)
	{
		cout << "input" << endl;
		char buff[128] = { 0 };
		cin >> buff;

		if (strncmp(buff, "end", 3) == 0)
		{
			break;
		}

		send(sockfd, buff, strlen(buff), 0);
		memset(buff, 0, 128);
		recv(sockfd, buff, 127, 0);
		cout << buff << endl;
	}

	closesocket(sockfd);
	WSACleanup();

	return 0;
}
#endif

tcp特点

 int n = recv(c,buff,1,0);

一次只收一个字符
在这里插入图片描述

当发送速度快,会一起写到缓冲区,然后一起发送,数据太大,会拆开发送
在这里插入图片描述
套接字有发送缓冲区和接收缓冲区

netstat


应答确认 超时重传
乱序重拍 去重
滑动窗口控制
tcp特点
面向连接的 可靠的 流式服务

udp编程流程

图示理解

在这里插入图片描述

udp特点

无连接 不可靠 数据报服务
双方无连接:服务器关了,再起启动,发送消息还是能收到,在建立一个客户端,也可以发送信息。
udp发送数据时,要保证数据收完,否则其他数据就丢了
严格的一对一,发几次收几次

一个端口可以被一个套接字绑定,可以绑定两个,是协议不同
在这里插入图片描述
tcp在应用层面不丢数据,底层会丢,网络层,丢了重发就行,tcp自身保证其可靠性
tcp适合传文件 丢一个字节都不行
udp适合视频通话 丢包就是卡了

http编程流程

图示+理解

在这里插入图片描述

应用层 http 浏览器和服务器之间的通讯
传输层 tcp

两次以上的请求复用了同一个tcp连接,就是长连接
http用80号端口
https用443号端口
小于1024的端口,需要管理员才能访问
在这里插入图片描述
后面有\r\n结束

在这里插入图片描述
在这里插入图片描述
最后一行还有一个\r\n
content_length 不包含报头
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
服务器收到浏览器的信息,放到buff数组里,服务器知道了浏览器想访问的资源就是index.html,服务器需要做的是找到该资源,发给浏览器就行

在这里插入图片描述
浏览器会自己发起一个请求,去访问图标
这是一组请求,可能会有多次请求,在一次点击的过程中
在这里插入图片描述

编程实现-网站服务器

myhttp.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include<fcntl.h>
#define PATH "/home/ittao/code/day17"
int socket_init();
char *get_filename(char buff[])
{
    if(buff==NULL){
        return NULL;
    }
    char *ptr=NULL;
    char*s=strtok_r(buff," ",&ptr);
    if(s==NULL){
        return NULL;
    }
    printf("way:%s\n",s);
    s=strtok_r(NULL," ",&ptr);
    return s;
}
void *thread_fun(void *arg)
{
    int c = (int)arg;
    while (1)
    {
        char buff[4096] = {0};
        int n = recv(c, buff, 4095, 0);
        if(n<=0){
            break;
        }
        printf("buff:%s\n", buff);
        char*filename=get_filename(buff);
        if(filename==NULL){
            send(c,"http err:404",12,0);
            break;
        }
        printf("filename:%s\n",filename);
        char path[256]={PATH};
        if(strcmp("/",filename)==0){
            strcat(path,"/index.html");
        }else{
            strcat(path,filename);
        }
        int fd=open(path,O_RDONLY);
        if(fd==-1){
            send(c, "http err:404", 12, 0);
        }
        int filesize=lseek(fd,0,SEEK_END);
        lseek(fd,0,SEEK_SET);

        char http_head[256]={" my HTTP/1.1 200 OK\r\n"};
        strcat(http_head,"Server:myhttp\r\n");
        sprintf(http_head+strlen(http_head),"Content-Length:%d\r\n",filesize);
        strcat(http_head,"\r\n");
        send(c,http_head,strlen(http_head),0);

        char data[1024];
        int num=0;
        while((num=read(fd,data,1024))>0){
            send(c,data,num,0);
        }
        close(fd);
    }
    close(c);
    printf("client close\n");
    pthread_exit(NULL);
}
int accept_client(int sockfd)
{
    struct sockaddr_in caddr;
    int len = sizeof(caddr);
    int c = accept(sockfd, (struct sockaddr *)&caddr, &len);
    return c;
}
int main()
{
    int sockfd = socket_init();
    if (sockfd == -1)
    {
        exit(1);
    }
    while (1)
    {
        int c=accept_client(sockfd);
        if (c != -1)
        {
            pthread_t id;
            pthread_create(&id, NULL, thread_fun, (void *)c);
        } 
    }
}
int socket_init()
{
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1)
    {
        return -1;
    }
    struct sockaddr_in saddr;
    memset(&saddr, 0, sizeof(saddr));
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(80);
    saddr.sin_addr.s_addr = inet_addr("192.168.1.49");
    int res = bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));
    if (res == -1)
    {
        printf("bind err\n");
        return -1;
    }

    res = listen(sockfd, 5);
    if (res == -1)
    {
        return -1;
    }
    return sockfd;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值