写在前面:说一下写这个系列的目的,随着对物联网开发的深入,越来越觉得自己网络基础知识的薄弱,虽然开发过程中不需要对网络基础有很深入的了解照样能进行,但有一些问题仍然是不知其因,所以这个系列打算从最基本的网络知识展开记录,也是一边学习一边整理笔记。欢迎大家共同学习,QQ:993650814.
Linux 网络编程 全解(一)--------网络基础协议
正文:
一、套接字概念
socket本身有“插座”的意思,用于表示进程间网络通信的特殊文件类型。本质为内核借助缓冲区形成的伪文件,既然是文件,理所当然,就可以使用文件描述符引用套接字。
TCP/IP中,IP+端口号唯一标识网络通信的一个进程,建立连接的两个进程各有一个socket来标识,那么这两个组成的socket pair就唯一标识一个连接。所以,可以用socket来描述网络连接的一对一关系。套接字通信原理图如下:
在网络通信中,套接字一定是成对出现的,一端的发送缓冲区对应着对端的接受缓冲区。socket整个编程接口框架图如下:
二、网络字节序:
1、网络数据流采用大端字节序,即低地址高字节。计算机本地存储采用的是小端模式,网络采用的是大端模式,所以使用的时候需要做网络字节序和主机字节序的转换。
相关转换函数如下:
int32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort
h代表host,n代表net,l代表32位长度,s代表16位长度。如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回,如果主机是大端字节序,这些函数不做转换,将参数原封不动地返回。
2、 IP地址转换函数如下:
int inet_pton(int af, const char *src, void *dst)
作用:本地字节序转换成网络字节序。
af:AF_INET 或者 AF_INET6;
src:传入点分十进制的IP;
dst:传出网络字节序的IP地址。
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
作用:网络字节序转换成本地字节序。
af:AF_INET 或者 AF_INET6;
src:网络字节序的IP地址
dst:转换后的本地的点分十进制的IP地址。
size:dst的大小。
p代表IP地址,n代表net,其中 inet_pton 和 inet_ntop 不仅可以转换 IPv4 的 in_addr,还可以转换 IPv6 的 in6_addr。
因此函数接口是 void *addrptr。
3、sockaddr 地址结构
sockaddr_in // (man 7 ip)
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 */// 网络IP地址
};
/* Internet address. */
struct in_addr {
uint32_t s_addr; /* address in network byte order */
};
sockaddr_in addr;
addr.sin_addr.s_addr = htonl(INADDR_ANY);//取出系统中有效的任意IP地址。
三、socket模型创建流程图:
几处需要知道的细节部分:
socket建立一个客户端和一个服务器一共3个套接字。
服务器部分:
socket()//第一个socket
bind()//绑定服务器的地址结构IP+port
listen()//设置同时监听上限数,backlog(最大128)设置同时与服务器建立连接的上限数(同时进行三次握手的客户端 //数量)。
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);//阻塞监听客户端连接,返回一个新的socket,与客户端成功 //连接的socket的fd,第二个socket。
注意:addr:传出参数,成功与服务器建立连接的那个客户端的地址结构(IP+port)
addrlen:传入传出参数,入:addr的大小。出:客户端addr的实际大小。
返回:与客户端成功连接的socket的fd。失败返回-1
客户端部分:
socket()// 第三个socket
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
//与服务器建立连接,
// addr:传入参数:服务器地址结构(IP+port)
如果不使用bind绑定客户端地址结构,采默认为用“隐式绑定”
四、代码实践和测试:
1、TCP server部分
#include "stdio.h"
#include
#include
#include
#include
#include
#include
#define SERVER_PORT (6666)
int main(void)
{
int lfd = -1;
int connfd = -1;
struct sockaddr_in addr;
struct sockaddr_in cli_addr;
socklen_t cli_addr_len = sizeof(cli_addr);
char buf[1024] = {0};
ssize_t read_size = 0;
char cli_ip[10];
lfd = socket(AF_INET, SOCK_STREAM, 0);
if(lfd < 0)
{
printf("socket error\n");
}
memset(&addr,0,sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(SERVER_PORT);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(lfd, (struct sockaddr *)&addr,sizeof(addr));
listen(lfd, 128);
connfd = accept(lfd, (struct sockaddr *)&cli_addr, &cli_addr_len);
while(1)
{
if(connfd > 0)
{
inet_ntop(AF_INET,&cli_addr.sin_addr.s_addr,cli_ip,sizeof(cli_ip));
printf("connect port = %d\t,connect ip = %s\n",ntohs(cli_addr.sin_port),cli_ip);
read_size = read(connfd, (void *)buf, sizeof(buf));
write(STDOUT_FILENO,buf,read_size);
write(connfd, "receive OK\n", strlen("receive OK\n"));
//close(connfd);
}
}
return 0;
}
2、 TCP client部分
#include "stdio.h"
#include
#include
#include
#include
#include
#include
#include
#define SERVER_PORT (6666)
int main(void)
{
int cfd = -1;
struct sockaddr_in ser_addr;
int ret = -1;
char send_buf[100] = {"client send count:"};
char read_buf[100] = {0};
int i = 0;
int len = strlen(send_buf);
int read_size = 0;
//len ++;
printf("len = %d\n",len);
cfd = socket(AF_INET,SOCK_STREAM,0);
if(cfd < 0)
{
printf("socket error\n");
}
printf("in client demo\n ");
memset(&ser_addr,0,sizeof(struct sockaddr_in));
ser_addr.sin_family = AF_INET;
ser_addr.sin_port = htons(SERVER_PORT);
inet_pton(AF_INET, "127.0.0.1", &ser_addr.sin_addr);
ret = connect(cfd,(struct sockaddr *)&ser_addr,sizeof(ser_addr));
printf("ret = %d\n",ret);
if(ret >= 0)
{
for(i = 1; i < 6; i ++)
{
printf("i = %d\n",i);
memset(read_buf,0,sizeof(read_buf));
i = toascii(i);
send_buf[len] = 0x30 + i;
printf("send buf : %s\n",send_buf);
write(cfd, send_buf, strlen(send_buf) );
read_size = read(cfd,read_buf,sizeof(read_buf));
write(STDOUT_FILENO,read_buf,read_size);
sleep(5);
}
}
return 0;
}
3、测试结果