Linux 网络编程 全解(二)--------套接字socket

版权声明:本BLOG上原创文章未经本人许可,不得转载,不得用于商业用途及传统媒体,否则属于侵权行为。 https://blog.csdn.net/weixin_40204595/article/details/82813020

写在前面:说一下写这个系列的目的,随着对物联网开发的深入,越来越觉得自己网络基础知识的薄弱,虽然开发过程中不需要对网络基础有很深入的了解照样能进行,但有一些问题仍然是不知其因,所以这个系列打算从最基本的网络知识展开记录,也是一边学习一边整理笔记。欢迎大家共同学习,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 <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>

#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 <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <ctype.h>


#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、测试结果

    

猜你喜欢

转载自blog.csdn.net/weixin_40204595/article/details/82813020