【WiFi】Socket编程

《TCP/IP 网络编程》.((韩)尹圣雨)
socket是什么
网络编程

1.1、socket()

网络数据传输用的软件设备

int socket(int domain, int type, int protocol);
domain(协议族)

在这里插入图片描述

type(套接字类型,数据传输方式)
名称套接字
SOCK_STREAM流格式套接字TCP
SOCK_DGRAM数据报格式套接字UDP
  • SOCK_STREAM (数据的完整送达,tcp发送数据,有ack)
  1. 数据在传输过程中不会消失;
  2. 数据是按照顺序传输的;
  3. 数据的发送和接收不是同步的(有的教程也称“不存在数据边界”)。主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的;发送可以分批30+50+20,接收放一次100个取走,也可能10个10个取等等
    就像一个传送带,浏览器所使用的 http 协议就基于此,确保数据的完整送达,有答复ack
  • SOCK_DGRAM (效率和实时)
  1. 强调快速传输而非传输顺序;
  2. 传输的数据可能丢失也可能损毁;
  3. 限制每次传输的数据大小;
  4. 数据的发送和接收是同步的(有的教程也称“存在数据边界”)。
    像送快递的一样,确保快速送达,QQ聊天和语音就是这种
protocol(计算机间通信中使用的协议信息)
  • 通常为0,前两个参数就能确定了;
  • 除非同一协议族中存在多个数据传输方式相同的协议(?书上没有例子~)

1.2、sockaddr_in

struct sockaddr_in   
{
   sa_family_t 		sin_family; //地址族
   uint16_t  		sin_port;  //16位TCP/UDP端口号
   struct in_addr  	sin_addr; //32位IP地址
   char     		sin_zero[8];//不适用
};
sin_family

这个和上面的协议族可以说是一对一的
在这里插入图片描述

sin_port

一台计算机可以同时提供多种网络服务,端口号就是为了区分不同网络服务

sin_addr

目前广泛使用 IPv4 地址,它的资源是非常有限的,一台计算机一个 IP 地址是不现实的,往往是一个局域网才拥有一个 IP 地址。

  • INADDR_ANY,用于服务器的,自动获取运行服务器端的计算机IP地址(客户端,除非带有部分服务器端的功能,否则不会采用)

1.3、bind()

服务器端用于向套接字分配IP地址和端口号,只有这样,流经该 IP 地址和端口的数据才能交给套接字处理

int bind(int sock, struct sockaddr *addr, socklen_t addrlen);  //Linux
int bind(SOCKET sock, const struct sockaddr *addr, int addrlen);  //Windows
sockfd(套接字文件描述符,socket创建的返回值)
myaddr(存有创建的套接字地址信息的结构体)
addrlen(myaddr的长度)

1.4、connect()

用来建立连接,用于TCP

int connect(int sock, struct sockaddr *serv_addr, socklen_t addrlen);  //Linux
int connect(SOCKET sock, const struct sockaddr *serv_addr, int addrlen);  //Windows
sockfd(套接字文件描述符,socket创建的返回值)
myaddr(存有创建的套接字地址信息的结构体)
addrlen(myaddr的长度)

以上是两者都通用的,下面分析不同的


2、TCP

先创建服务器,达到listen(),这是客户端才能连接connect()服务器;存在客户端调用connect()前,服务器端有可能率先调用accept(),此时,服务器进入阻塞状态;
在这里插入图片描述

2.1、listen()

让套接字进入被动监听状态,此时客户端才能进入可发处连接请求,既是说,调用connect函数

int listen(int sock, int backlog);  //Linux
int listen(SOCKET sock, int backlog);  //Windows
sock(套接字文件描述符)
backlog(连接请求等待队列的长度,如5,则最多使5个连接请求进入队列)

2.2、accept()

响应客户端的请求

int accept(int sock, struct sockaddr *addr, socklen_t *addrlen);  //Linux
SOCKET accept(SOCKET sock, struct sockaddr *addr, int *addrlen);  //Windows
sock(套接字文件描述符)
myaddr(存有客户地址信息的结构体)
addrlen(myaddr的长度)

2.3、读、写

因为TCP是需要连接,才能通信的,所以在发送和接收数据不需要另一方的地址信息

  • 写数据,只是把数据写入到缓存里面(类似队列),具体什么时候发送,多大的数据才会发,有一个判断规则,所以导致接收方不知道头尾~
  • 读数据,也是从缓存里面读取出来
  • 当read()函数返回值为0时,表示对端已经关闭了 socket,这时候也要关闭这个socket,否则会导致socket泄露。netstat命令查看下,如果有closewait状态的socket,就是socket泄露了
ssize_t write(int fd, const void *buf, size_t nbytes);//Linux
ssize_t read(int fd, void *buf, size_t nbytes);//Linux

int send(SOCKET sock, const char *buf, int len, int flags);//Windows
int recv(SOCKET sock, char *buf, int len, int flags);//Windows
  • sock 套接字
  • buf 为要发送的数据的缓冲区地址
  • len 为要发送的数据的字节数
  • flags 为发送数据时的选项,一般为0或NULL

如何解决tcp粘包问题

我们可以把信息做成json,前面是包的基础信息(开放,不加密),后面是要发送的通信协议内容,协议内容可以进行加密

Server

#define TCP_LISTEN_PORT  9190
#define BUF_SIZE 30

static struct sockaddr_in serv_addr;
static struct sockaddr_in clnt_addr;
int tcp_socket_fd = -1;
void TCP_Server_Socket_Init(void)
{
	tcp_socket_fd  = socket(PF_INET, SOCK_STREAM, 0);
    if(-1 == tcp_socket_fd )
    {
        printf("socket() error\n");
        return;
    }

    memset(&serv_addr, 0, sizeof(serv_addr));
    
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_addr.sin_port = htons(TCP_LISTEN_PORT); 

	if(-1 == bind(tcp_socket_fd , (struct sockaddr*)&serv_addr, sizeof(serv_addr)))
	{
	    printf("bind() error\n");
        return;
	}  
	
	if(-1 == listen(tcp_socket_fd , 5)
	{
	    printf("listen() error\n");
        return;
	}  
}


int main()
{
    int fd_ret = 0;
    struct timeval timeout;
    fd_set fb_sockset;
    
	char message[BUF_SIZE];
	int srt_len;
	int clnt_socket_fb;
	socklen_t clnt_adr_sz;
	
	TCP_Server_Socket_Init();
	clnt_adr_sz = sizeof(clnt_adr);
	while(1)
	{
		FD_ZERO(&fb_sockset);
		FD_SET(tcp_socket_fd ,&fb_sockset);
		timeout.tv_sec=2;
		timeout.tv_usec=0;
		
		fd_ret = select(tcp_socket_fd +1, &fb_sockset, NULL, NULL, &timeout);
		if(fd_ret > 0)
		{
			clnt_socket_fb= accept(tcp_socket_fd ,  (struct sockaddr*)&clnt_addr, &clnt_adr_sz );
			if(-1 == clnt_socket_fb)
			{
				printf("accept() error\n");
			}
			else
			{
				while(srt_len=recv(clnt_socket_fb, message, BUF_SIZE, 0)!=0)
				{
					send(clnt_socket_fb, message, srt_len, 0);
					printf("Message from clent: %s",message);
					close(clnt_socket_fb);
				}
			}
		}
	}
	close(tcp_socket_fd );
	return 0;
}

Client

#define TCP_LISTEN_PORT  9190
#define BUF_SIZE 30

static struct sockaddr_in serv_addr;
static struct sockaddr_in clnt_addr;
int tcp_socket_fd = -1;
void TCP_Client_Socket_Init(void)
{
	tcp_socket_fd = socket(PF_INET, SOCK_STREAM, 0);
    if(-1 == tcp_socket_fd )
    {
        printf("socket() error\n");
        return;
    }

    memset(&clnt_addr, 0, sizeof(clnt_addr));
    
    clnt_addr.sin_family = AF_INET;
    clnt_addr.sin_addr.s_addr = inet_addr("192.168.0.1");//方式1
    clnt_addr.sin_addr.s_addr = htonl(0xC0A80001);//方式2
    clnt_addr.sin_port = htons(TCP_LISTEN_PORT ); 

	if(-1 == connect(tcp_socket_fd, (struct sockaddr*)&clnt_addr, sizeof(clnt_addr))))
	{
		printf("connect() error\n");
	}
	else
	{
		printf("connected...............\n");
	}
}


int main()
{
	char message[]=“Nice.to.meet.you!”;
	int srt_len;
	socklen_t serv_adr_sz;
	TCP_Client_Socket_Init();
	serv_adr_sz = sizeof(serv_addr);
	while(1)
	{
		send(tcp_socket_fd , message, strlen(message), 0, );
		str_lent = recv(tcp_socket_fd , message, BUF_SIZE -1, 0);
		printf("Message from server: %s",message);
	}
	close(tcp_socket_fd);
	return 0;
}

3、UDP

3.1、读、写

因为UDP是不需要连接,就能通信的,所以在发送和接收数据需要另一方的地址信息

ssize_t sendto(int sock, const void *buf, size_t nbytes, int flags, struct sockaddr *to, socklen_t addrlen);//Linux
ssize_t recvfrom(int sock, void *buf, size_t nbytes, int flags, struct sockaddr *to, socklen_t addrlen);//Linux

int send(SOCKET sock, const char *buf, int len, int flags);//Windows
int recv(SOCKET sock, char *buf, int len, int flags);//Windows
  • sock 套接字
  • buf 为要发送的数据的缓冲区地址
  • len 为要发送的数据的字节数
  • flags 为发送数据时的选项,一般为0或NULL
  • to 目标地址信息的结构体
  • addrlen to的长度

Server

#define UDP_LISTEN_PORT  9190
#define BUF_SIZE 30

static struct sockaddr_in serv_addr;
static struct sockaddr_in clnt_addr;
int udp_socket_fd = -1;
void UDP_Server_Socket_Init(void)
{
	udp_socket_fd = socket(PF_INET, SOCK_DGRAM, 0);
    if(-1 == udp_socket_fd )
    {
        printf("socket() error\n");
        return;
    }

    memset(&serv_addr, 0, sizeof(serv_addr));
    
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_addr.sin_port = htons(UDP_LISTEN_PORT ); 

	if(-1 == bind(udp_socket_fd , (struct sockaddr*)&serv_addr, sizeof(serv_addr)))
	{
	    printf("bind() error\n");
        return;
	}  
}


int main()
{
	char message[BUF_SIZE];
	int srt_len;
	socklen_t clnt_adr_sz;
	UDP_Server_Socket_Init();
	clnt_adr_sz = sizeof(clnt_adr);
	while(1)
	{
		str_lent = recvfrom(udp_socket_fd , message, BUF_SIZE, 0,  (struct sockaddr*)&clnt_addr, &clnt_adr_sz );
		sendto(udp_socket_fd, message, BUF_SIZE, 0,  (struct sockaddr*)&clnt_addr, clnt_adr_sz );
		
		printf("Message from client: %s",message);
	}
	close(udp_socket_fd);
	return 0;
}

Client

#define UDP_LISTEN_PORT  9190
#define BUF_SIZE 30

static struct sockaddr_in serv_addr;
static struct sockaddr_in clnt_addr;
int udp_socket_fd = -1;
void UDP_Client_Socket_Init(void)
{
	udp_socket_fd = socket(PF_INET, SOCK_DGRAM, 0);
    if(-1 == udp_socket_fd )
    {
        printf("socket() error\n");
        return;
    }

    memset(&clnt_addr, 0, sizeof(clnt_addr));
    
    clnt_addr.sin_family = AF_INET;
    clnt_addr.sin_addr.s_addr = inet_addr("192.168.0.1");//方式1
    clnt_addr.sin_addr.s_addr = htonl(0xC0A80001);//方式2
    clnt_addr.sin_port = htons(UDP_LISTEN_PORT ); 
}


int main()
{
	char message[]=“Nice.to.meet.you!”;
	int srt_len;
	socklen_t serv_adr_sz;
	
	UDP_Client_Socket_Init();
	serv_adr_sz = sizeof(serv_addr);
	while(1)
	{
		sendto(udp_socket_fd, message, strlen(message), 0,  (struct sockaddr*)&clnt_addr, sizeof(clnt_addr) );
		str_lent = recvfrom(udp_socket_fd , message, strlen(message), 0,  (struct sockaddr*)&clnt_addr, &serv_adr_sz);
		printf("Message from server: %s",message);

	}
	close(udp_socket_fd);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值