网络编程基础知识以及代码示例(c++)

讲到网络编程就难免会想起TCP/IP这个超级重要的协议了。刚初学网络编程的朋友一定会混淆,因为如果拆开来看TCP协议是属于传输层的协议,IP协议是网络层的协议。所以当别人问你TCP/IP协议是什么的时候,我相信很多人都会把它拆开来说。其实呢,TCP/IP协议是一组协议的统称,可以说它是一个协议簇,里面包含了TCP,UDP,IP,ARP等子协议。当然TCP/IP协议也叫作传输控制协议或者互联网协议,这样就好理解多了。那么为什么大家习惯叫做TCP/IP协议,因为在这些子协议中,TCP协议和IP协议是最重要的。

在这里插入图片描述
1、 TCP/IP模型

接下来讲一下这个TCP/IP模型,TCP/IP模型主要分成四层,分别为应用层、传输层、网络层、网络接口层。
应用层:它是协议的最高层,应用程序使用相应的应用层协议把封装好的数据包传给传输层。应用层主要有FTP、NFS、Telnet、SMTP、SNMP等协议。传输层:它是协议的第二层。接到应用层的数据包之后,传输层会选择相应的协议,然后对数据流进行分组。
网络层:它是协议的第三层。负责在主机之间的通讯中选择数据包的传输路径即路由。网络接口层:它是协议的最底层。负责将发送的二进制流转换成数据帧或者将接收到的数据帧转换成二进制流。

在这里插入图片描述
2、 Socket类型

在计算机的世界里,有很多类型的套接字(Socket),比如有本地节点的路径名(Unix套接字、CCITT X.25(X.25套接字)、DARPA Internet 地址(Internet套接字)等,接下来主要阐述第三种最常用的socket——Internet Socket。
Internet Socket是最具代表性的也是最经典的套接字,那么我们根据数据的传输方式可以将它划分成一下两种,一种是基于TCP协议的Socket编程,一种是基于UDP协议的Socket编程。

1、 基于TCP协议的Socket编程
这是一种面向连接的套接字编程,我们称之为流式套接字(Stream Socket),在计算机代码中用SOCK_STREAM表示。它具有以下几个特点:a、数据传输是按照顺序进行的;b、数据传输的可靠的,准确无误的;c、数据的发送和接收是不同步的,存在着缓冲区;
在这里插入图片描述
服务器端的网络编程步骤如下:
首先使用socket()函数创建一个套接字,然后用SOCKADDR_IN addrSrv;定义一个结构体变量。那么这个结构体里面可以初始化IP地址、网络IP类型、端口号等信息。在服务端中,这个IP是服务器要绑定的主机的IP。初始化完之后,接着使用bind()函数进行绑定,绑定成功之后,将套接字设置为监听模式,然后使用accept()等待客户端的连接请求。
#include <stdio.h>
#include <WinSock2.h>
#pragma comment(lib,“ws2_32.lib”)
int main()
{
WSADATA wd; //定义一个套接字结构体
if(0 != WSAStartup(MAKEWORD(2,2),&wd))
{
printf(“加载套接字库失败:%d\n”,GetLastError());
return 0;
}
//判断版本是否一致
if(LOBYTE(wd.wVersion) != 2 || HIBYTE(wd.wVersion)!= 2)
{
printf(“请求的套接字版本不一致:%d\n”,GetLastError());
return 0;
}
//创建套接字
//tcp/ip =>TCP UDP
//TCP(传输控制协议) => 特点:可靠传输、必须建立连接
//UDP (用户数据报协议)=>特点:不可靠传输、可以不需要建立连接(QQ\微信)
SOCKET sockSrv = socket(AF_INET,SOCK_STREAM,0);
if(sockSrv == INVALID_SOCKET)
{
printf(“创建套接字失败!”);
return 0;
}
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY); //IP地址
addrSrv.sin_family = AF_INET; //ipv4
addrSrv.sin_port = htons(6940); //端口号要大于1024 //端口号

if(SOCKET_ERROR == bind(sockSrv,(sockaddr*)&addrSrv,sizeof(addrSrv)))
{
	printf("绑定套接字失败:%d\n",GetLastError());
	return 0;
}else{ printf("绑定成功!\n");}

//将套接字设置为监听模式
if(SOCKET_ERROR == listen(sockSrv,5))
{
	printf("设置监听失败:%d\n",GetLastError());
	return 0;
}
SOCKADDR_IN addrClient;
int len = sizeof(SOCKADDR);
while(1)
{
	//accept是一个阻塞函数,会返回一个已经建立好连接的套接字
	//SOCKADDR是一个结构体类型
	printf("正在等待客户端的连接......\n");
	SOCKET sockClient=accept(sockSrv,(SOCKADDR*)&addrClient,&len);
	
	//inet_ntoa()是一个将十进制数转换成点分十进制的函数
	printf("欢迎客户端:%s\t:%d\t连接服务器\n",inet_ntoa(addrClient.sin_addr),addrClient.sin_port);
   
	char srvbuf[100];
	sprintf(srvbuf,"【欢迎%s进入聊天系统】",inet_ntoa(addrClient.sin_addr));
    send(sockClient,srvbuf,strlen(srvbuf),0);
	while(1)
	{
		memset(srvbuf,0,sizeof(srvbuf));
		printf("请输入信息:\n");
		scanf("%s",srvbuf);
		send(sockClient,srvbuf,strlen(srvbuf),0);
		if(strcmp(srvbuf,"exit")==0)
		{
			printf("服务器结束与当前客户端的会话!\n");
			closesocket(sockClient);
			break;
		}

       char recbuf[100];
       memset(recbuf,0,sizeof(recbuf));
       recv(sockClient,recbuf,sizeof(recbuf),0);
       if(strlen(recbuf)>0)
         printf("客户端说:%s\n",recbuf);
	   if(strcmp(recbuf,"exit")==0)
		{
			printf("客户端已下线!\n");
			closesocket(sockClient);
			break;
		}
	}
}
return 0;

}

**客户端的代码如下:
#include <stdio.h>
#include <WinSock2.h>
#pragma comment(lib,“ws2_32.lib”)
int main()
{
char srvAddres[16];
printf(“请输入需要连接的服务器IP地址:\n”);
scanf(“%s”,srvAddres);
WSADATA wd; //定义一个套接字结构体
if(0 != WSAStartup(MAKEWORD(2,2),&wd))
{
printf(“加载套接字库失败:%d\n”,GetLastError());
return 0;
}
//判断版本是否一致
if(LOBYTE(wd.wVersion) != 2 || HIBYTE(wd.wVersion)!= 2)
{
printf(“请求的套接字版本不一致:%d\n”,GetLastError());
return 0;
}
SOCKET sockClient = socket(AF_INET,SOCK_STREAM,0);
if(sockClient == INVALID_SOCKET)
{
printf(“创建套接字失败!”);
return 0;
}
SOCKADDR_IN addrClient;
addrClient.sin_addr.S_un.S_addr = inet_addr(srvAddres); //inet_addr()函数的作用是将一个字符串转化成IP地址
addrClient.sin_family = AF_INET; //ipv4
addrClient.sin_port = htons(6940); //端口号要大于1024 //端口号

//连接服务器
if(SOCKET_ERROR == connect(sockClient,(SOCKADDR*)&addrClient,sizeof(SOCKADDR)))
{
	 printf("连接失败!错误代码:%d\n",GetLastError());
	 return 0;
}
char recbuf[100]={0};
recv(sockClient,recbuf,sizeof(recbuf),0);
printf("服务器说:%s\n",recbuf);
while(1)
{
  char recbuf[100];
  memset(recbuf,0,sizeof(recbuf));
  recv(sockClient,recbuf,sizeof(recbuf),0);
  if(strlen(recbuf)>0)
     printf("服务器说:%s\n",recbuf);
   if(strcmp(recbuf,"exit")==0)
		{
			printf("客户端已下线!");
			closesocket(sockClient);
			break;
		}
   char srvbuf[100]={0};
   printf("请输入信息:\n");
   scanf("%s",srvbuf);
   send(sockClient,srvbuf,strlen(srvbuf),0);
   if(strcmp(srvbuf,"exit")==0)
	{
		printf("客户端已下线!!");
		closesocket(sockClient);
		break;
	}
}
return 0;

}

2、 基于UDP协议Socket编程
这是一种面向无连接的套接字,我们称之为数据包套接字,在计算机代码中,用SOCK_DGRAM表示,SOCK_DGRAM是一种高效率、高速度的、不可靠的通信数据流。
在这里插入图片描述
在这里就跟大家解释过程了,相信大家经过两者的比对,也能发现他们的不同之处。

UDP服务端编程:
#include <stdio.h>
#include <WinSock2.h>
#pragma comment(lib,“ws2_32.lib”)
int main()
{
WSADATA wd; //定义一个套接字结构体
if(0 != WSAStartup(MAKEWORD(2,2),&wd))
{
printf(“加载套接字库失败:%d\n”,GetLastError());
return 0;
}
//判断版本是否一致
if(LOBYTE(wd.wVersion) != 2 || HIBYTE(wd.wVersion)!= 2)
{
printf(“请求的套接字版本不一致:%d\n”,GetLastError());
return 0;
}

SOCKET sockSrv = socket(AF_INET,SOCK_DGRAM,0);
if(sockSrv == INVALID_SOCKET)
{
	printf("创建套接字失败!");
	return 0;
}
SOCKADDR_IN addrSrv;               
addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);    //IP地址
addrSrv.sin_family = AF_INET;                        //ipv4
addrSrv.sin_port = htons(6940);         //端口号要大于1024  

if(SOCKET_ERROR == bind(sockSrv,(sockaddr*)&addrSrv,sizeof(addrSrv)))
{
	printf("绑定套接字失败:%d\n",GetLastError());
	return 0;
}else{ printf("绑定成功!\n");}

while(1)
{
	char buf[1024]={0};
	int len = sizeof(SOCKADDR);
	recvfrom(sockSrv,buf,sizeof(buf),0,(SOCKADDR*)&addrSrv,&len);
	printf("客户端说:%s\n",buf);

}
closesocket(sockSrv);
return 0;

}

客户端编程:
#include <stdio.h>
#include <WinSock2.h>

#pragma comment(lib,“ws2_32.lib”)

int main()
{
char addrSer[16]={0};
printf(“请输入IP地址:\n”);
scanf(“%s”,addrSer);
WSADATA wd; //定义一个套接字结构体
if(0 != WSAStartup(MAKEWORD(2,2),&wd))
{
printf(“加载套接字库失败:%d\n”,GetLastError());
return 0;
}

//判断版本是否一致
if(LOBYTE(wd.wVersion) != 2 || HIBYTE(wd.wVersion)!= 2)
{
	printf("请求的套接字版本不一致:%d\n",GetLastError());
	return 0;
}
SOCKET sockCen = socket(AF_INET,SOCK_DGRAM,0);
if(sockCen == INVALID_SOCKET)
{
	printf("创建套接字失败!");
	return 0;
}
SOCKADDR_IN addrSrv;               
addrSrv.sin_addr.S_un.S_addr = inet_addr(addrSer);    //IP地址
addrSrv.sin_family = AF_INET;                        //ipv4
addrSrv.sin_port = htons(6940);  

while(1)
{
	char buf[1024]={0};
	printf("请输入信息:");
	scanf("%s",buf);
	sendto(sockCen,buf,strlen(buf)+1,0,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));

}
closesocket(sockCen);

return 0;

}

作者赠言:希望大家能通过这篇文章能有所收获和启示。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐