(自用)Socket编程

套接字概念(太长不看,看总结摘要)

Socket中文意思是“插座”,在Linux环境下,用于表示进程x间网络通信的特殊文件类型。本质为内核借助缓冲区形成的伪文件。

既然是文件,那么理所当然的,我们可以使用文件描述符引用套接字。Linux系统将其封装成文件的目的是为了统一接口,使得读写套接字和读写文件的操作一致。区别是文件主要应用于本地持久化数据的读写,而套接字多应用于网络进程间数据的传递。

在TCP/IP协议中,“IP地址+TCP或UDP端口号”唯一标识网络通讯中的一个进程。“IP地址+端口号”就对应一个socket。欲建立连接的两个进程各自有一个socket来标识,那么这两个socket组成的socket pair就唯一标识一个连接。因此可以用Socket来描述网络连接的一对一关系。

套接字通信原理如下图所示:

在网络通信中,套接字一定是成对出现的。一端的发送缓冲区对应对端的接收缓冲区。我们使用同一个文件描述符索发送缓冲区和接收缓冲区。

总结摘要

1.Linux系统中,套接字=一种文件,编程时有相应的文件标识符(fd)

2.用于网络进程间(一般是不同电脑的应用)的数据传输

3.IP地址+端口号”就对应一个socket。

4.上面的图片

Socket通信创建流程图

网络字节序

两种字节序

无论机器是小端字节序还是大端字节序

网络上发送数据一般是先发送低地址再发送高地址

网络上接收数据一般也是先低后高

收发数据都是先低后高

当小端字节序机器向大端字节序机器发送数据时,会发生问题:

      

TCP\IP协议规定,网络数据流采用大端字节序

也就是说,无论主机是小端字节序存储,还是大端字节序存储,在TCP\IP协议中发送主机和接收主机都看成是大端字节序。

小端主机参与接收发送数据需要进行字节转换,而大端主机不用

为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用htonx库函数做网络字节序和主机字节序的转换。

总结:

TCP\IP协议是默认规定发收双方是大端主机,所以发收双方需要htonx函数进行字节转换(使得小端主机自己转换,大端主机不变)

转换相关函数 htonx ntohx

eg:回声服务器echo_server.c

项目需求

Socket通信三要素

1)通信的目的地址 (IP地址)

2)使用的端口号 (服务器用不同的端口号区分不同的应用) http 80 smtp 25

3)使用的传输层协议(TCP、UDP)

Socket通信模型

sockaddr数据结构

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

sockaddr与sockaddr_in的结构差异:

sockaddr_in结构对于sockaddr的14字节地址数据进行了细分,如上图所示

分为16位端口号、32位IP地址、8字节填充

代码定义

PS:

1.in_addr中存储的ip地址是32位整型的,使用时可能要转换为字符串形式

eg:回音服务器echo_server.c中 

其中AF_INET表示使用IPV4协议,INADDR_ANY为0.0.0.0表示本机 

贴标签(调用bind函数)的时候需要强制将sockaddr_in强转为sockaddr

bzero函数——清空标签

头文件:#include<string.h>

函数原型:

作用:

在定义sockaddr_in结构体后用来清空里面的内容

代码示例:

struct sockaddr_in server_addr;//代表标签
bzero(&server_addr,sizeof(struct sockaddr_in));

IP地址转换函数 inet_xxxx

 

 其中p代表的是字符串形式的ip地址,n代表网络字节序

这里的size指的是dst的size 

代码示例:
#include<stdio.h>
#include<string.h>
#include<arpa/inet.h>

int main()
{
	char ip[] = "2.3.4.5";
	char server_ip[64];
	struct sockaddr_in server_addr;
	inet_pton(AF_INET,ip, &server_addr.sin_addr.s_addr);
	printf("s_addr : %x\n",server_addr.sin_addr.s_addr);//网络字节序
	printf("s_addr from net to host : %x\n",ntohl(server_addr.sin_addr.s_addr));//主机字节序
	inet_ntop(AF_INET, &server_addr.sin_addr.s_addr,server_ip,64);
	printf("server ip : %s \n",server_ip);
	return 0;
}

输出结果:

 

INADDR_ANY——0.0.0.0 本机

宏定义,真实值为0,表示本机

server_addr.sin_addr.s_addr = htonl(INADDR_ANY);

表示服务器监听本机所有IP地址

socket函数——创建邮箱

代码定义:

代码示例:

1.服务器连接前创建socket

2.accpet接收客户端请求形成socket

服务端相关函数

bind函数——贴标签

功能:

服务器端程序用bind函数来绑定一个固定的网络地址和端口号。

代码定义:

代码示例:

sock = socket(AF_INET,SOCK_STREAM,0);
//2.清空标签,写上地址和端口号
bzero(&server_addr,sizeof(server_addr));

server_addr.sin_family = AF_INET;//悬崖边则协议族IPV4
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);//监听本地所有IP地址
server_addr.sin_port = htons(SERVER_PORT);//绑定端口号

//将标签铁道收信的信箱上
bind(sock, (struct sockaddr*)&server_addr, sizeof(server_addr));

首先将整个结构体清零,然后设置地址类型为AF_INET,网络地址为INADDR_ANY,这个宏表示本地的任意IP地址,因为服务器可能有多个网卡,每个网卡也可能绑定多个IP地址,这样设置可以在所有的IP地址上监听,直到与某个客户端建立了连接时才确定下来到底用哪个IP地址,端口号为6666。

 

listen函数——等待来信

代码定义:

 

accept函数

功能:

三次握手完成后,服务器调用accept()接受连接,如果服务器调用accept()时还没有客户端的连接请求,就阻塞等待直到有客户端连接上来。

代码定义:

产生的是一个客户端socket

代码示例:

struct sockaddr_in client;
socklen_t client_addr_len;
client_addr_len = sizeof(client);
client_sock = accept(sock,(struct sockaddr*)&client,&client_addr_len);
//sock为之前创建的服务端连接前socket

客户端相关函数

connect函数

函数定义:

通用函数

出错处理函数

可以对strerror函数进行进一步封装:

 

项目练习

  • 28
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值