Linux系统编程--网络套接字Socket函数详解+互发消息聊天例子+扩充知识点

文章主要分为三大部分,
①预备知识
②函数详解
③代码实例+解释。
内容过多,可直接根据目录进行翻阅查看需要的部分。
目录在有左下角↙↙↙

套接字的概念

Linux当中的一种文件类型,伪文件,不占用存储空间,可进行IO操作,可间接看做文件描述符使用

预备知识(代码中会使用)

①什么是ip、port(端口)

ip:在网络环境中,唯一表示一台主机
Port:在主机中唯一表示一个进程
通俗来将ip当作地址port当作门牌号

②通信方式有哪几种,Socket有什么区别,为什么

通信方式:信号量 管道 消息队列 共享内存 套接字
**区别:**套接字支持网络上两台以上的设备进行通信,其他其中只能在一台设备上
原因: Socket有双个缓冲区在这里插入图片描述

③如何利用套接字进行读写交流

(1)服务器通过accept函数返回值可以获得客服端的套接字,我们就可以对客服端进行IO(读写)操作
(2)客服端通过connect函数的第一个参数传出服务器套接字,我们就可以对服务器进行IO(读写)操作
(3)将套接字看做文件描述符使用,更好理解

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

④如何查看本机ip

执行语句

ifconfig

上面的是网络ip地址 下面的是测试ip(本机可用)

⑤网络字节序和主机字节序

网络字节序 :通常采用大端对齐(一种将高序字节储存在起始地址)
主机字节序 :通常采用小段对齐(一种将低序字节存储在起始地址)

⑥如何实现网络字节序和主机字节序的转换(转换ip地址)

点分十进制:127.0.0.1 本机测试ip
方法一:点分十进制->无符号整形->网络字节序

#include <arpa/inet.h>
uint32_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表示network,l表示32位长整数,s表示16位短整数。
如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回,如果主机是大端字节序,这些函数不做转换,将参数原封不动地返回

方法二:点分十进制->网络字节序

#include <arpa/inet.h>
int inet_pton(int af, const char *src, void *dst);

参数:地址族协议+点分十进制ip+源地址(保存)

⑦sockaddr数据结构

strcut sockaddr 很多网络编程函数诞生早于IPv4协议,那时候都使用的是sockaddr结构体,为了向前兼容,现在sockaddr退化成(void *)的作用,传递一个地址给函数,至于这个函数是sockaddr_in还是sockaddr_in6,由地址族确定,然后函数内部再强制类型转化为所需的地址类型
在这里插入图片描述

⑧TCP/UDP协议 长链接 短链接

协议的概念:是为进行网络中的数据交换而建立的规则、规则或约定
TCP:是点对点通信 更可靠(三次握手)确定连接上才发送包

UDP: 一个是不可靠、不定时 发送信息不需要确认能不能接受到
长链接(实时通信 lol)
短链接(实时性不高 占用资源不多 卡牌游戏)
总结:所以我们Socket进行网络通信采用TCP/IP协议

⑨TCP/IP协议和http ftp socket 协议的关系

(1)TCP/IP为协议栈(原料

(2)http(web网页)ftp(文件传输) socket(网络)都是协议(产物

⑩TCP通讯中的三次握手和四次握手

在这里插入图片描述
比喻三次握手:(连接和传输)
第一次A:微笑
第二次B :微笑+握手
第三次A: 握手

比喻四次握手:(断开连接)
第一次A:请求关闭连接
第二次B:应答请求
第三次B:我也请求关闭连接
第四次A:应答请求

Socket模式流程图

在这里插入图片描述

网络套接字之函数详解

—> 头文件 <—

   #include <sys/types.h>          /* See NOTES */
   #include <sys/socket.h>

(1) 函数详解之socket()函数
函数作用:
用于服务器和客户端,创建套接字,返回一个可操作的文件描述符
参数使用:

int socket(int domain,int type,int protocal);
参数一:表示ip地址类型,常用的有两种
其中AF_INET表示IPv4地址,比如127.0.0.1,这是一个本机测试ip
其中AF_INET表示IPv6地址,比如2001:3CA1:10F:1A:121B:0:0:10
        
参数二:表示数据传输方式/套接字类型,常见两种
 SOCK_STREAM(流格式套接字/面向连接的套接字)
 SOCK_DGRAM (数据报套接字/无连接的套接字)
参数三:表示传输协议,理论上前两个参数已经可以推演出采用哪种协议
主要是为了解决,两种不同的协议支持同一种地址类型和数据型。如果我们不指明使用哪种协议,操作系统是没办法自动推演的。
如果两种情况只有一个协议满足条件,可以将protocol 的值设为 0,系统自动推演出采用哪种协议
返回值:返回一个套接字(文件描述符fd)

(2)函数详解之bind()函数
函数作用:
用于服务器,给sockfd套接字绑上本机地址和使用端口,确定了服务器的身份
参数使用:

Int bind(int sockfd,const struct sockaddr*addr,socklen_t addrlen);
参数一:套接字的fd(文件描述符),socket()函数的返回值
参数二:结构体 ip+port(端口)
struct sockaddr_in{ (涉及强制转换sockaddr_in ->sockaddr  参考)
	short int sin_family;              //地址族
	unsigned short int sin_port;       //端口号
	struct in_addr sin_addr;           //IP地址
}
struct in_addr {
     __be32 s_addr;
};

参数三:结构体的字节长度
返回值:判断成功失败

(3)函数详解值之listen()函数
函数作用:
用于服务器,使socket处于监听模式,监听时候有客户端连接,并放入队列
参数使用:

int listen(int sockfd,int backlog);
参数一:bind绑定ip和端口的套接字
参数二:请求链接客户端队列的最大存放数目
返回值:判断成功失败

(4)函数详解值之accept()函数
函数作用:
用于服务器,接收一个客户端的连接请求,并返回连接客户端的套接字便于IO操作,如果没有客户连接会阻塞等待。
参数使用:

int accept(int sockfd,struct sockaddr*addr,socklen_t*addrlen)
参数一:服务器的套接字(也叫监听套接字),表明了自己的身份
参数二:传出参数,跟我建立连接的客户端的结构体(内含客户端ip+端口)
参数三: 结构体长度的指针  &sizeof()
返回值:连接客户端的套接字

(5)函数详解值connect()函数
函数作用:
用于客户端,函数可以和自动与远端服务器建立连接
参数使用:

int connect(int sockfd,struct sockaddr*serv_addr,int addrlen)
参数一:传出参数,传出连接成功服务器的套接字(文件描述符)便于在客户端对服务器进行IO操作
参数二:绑定我要链接服务器的结构体(需要初始化绑上ip和断口),表明目的
参数三:结构体的长度

简单一对一互发消息实例代码+详细解释

服务器代码

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#define SER_PORT 8000
int main(void)
{
	int sockfd,connfd;//
	int len;
	char wbuf[1024];
	char rbuf[1024];
	struct sockaddr_in serveraddr,clientaddr; //两个结构体 一个用于绑定身份到套接字  一个用于接收客服端的结构体
	//1.创建监听套接字
	sockfd = socket(AF_INET,SOCK_STREAM,0);
	//2.bind(通信需要套接字 把家的地址 门牌号绑上去 ip和端口)
	bzero(&serveraddr,sizeof(serveraddr)); //类似memset 清空结构体
	//地址族协议,选择IPV4
	serveraddr.sin_family = AF_INET;     //属于ipv4还是ipv6
	//IP地址 本机任意可用ip地址
	serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
	serveraddr.sin_port = htons(SER_PORT);//端口号	
	bind(sockfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr));
	//3.监听 和服务器连接的总和
	listen(sockfd,128);	
	int size = sizeof(clientaddr);
	//4.accept 阻塞监听 客服端链接的请求
	connfd = accept(sockfd,(struct sockaddr *)&clientaddr,&size); 	
	//输出客服端的ip和端口
	char ipstr[128];
	printf("client ip%s ,port %d\n",inet_ntop(AF_INET,&clientaddr.sin_addr.s_addr,ipstr,sizeof(ipstr)),
		ntohs(clientaddr.sin_port));
	//5.处理客户端请求
	//读和写
	while(1)
	{
		memset(wbuf,0,sizeof(wbuf));//清空
		memset(rbuf,0,sizeof(wbuf));	
		//接收消息	
		int len = read(connfd,rbuf,sizeof(rbuf));
		if(len==0)//表示断开连接
		{
			printf("client is close....\n");
		}
		printf("receive from client:%s",rbuf);
		//发送消息
		printf("send to client:");
		fgets(wbuf,sizeof(wbuf),stdin);
		write(connfd,wbuf,strlen(wbuf));	
	}
	close(connfd);
	close(sockfd);
	return 0;
}

客户端代码

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#define SER_PORT 8000
int main(void)
{
	int sockfd;
	struct sockaddr_in serveraddr;
	int len;
	char wbuf[1024],rbuf[1024];
	//1、socket 通信用套接字,创建一个sockfd
	sockfd = socket(AF_INET,SOCK_STREAM,0);
    char ipstr[]="127.0.0.1";
	//2、编辑要连接的服务器地址,并绑定
	bzero(&serveraddr,sizeof(serveraddr));
	serveraddr.sin_family = AF_INET;            //设置地址族协议
	serveraddr.sin_port = htons(SER_PORT);      //设置端口号
	inet_pton(AF_INET,ipstr,&serveraddr.sin_addr.s_addr);//设置ip地址   点分十进制转成网络字节序
	//2、connect 连接服务器 sockfd传出服务器套接字
	connect(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr));
	//3、读写
	while(1)
	{
		memset(wbuf,0,sizeof(wbuf));
		memset(rbuf,0,sizeof(rbuf));
		//发送消息
		printf("send to server:");
		fgets(wbuf,sizeof(wbuf),stdin);
		write(sockfd,wbuf,strlen(wbuf));
		
		//接收消息
		len=read(sockfd,rbuf,sizeof(rbuf));
		if(len==0)//表示断开连接
		{
			printf("server is close....\n");
		}
		printf("receive from server:%s",rbuf);
		
		
	}
	//4、close
	close(sockfd);
	return 0;
}

测试 结果
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_44972997/article/details/107694504