首页 > 代码库 > C++网络Socket

C++网络Socket

【多机测试】
若没有多台电脑,可以使用虚拟机

有多台电脑,连接交换机

观察IP
ipconfig(Linux下是ifconfig)

检测连通性
ping


【发送接收缓冲区】

SendTo()

a)存入数据和发送数据存在时间差
b)如果存入数据太快,太多,则发送缓冲区会满

RecvFrom()

a)不论是否取,OS总是把数据收下存好
b)RecvFrom是从RecvBuf里取走现成的数据,如果不及时取,则缓冲区满。

 

缓冲区满的处理
发送数据(两种选择)
(1)直到缓冲区有剩余空间-阻塞
(2)新发送的数据没有存入缓冲区

接受数据(两种选择)
(1)新数据不被接受
(2)删除缓冲区已有数据,存放新的数据

丢包现象

设置发送/接受缓冲区的大小

if(1)
{
int bufsize=128*1024;
int ret=setsockopt(
	sock.hSock,
	SOL_SOCKET,
	SO_SNDBUF,//SO_RCVBUF
	(const char *)&bufsize,
	sizeof(int)
);
}

  

【丢包】
一般情况下设置缓冲区大小并不能解决丢包问题
(1)发送是否均匀
(2)接受是否及时
(3)数据带宽是否已经超过实际传输宽度

另外,即使HostA和HostB处理都没问题,网络上任意一个中间节点设备在转发该包时都有可能丢包。

结论:对UDP来说,丢包“家常便饭”,设计时考虑这一点


【网络字节序】
网络上的数据是一个字节一个字节地串行传输的
"hello"
h->68
e->65
l->6C
l->6C
o->6F

unsigned int a=0x12345678
它在内存的排列
78 56 34 12

规定
规定低字节在前,则称为小端
规定高字节在前,则称为大端

大端与小端是一套(硬件和软件),我们见到的主流都是小端
如intel/AMD,很少有大端系统

【网络传输时】
unsigned int a=0x12345678
如12 34 56 78(大端)
如78 56 34 12(小端)

惯例都是按大端传输
big-endian

一个int型数据占4个字节,下面按大端传输int型数据
【观察】

//HostA
#include<iostream>
using namespace std;

#include "afos/AfSocket.h"

void Endian_intToChar(int temp,char bytes[])
{
	bytes[0]=(char)(temp >> 24);
	bytes[1]=(char)(temp >> 16);
	bytes[2]=(char)(temp >> 8);
	bytes[3]=(char)(temp);
}

int main()
{
	//发送方
	AfSockAddr local("192.168.1.105",9000);//发送方地址
	AfUdpSocket soc;
	soc.Open(local,true);

	cout<<"发送方-端口9000 ->"<<endl;
	while (1)
	{
		cout<<"please push Enter!"<<endl;
		getchar();//每按一次回车...
		AfSockAddr peer("192.168.1.105",9001);
		char bytes[4];
		int temp=0x34560012;//int型数据的传输不再深究
		Endian_intToChar(temp,bytes);
		soc.SendTo(bytes,4,peer);
	}
	soc.Close();
	return 0;
}

  

//HostB
#include<iostream>
using namespace std;

#include "afos/AfSocket.h"

int Endian_CharToInt(char bytes[],int n)//四个字节char转为一个int型数据
{
	int temp=0;
	temp+=(bytes[0]<<24);
	temp+=(bytes[1]<<16);
	temp+=(bytes[2]<<8);
	temp+=(bytes[3]);
	return temp;
}

int main()
{
	//接受方
	AfSockAddr local("192.168.1.105",9001);//AfSockAddr local("192.168.1.104",9001);//接收方地址
	AfUdpSocket soc;
	soc.Open(local,true);

	cout<<"接收到-端口9001->"<<endl;
	char buffer[128];
	while (1)
	{
		AfSockAddr peer;
		int len=soc.RecvFrom(buffer,128,peer);
		buffer[len]=‘\0‘;
		if (len>0)
		{
			int temp;
			temp=Endian_CharToInt(buffer,len);
			printf("%08x\n",temp);
		}
		if (strcmp("quit",buffer)==0)
		{
			break;
		}
	}
	soc.Close();
	return 0;
}

  

【默认分配的端口号】
【端口的占用】
【多个发送方,一个接收方】
【阻塞与超时】

【1】默认分配的端口号
发送方可以不显示指定端口号
AfUdpSocket soc;
soc.Open();

可以查看自动分配的端口号
AfSockAddr local;
sock.GetLocalAddr(local);
unsigned int port=local.GetPort();
printf("local port:%d\n",port);

注意:在发送数据的时候,才会自动分配端口。

【2】端口的占用
当端口处于占用时,再创建一个Socket,还使用相同的端口,则创建失败,sock.Open()返回-1

一般设为可重用
sock.Open(local,true);//true允许重用,false不允许重用

 

【3】多个发送方,一个接收方

是谁发来的
string peer_ip=peer.GetIp_str();
int peer_port=peer.GetPort();
printf("from: IP->%s Port->%d",peer_ip.c_str(),peer_port);


【4】阻塞与超时

sock.RecvFrom()缓冲区无数据则等待,即阻塞,可设置最大时间
设置接收超时

sock.SetOpt_RecvTimeout(3000);//毫秒设置接收超时

 

AfUdpSocket soc;
soc.SetOpt_RecvTimeout(3000);//毫秒设置接收超时


int len=soc.RecvFrom(buffer,128,peer);


if (len<0)
{
printf("got %d\n",len);
}

 

 

非阻塞,定时查看(轮训),有数据返回0,无数据返回-1

 

C++网络Socket