网络编程知识总结

本文详细介绍了互联网从冷战时期的阿帕网到TCP/IP协议的发展,以及网络层次结构和OSI七层模型。重点阐述了TCP和UDP协议的区别,如TCP提供可靠的数据传输,适用于安全性要求高的场景,而UDP则以高效但不可靠的传输适合于流媒体和少量数据交换。同时,解释了套接字编程接口,包括创建、绑定、监听、接受连接和数据交互等步骤。最后,给出了服务器和客户端的C语言实现示例。
摘要由CSDN通过智能技术生成

internet历史:

1、冷战时期: 1957年前苏联,1958。

			  1968年“资源共享的计算机网络”,是早期的网络雏形,简称"阿帕网","ARPAnet"。

2、阿帕网早起的传输控制协议: NCP,不能互联不同类型的计算机和不同类型的操作系统,没有纠错能力。

3、TCP/IP 协议: 是Internet工业上的标准,也被称为"世界语"。

	TCP协议:检测网络传输中的差错控制协议。
	IP 协议:互联不同类型的计算机和操作系统。

4、网络体系结构:

	采用分而治之的设计思想。主要指网络层次结构和每层使用的协议的集合。
  •   OSI七层模型:
      	应用层:提供应用层协议,FTP、E_mail、HTTP等,用来获取数据与用户进行交互。
      	表示层:将数据格式定义,转换/加密。
      	会话层:建立通信中逻辑名字与物理名字之间的联系。
      	传输层:差错处理/恢复,流量控制。提供可靠的数据传输服务。
      	网络层:数据分组、路由选择。
      	数据链路层:数据组成可发送、接口的帧。
      	物理层:信号、速率、接口等。
      	
      Tcp/IP协议体系结构:
      	应用层:提供应用程序的协议(TelNet、FTP、HTTP、DNS、SMTP等)
      		HTTP:超文传输协议
      		HTTPS:超文传输协议,被加密的,安全性更高
      		DNS:域名解析协议
      	传输层:提供数据传输控制协议(TCP、UDP)
      		TCP:传输控制协议
      		UDP:用户数据报协议
      	网络层:提供IP协议、ICMP(ping)、IGMP(组播)
      		IP:网间协议
      		ICMP:互联网控制协议: ping
      		IGMP:
      	网络接口和物理层:ARP(地址解析协议)、RARP(逆地址解析协议)
      		ARP:地址解析协议, 将MAC地址  --》 IP
      		RARP:逆地址解析协议, 将 IP --》 MAC地址
    

*** 5、TCP、UDP协议:

	TCP:提供一种面向连接的控制协议,数据安全可靠(数据无失序、无重复、无丢失)。
		应用场景:
			1、安全性要求较高的场合,QQ、微信的密码传输。
			2、大量数据传输,FTP
			3、需要点对点建立的传输
		优缺点:
			1、数据安全可靠
			2、传输效率低、耗时高
	UDP:无连接的协议,不保证数据的可靠性。
		应用场景:
			1、少量数据传输,QQ、微信 聊天数据
			2、网络流媒体数据传输,视频缓存
			3、广播、组播
		优缺点:
			1、传输速率高
			2、安全性差,容易丢包

** 6、预备知识

	1、socket:
		是一种网络编程的接口;
		也是一个特殊的文件描述符。
		
	2、套接字类型:
		流式套接字(SOCK_STREAM):提供一个面向连接的服务,保证数据的安全性、可靠性,(TCP协议)。
		数据报套接字(SOCK_DGRAM):无连接的服务器,不保证数据的安全可靠性(UDP协议)。
		原始套接字(SOCK_RAW):原始底层通信服务(IP、ICMP、IGMP)。
	3、ip地址:
		用来在网络中标识主机的。常以点分形式"192.168.12.177"。
			由:
  •   			网络号 + 子网号 + 主机号 : "192.168" + "12" + "177"。
      		
      	IPv4地址:32位存储,主机号占 8位
      	IPv6地址:128位存储,主机号占 32位
      	
      	根据ip地址的高8位进行分类:
      		A类:"0.0.0.0"  	~ "127.255.255.255"
      		B类:"128.0.0.0"	~ "191.255.255.255"
      		C类:"192.0.0.0" 	~ "223.255.255.255"
      		D类:"224.0.0.0" 	~ "239.255.255.255"		组播地址
      		E类:"240.0.0.0" 	~ "255.255.255.255"		保留地址
    
  •   	主机号: = ip地址 & (~子网掩码)
      			 = "192.168.12.177" * (~(255.255.255.0))
      		     = 177
      			 
      	网段号: = ip地址 & (255 << 8)
      			 = "192.168.12.177" & (255 << 8)
      			 = 12
      			 
      	注意:网段(子网号)就是路由器编号,路由器ip:网络号+子网号+1
      	
      4、相关ip的转换函数:
      		#include  <arpa/inet.h>
      	1、将主机字符串的ip地址转换成 网络字节序 二进制地址,返回转换后的地址。
    

** in_addr_t inet_addr(const char *ip);

		2、将网络字节序的二进制ip转换成 主机字符串ip

** char *inet_ntoa(struct in_addr inaddr);

	5、端口号
		用来标识处理网络数据的进程。也就是该进程的进程号。
		
	6、相关端口号的转换函数:
		1、主机字节序转网络字节序(小端序 -> 大端序)

** u_long htonl (u_long hostlong); // host to network long
** u_short htons (u_short short); // host to network short

		2、网络字节序 转主机字节序(大端序 -> 小端序)

** u_long ntohl (u_long hostlong); // network to host long
** u_short ntohs (u_short short); // network to host short

编程流程:

1、基于tcp的服务器流程:

	1、创建套接字 socket: 打开网卡设备、设置使用的网络协议和传输控制协议。
	2、绑定		  bind:   将本机ip、端口号与套接字绑定(将ip和端口号共享到网络中)。
	3、监听 	  listen   设置监听套接字,在内核中开辟 请求的队列,并设置该缓存长度。
	4、接受连接   accept   扫描请求队列是否有请求,如果没有就阻塞等待;
							如果有请求就 取出请求建立连接(通信的管道)。
	5、数据交互	  read/write、recv/send
	6、关闭		  close

2、系统调用接口:

	1、创建套接字
		函数原型:
			int socket(int domain, int type, int protocol);
		头文件:
			#include <sys/types.h>
			#include <sys/socket.h>
		参数:
			domain		:地址族协议,(网络层协议:IPV4-->AF_INET、IPV6-->AF_INET6)
			type		:套接字类型,(传输控制协议:流式套接字 --> SOCK_STREAM)
			protocol	:通常设置  0
		返回值:
			成功:返回 套接字(文件描述符)
			失败:返回 -1,并设置错误信息
	2、函数原型:
		int bind(int sockfd, struct sockaddr *addr, size_t size);
		功能:
			将ip地址、端口号 与 套接字进行绑定,将ip和端口共享到网络中。
		头文件:
			同上
		参数:
			sockfd:	套接字,socket函数成功的返回值
			addr  :	通用协议地址结构体的首地址
			size  :	结构体的大小
		返回值:
			成功:返回 0
			失败:返回-1,并设置错误信息
			
		通用协议地址结构体:
			struct sockaddr {
			   sa_family_t sa_family; 		//地址协议族 AF_INET
			   char        sa_data[14];
		    }
			
		Internet协议地址结构体:
			struct sockaddr_in {
			   sa_family_t    sin_family; /* address family: AF_INET */
			   in_port_t      sin_port;   /* port in network byte order */
			   struct in_addr sin_addr;   /* internet address */
		    };

		    /* Internet address. */
		    struct in_addr {
			   uint32_t       s_addr;     /* address in network byte order */
		    };
			
		结构体头文件: #include <arpa/inet.h>
		
		填写ip地址和端口号
			struct sockaddr_in addr;
			addr.sin_family		 = AF_INET;
			addr.sin_port 		 = htons(9527);
			// INADDR_ANY (0.0.0.0) means any address  for  binding;
			addr.sin_addr.s_addr = inet_addr("0.0.0.0");
	
	3、函数原型:
		int listen(int sockfd, int backlog);
		功能:
			设置监听套接字,设置 客户端连接请求队列的长度,
			也就是设置同一时刻能接受的最大请求数。
			
			监听完成就是启动服务器,套接字变成监听套接字
		参数:
			sockfd:套接字
			backlog:请求队列的长度,在内核中开辟的。
		返回值:
			成功:返回 0
			失败:返回-1,并设置错误信息
			
	4、函数原型:
		int accept(int sockfd, struct sockaddr *addr, socklen_t *len);
		功能:
			阻塞等待,接受客户端连接,建立通信管道。
			如果 请求队列中没有客户端的请求,该函数就阻塞。有就立即接受连接,建立通道。
		参数:
			sockfd:监听套接字
			addr  :结构体首地址(存储连接成功的客户端ip、端口号的结构体首地址)
			len	  :存储结构体的大小(存储客户端信息的结构体首地址)
		返回值:
			成功:返回连接套接字(通信管道的id号),标识成功的客户端。
			失败:返回-1,并设置错误信息
		注意:
			1、如果不需要保存客户端ip地址和端口号,则第2、3参数 传递 NULL。
			2、该函数调用一次 就只能接受一个 客户端连接
			
	5、数据交互
		函数原型:
			ssize_t read(int connfd, void *buf, size_t size);
			参数:
				connfd:连接套接字(通信管道id)
				
			ssize_t recv(int connfd, void *buf, size_t len, int flags);
			参数:
				flags:阻塞与非阻塞模式,通常0表示阻塞。
			
			注意:
				1、read读数据,默认为阻塞模式,如果读缓冲区有数据立即返回,无数据就等待
				2、recv读数据可以设置 模式
				3、当 连接断开时,read或recv立即返回 0值
				
			ssize_t write(int connfd, void *buf, size_t size);
			参数:
				connfd:连接套接字(通信管道id)
			
			ssize_t send(int connfd, void *buf, size_t len, int flags);
			参数:
				flags:阻塞与非阻塞模式,通常0表示阻塞。
				
	6、关闭套接字
		int close(int sockfd);

server、client基础版本

server.c

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

#define BUFF_SIZE 128
int  main()
{
//1.socket
int sockfd=socket(PF_INET,SOCK_STREAM,0);
    if(sockfd==-1)
    {
        perror("SOCKET error!");
        return -1;
    }
printf("sockfd success!\n");
//2.bind
struct sockaddr_in saddr;
memset(&saddr,0,sizeof(saddr));

saddr.sin_family=PF_INET;
saddr.sin_port=htons(8888);
saddr.sin_addr.s_addr=INADDR_ANY;

int s_len=sizeof(saddr);

if (bind(sockfd,(struct sockaddr *)&saddr,s_len)<0)
{
    perror("bind error!");
    return -1;
}
printf("bind success!\n");
//3.listen
int ret =listen(sockfd,5);
if(ret==-1)
{
    perror("listen fail!");
return -1;
}
printf("listening...\n");
//4.accept
while(1)
{//first while
struct sockaddr_in caddr;
memset(&caddr,0,sizeof(caddr));
int c_len=sizeof(caddr);
printf("wait a client ...!\n");

int connfd=accept(sockfd,(struct sockaddr*)&caddr,&c_len);
if(connfd==-1)
{
    perror("conncet fail!");
    return -1;
}
printf("connect success!\n");
char buf[BUFF_SIZE];
while(1)
{//second while
    ret=read(connfd,buf,sizeof(buf));
    if(ret<0)
    {
        perror("read error!");
        return -1;
    }
    if(ret==0)
    {
        printf("disconnect!\n");
        break;
    }
    printf("client:%s\n",buf);
    //hui fu
write(connfd,buf,sizeof(buf));
}//second while
close(connfd);
}//first while
 close(sockfd);
//5.read/write or revc/send

//6.close(connfd)and(sockfd)
    return 0;
}

client.c

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

#define BUFF_SIZE 128
int  main()
{
//1.socket
int sockfd=socket(PF_INET,SOCK_STREAM,0);
    if(sockfd==-1)
    {
        perror("SOCKET error!");
        return -1;
    }
printf("sockfd success!\n");
struct sockaddr_in saddr;
memset(&saddr,0,sizeof(saddr));
saddr.sin_family=PF_INET;
saddr.sin_port=htons(8888);
saddr.sin_addr.s_addr=INADDR_ANY;
if(connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr))==-1)
{
perror("connect fail!");
return -1;
}
char buf[BUFF_SIZE];
while(1)
{//first while
    printf("input>");
    fgets(buf,sizeof(buf),stdin);
    if(strcmp(buf,"quit\n") == 0)break;
    write(sockfd,buf,sizeof(buf));
    bzero(buf,0);
    read(sockfd,buf,sizeof(buf));
    printf("server:%s\n",buf);
}//first while
close(sockfd);
        return 0;
}

封装版本

serve.h

/*服务器搭建流程
1、肯定是要操作文件(网卡设备),那么socket去得到套接子。
2、绑定地址和端口号,就相当于挂起招牌,放到网络中去
3、设置监听,同一时刻允许的最大连接数,多了就会丢弃。
4、循环等待客户端连接
5、在循环里面等待到客户端连接,就开始按照客户端的合理需求,给客户端反馈。
6、关闭连接套接字,关闭socket套接子。

*/
#ifndef _SERVER_H
#define _SERVER_H
#include <stdio.h>
#include <string.h>
//socket
#include <sys/types.h>
#include <sys/socket.h>
#include <time.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<netinet/in.h>
#include<arpa/inet.h>
//read 
#include <unistd.h>

#define MAXMEM 5 //listen maxmember queue
#define TIMEOUT 5//超时检测时间设置

static struct sockaddr_in s_addr;//save host IP and PORT,put it online
static int s_len = sizeof(s_addr);

static struct sockaddr_in c_addr;//save connect client IP and PORT 
static int c_len = sizeof(c_addr);

static char buf[64] = {0};//save massage come from client
static int ret = 0;
static int sockfd;
static int connfd;

static int reuseaddr=1;

int server_init(int port);
int WaitForClient(int sockfd);

#endif

server.c

#include "server.h"

// 绑定IP和端口号放到网络上,一定要确定绑定操作的正确。主机字节序转换成网络字节序;
int server_init(int port)
{
    //得到套截字
    if((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0)
    {
        perror("sockfd");
        return -1;
    }
    //设置端口号重用
    if(setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&reuseaddr,sizeof(reuseaddr) ) < 0)
    {
    	perror("socket");
    	return -1;
    }
    //准备一个saddr给bind函数
    bzero(&s_addr,0);
    s_addr.sin_family = AF_INET;
    s_addr.sin_port = htons(port);
    s_addr.sin_addr.s_addr=htonl(INADDR_ANY);	
    	
    if( bind(sockfd,(struct sockaddr *)&s_addr,s_len ) <0)
    {
        perror("bind");
        return -1;
    }
    //设置同一时刻最大的连接数
    if( listen(sockfd,MAXMEM) < 0)
    {
        perror("listen");
        return -1;
    }
	return sockfd;
}
//等待客户端连接函数

int WaitForClient(int sockfd)
{	
	//超时检测
	struct timeval tm = {TIMEOUT,0};
	if( setsockopt(sockfd,SOL_SOCKET,SO_RCVTIMEO,&tm,sizeof(tm)) < 0)
	{
		perror("setsockopt");
		return -1;
	}
	
    	bzero(&c_addr,0);        
   	//accept等待客户端连接
       if((connfd = accept(sockfd,(struct sockaddr *)&c_addr,&c_len)) < 0)
       {
           perror("accept");
           return -1;
       }
       printf("connect from [%s:%d]\n",inet_ntoa(c_addr.sin_addr),ntohs(c_addr.sin_port));
       //数据收发
      	while(1)	
      {
      		ret = read(connfd,buf,sizeof(buf));
      		if(ret == 0)
      		{
      			break;
      		}
      		else if(ret < 0)
      		{
      			perror("read");
      			break;
      		}
      		if(strcmp(buf,"quit") == 0)
      		{
      			printf("wait for a new client\n");
       		close(connfd);
       		break;
      		}
      		else if(strcmp(buf,"exit") == 0)
      		{
      			printf("server quit\n");
      			exit(0);
      		}
      		printf("client:%s\n",buf);
      		write(connfd,buf,ret);
      		memset(buf,0,sizeof(buf));      		
      }
      close(connfd);
}

text.c 测试代码

#include"server.h"

int main(int argc,char *argv[])
{
    
    int sockfd = server_init(8888);
    if(sockfd < 0)
    {
    	return -1;
    }
    printf("server init success!\n");

    //wait for client connect
    while(1)
    {
    	WaitForClient(sockfd);
    }//1.while()
    close(sockfd);
    return 0;
}

网络传输时候组装数据和解析数据的

解析数据

int sscanf(要解析的字符串, “格式”,解析存放缓冲区);
注意%s解析的时候:“ ”遇到空格会断开,解决办法:还没想到;

组装数据

int sprintf(组装存放区域,“格式”,要组装的成员);注意等数据传输到网络中的时候都是以字符串的形式传输的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值