欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 教育 > 锐评 > [Linux网络编程]02-Socket编程

[Linux网络编程]02-Socket编程

2024/10/25 20:30:06 来源:https://blog.csdn.net/SlanderMC/article/details/143078957  浏览:    关键词:[Linux网络编程]02-Socket编程

一.套接字(Socket)

在通信过程中,套接字一定是成对出现的(通信双方各持一个)

一个文件描述符指向一个套接字(该套接字内部由内核借助两个缓冲区实现读写),即一个套接字只有一个文件描述符,但有两个缓存区,与管道正好相反。

Linux套接字实现原理:
在这里插入图片描述

二.预备知识

1.网络字节序和本地字节序
内存中的多字节数据相对于内存地址有大端小端之分

小端法:(PC本地存储)高位存高地址,低位存低地址
大端法:(网络存储)高位存低地址,低位存高地址

网络的数据流采用大端字节序,而本地数据流采用小端字节序.
因此要通过函数来转换:
htonl : 本地 -->网络 (IP)
htons : 本地 --> 网络 (port端口)
ntohl : 网络 --> 本地 (IP)
ntohs : 网络 --> 本地 (port)

在这里插入图片描述
此函数转换端口号字节序容易,但是进行IP的大小端转换比较麻烦,因为linux中ip为字符串类型,而函数需要的是整数,需要经过多次转换,所以linux提供了以下封装好的转换函数inet_pton / inet_ntop。

2. ip转换函数
两个函数,inet_ptoninet_ntop

int inet_pton(int af,const char *src, void *dst)
本地字节序—>网络字节序

参数:
af:AF_INET、AF_INET6 ------------------>宏,分别对应IPV4和IPV6
src:传入,IP地址(点分十进制)
dst:传出,转换后的 网络字节序的 IP地址

返回值:
成功:1
异常: 0,说明src指向的不是一个有效的ip地址。
失败:-1

const char *inet_ntop(int af, const void *src, char *dst,socklen t size)
网络字节序 —>本地字节序
参数:
af:AF INET、AF INET6
src:网络字节序IP地址
dst:本地字节序(stringIP)
size:dst 的大小。

返回值:
成功:dst
失败:NULL

3.sockaddr数据结构
头文件#include <arpa/inet.h>
网络操作函数大部分需要的参数类型为sockaddr类型,这是为了向前兼容。
但实际使用时,我们先利用sockaddr_in将结构体内容设置好,然后再利用指针转化为sockaddr类型。

在这里插入图片描述
在这里插入图片描述1. sin_family:表示你要使用的地址结构类型,AF_INET是IPV4,AF_INET6是IPV6;
2. sin_port:网络字节序的端口号,因此要使用htons转换一下;
3. struct in_addr:一个结构体,里面有一个s_addr,要传入网络字节序的ip地址,因此要使用inet_pton函数;也可以使用第二种方法,宏,如INADDR_ANY(任意可用IP地址)

三.Socket编程函数

虽然说网络通信,socket成对出现,但实际上一次通信,会存在多个Socket。

当服务器端调用socket函数时会产生一个套接字,而accept函数会阻塞监听客户端连接,当有客户端连接的时候 该函数会返回一个新的套接字去和客户端连接,旧的套接字用以监听连接,因此一个socket通信模型中,会有三个套接字存在(客户端,服务端(由accept函数返回)和 监听socket)

通信模型图解:
在这里插入图片描述客户端:socket()创建套接字,connect()建立连接。
服务端:socket()创建套接字,bind()绑定ip和端口,listen()用来设置与服务端连接的客户端数量上线,accept()则用于结束客户端连接,没有请求时则阻塞在该函数上。

1.socket()

头文件: #include <sys/socket.h>
int socket(int domain, int type, int protocol); 创建一个套接字
参数:
domain:ip版本,AF_INET或AF_INET6
type:数据传输协议 SOCK_STREAM(流式协议,TCP)或SOCK_DGRAM(UDP)
protocol:默认传0,根据type来选择,SOCK_STREAM的代表协议是TCP,SOCK_DGRAM的是UDP

返回值:
成功:新套接字所对应的文件描述符;失败:-1 error

2.bind()
给socket通过sockaddr结构体绑定地址结构的函数

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

参数:
sockfd:socket文件描述符
addr:传入参数 (struct sockaddr *)&addr
addrlen:sizeof(addr) 地址结构的大小

返回值:
成功是0,失败-1 error

3.listen 函数

设置同时与服务器建立连接的上限数(同时进行3次握手的客户端数量);
int listen(int sockfd, int backlog);

参数:
sockfd:socket文件描述符
backlog:上限数值

返回值:
成功是0,失败-1 error

4.accept函数

阻塞等待客户端建立连接,成功的话 返回一个与客户端成功连接的socket文件描述符。
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

参数:
sockfd:socket的返回值
addr:传出参数,成功与服务器建立连接的那个客户端的地址结构(ip+端口)
addrlen:&clit_addr_len 传入传出参数,入:addr的大小 出:客户端addr的实际大小
socklen_t clit_addr_len = sizeof(addr); &clit_addr_len
返回值:
成功:能与服务器进行数据通信的 socket 对应的文件描述符
失败:-1 error

5.connect函数
客户端使用,使用现有的socket与服务器建立连接的函数

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数:
sockfd:socket的返回值
addr:服务器的地址结构
addrlen:服务器地址结构的长度
返回值:
成功:0 失败:-1 error

四.C-S模型通信案例实现

SERVER

  1 #include<iostream>2 #include<unistd.h>3 #include<fcntl.h>4 #include<sys/socket.h>5 #include<arpa/inet.h>6 #include<ctype.h>7 using namespace std;8 int main()9 {10   //1.socket11   int fd,sfd;12   fd = socket(AF_INET,SOCK_STREAM,0);//AF_INET ->IPV4  SOCK_STREAM -> TCP13   //2.bind14   struct sockaddr_in serv_addr,client_addr;15   serv_addr.sin_family = AF_INET ;16   serv_addr.sin_port = htons(9527);17   serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);  //宏,INADDR_ANY,系统选择可用ip        18   bind(fd,(struct sockaddr*)&serv_addr,sizeof(serv_addr));19   //3.listen20   listen(fd,128);21   //4.accept22   socklen_t clientaddr_len;  23   clientaddr_len = sizeof(client_addr);24   sfd =  accept(fd,(struct sockaddr*)&client_addr,(socklen_t*)&clientaddr_len);25   if(sfd == -1)                                                                                               26   {27      perror("accept error");28      exit(0);29   }30   //取出client_addr,利用inet_ntop和ntohs获取客户端地址结构:ip,端口等31   char client_ip[1024];32   cout<<"ip:"<<inet_ntop(AF_INET,&client_addr.sin_addr.s_addr,client_ip,sizeof(client_ip))<<endl;33   cout<<"port:"<<ntohs(client_addr.sin_port)<<endl;34   //5.连接完成,处理逻辑35   char buf[BUFSIZ];36  while(true)37  {38   int p = read(sfd,buf,sizeof(buf));//接收客户端数据39   write(STDOUT_FILENO,buf,p);40   for(int i = 0;i<p;i++)41     {42      buf[i] = toupper(buf[i]);43     }44   write(sfd,buf,p);  //发给客户端45  }46   return 0;47 }

CLIENT

#include<iostream>2 #include<unistd.h>3 #include<sys/socket.h>4 #include<arpa/inet.h>5 using namespace std;6 7 int main()8 {9   int fd;10   //1.socket11   fd = socket(AF_INET,SOCK_STREAM,0);12   //2.connet13   struct sockaddr_in sockaddr;14   sockaddr.sin_family = AF_INET;15   sockaddr.sin_port = htons(9527);16   inet_pton(AF_INET,"127.0.0.1",&sockaddr.sin_addr.s_addr);17 18   connect(fd,(struct sockaddr*)&sockaddr,sizeof(sockaddr));19   //3.通信20   char buf[BUFSIZ]; //409621   while(true)22   {   23     write(fd,"hello\n",sizeof("hello\n"));24     int ret = read(fd,buf,sizeof(buf));25     write(STDOUT_FILENO,buf,ret);26     sleep(1);27   }28   return 0;29 }

结果:
在这里插入图片描述

在这里插入图片描述

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com