linux网络通信-套接字

socket编程是网络常用的编程,我们通过在网络中创建socket关键字来实现网络间的通信。

1.TCP/IP协议
先来简单了解一下TCP/IP协议:

iso7层架构

应用层应用层不仅要提供应用进程所需要信息交换和远程操作,而且还要作为应用进程的用户代理,完成一些为进行语义上有意义的信息交换所必须的功能。
表示层用于处理两个通信系统间信息交换的表示方式,它包括数据格式变换、数据加密与解密、数据压缩与恢复等功能。
会话层组织同步的两个会话用户之间的对话,并管理数据的交换。
传输层向用户提供可靠的端到端服务,透明地传送报文。
网络层通过执行路由选择算法,为报文分组通过通信子网选择最适当的路径。
数据链层在物理层提供比特流传输服务的基础上,在通信实体之间建立数据链路连接,传送以帧为单位的数据
物理层物理层的主要功能是利用物理传输介质为数据链路层提供物理连接,以透明地传送比特流。

不同于iso7层架构,TCP/IP协议分为4层

应用层TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet 等等
传输层TCP,UDP
网络层IP,ICMP,OSPF,EIGRP,IGMP
数据链层SLIP,CSLIP,PPP,MTU

在tcp/ip协议中,tcp通过三次握手建立起一个tcp的链接
第一次握手:客户端尝试连接服务器,向服务器发送syn包,syn=j,客户端进入SYN_SEND状态等待服务器确认。
第二次握手:服务器接收客户端syn包并确认(ack=j+1),同时向客户端发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。

socket就在应用程序的传输层和应用层之间,设计了一个socket抽象层,传输层的底一层的服务提供给socket抽象层,socket抽象层再提供给应用层。

2.socket编程
(1)套接字的创建和销毁

使用socket函数创建一个套接字

#include<sys/socket.h>
int socket(int domain,int type,int protocol)

参数domain(域)确定通信的特性

描述
AF_INETIPV4因特网域
AF_INET6IPV6因特网域
AF_UNIXUNIX域
AF_UPSPEC未指定

参数type确定套接字的类型

类型描述
SOCK_DGRAM固定长度的,无连接的,不可靠的报文传递
SOCK_RAMIP协议的数据包接口
SOCK_SEQPACKET固定长度的,有序的,可靠的,面向连接的报文传递
SOCK_STREAM有序的,可靠的,双向的,面向连接的的字节流

参数protocol通常是0,表示为给定的域和套接字类型选定默认的协议。

调用套接字与调用open函数相似,均可获得用于I/O的文件描述符。当不再需要该文件符时,调用close函数来关闭对文件和或套接字的访问。

套接字的通信是双向的,可以采用函数shutdown来禁止一个套接字的I/O。

#include<sys/socket.h>
int   shutdown(int sockfd,int how);
                        返回值:成功返回0,出错返回-1

参数how:SHUT_RD关闭读端,SHUT_WR关闭写端

(2).字节序
大端字节序:最大字节地址出现最低有效字节
小端字节序:最小字节地址在最低有效字节
对于32位整数0X04030201,最高有效字节为04,最低为01。采用大端,则最大字节地址包含01。反之则最小字节地址包含01。

网络协议指定了字节序,对于TCP/IP协议栈,使用大端字节序。
应用程序需要在处理器字节序与网络字节序之间转换它们。对于TCP/IP应用程序,有四个用来转换的函数。

#include<arpa/inet.h>
uint32_t   htonl(uint32_t hostint32);     //host  to net  将主机字节序转换为32位网络字节序
uint16_t   htons(uint16_t hostint16);    //host  to net  将主机字节序转换为16位网络字节序
uint32_t   ntohl(uint32_t  netint32);     //net  to host  将网络字节序转换为32位主机字节序
uint16_t   ntohs(uint16_t netint16);    //net  to host  将网络字节序转换为16位主机字节序

(3)地址格式
在linux下,套接字地址用结构体sockaddr_in表示

struct sockaddr_in {
        short sin_family;                               //地址类型,ipv4或ipv6
        unsigned short sin_port;                   //端口
        struct  in_addr sin_addr;                   //ip地址
        unsigned char sin_zero[8];               //无意义,用来补全16位字节
};

(4)将套接字与地址关联
给服务器器关联上一个众所周知的地址。
使用函数bind来关联地址和套接字

#include<sys/socket.h>
int bind(int sockfd,const struct sockaddr *addr,socklen_t len)
返回值:成功返回0,失败返回-1

参数sockfd:是由socket()调用返回的并且未作连接的套接字描述符(套接字号)。参数addr :是赋给套接字s的本地地址(名字),其长度可变,结构随通信域的不同而不同。
参数len:表明了addr的长度。

(5)建立套接字连接
函数connect

#include<sys/socket.h>
int connect(int sockfd,const struct sockaddr *addr,socklen_t len)
返回值:成功返回0,失败返回-1

参数sockfd:是本地套接字描述符
参数addr是服务器地址
参数len:是服务器地址长度

函数listen
服务器调用listen函数来宣告它愿意接受请求

#include<sys/socket.h>
int listen(int sockfd,int backlog)
返回值:成功返回0,出错返回-1

参数sockfd:标识一个本地已建立、尚未连接的套接字号,服务器愿意从它上面接收请求。
参数backlog:提示系统该进程所要入队的未完成连接请求数量。队列满,服务器就会拒绝多余的连接请求。

函数accept
一旦服务器调用了lieten,使用的套接字就能接受连接请求。使用accept来获得连接请求,并建立连接。

#include<sys/socket.h>
int accept(int sockfd,struct sockaddr *restrict addr,socklen_t *restrict len)
返回值:成功返回描述符,出错返回-1

参数sockfd:本地套接字描述符
参数addr:客户端套接字地址

(6)数据传输
有6个函数用于数据传输,3个发送,3个接受。这里我就只写两个
函数send

#include<sys/socket.h>
ssize_t send(int sockfd,const void *buf,size_t nbytes,int flags);
返回值:成功返回字节数,失败返回-1

send函数与write函数相似,多了一个flags参数
总结一下这些flag

MSG_CONFIRM提供链路层反馈以保持地址映射有效
MSG_DONTROUTE勿将数据包路由出本地网络
MSG_DONTWAIT允许非阻塞操作
MSG_EOR如果协议支持,标记记录结束
MSG_MORE延迟发送数据包,允许写更多数据
MSG_NOSIGNAL在写无连接的套接字时不产生SIGPIPE信号
MSG_OOB如果协议支持,发送带外数据

函数recv

#include<sys/socket.h>
ssize_t recv(int sockfd,void *buf,size_t nbytes,int flags)

与read函数相似,多了一个flags控制标志

MSG_GMSG_CLOEXEC位unix域套接字上接收的文件描述符设置执行时关闭标志
MSG_DONTWAIT启用非阻塞操作
MSG_ERR接收错误消息作为辅助数据
MSG_OOB如果协议支持,获取带外数据
MSG_PEEK返回数据包内容,而不真正取走数据包
MSG_TRUNC即使数据包被截断,也返回数据包的长度
MSG_WAIT等待直到所有数据可用

服务器例程

#include <stdlib.h>
#include <sys/types.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>

int main()
{
	int sockfd;
	int clitfd
	struct sockaddr_in serv_addr;
	struct sockaddr_in clint_addr;
	memset(&serv_addr, 0, sizeof(serv_addr));
	char buffer[]="hello I'm a server"
	
	if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)       //创建服务器套接字
	{
		fprintf(stderr,"socket failed\n");
		exit(1);
	}
	
	serv_addr.sin_family=AF_INET;                        //初始化服务器IP,端口号
	serv_addr.sin_addr.s_addr=htonl(127.0.0.1);
	serv_addr.sin_port=htons(8080);
	
	bind(sockfd,(struct sockaddr*)&serv_addr,sizeof(serv_addr));         //将服务器与地址绑定
	listen(sockfd,10);                                                   //监听
	
	clitfd=accept(sockfd,(struct sockaddr*)&clint_addr,(socklen_t)(sizeof(clint_addr)));           //接受请求,返回客户端描述符      
	send(clitfd,buffer,sizeof(buffer),0);                                                          //向客户端写数据
	
	close(sockfd);                                                                                 //关闭套接字描述符
	close(clitfd);
	return 0;

}

客户端例程

#include <stdlib.h>
#include <sys/types.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>

int main()
{
	struct sockaddr_in clint_addr;
	struct sockaddr_in serv_addr;
	int  clintfd;
	char buffer[100];
	memset(&clint_addr, 0, sizeof(clint_addr));
	
	if((clintfd=socket(AF_INET,SOCK_STREAM,0))==-1)
	{
		fprintf(stderr,"socket failed\n");
		exit(1);
		
	}
	
	clint_addr.sin_family=AF_INET;
	clint_addr.sin_addr.s_addr = htonl(127.0.0.1);  
    clint_addr.sin_port = htons(1234); 
	
	if((connect(clintfd,(struct sockaddr_in*)&serv_addr,sizeof(serv_addr)))==-1)
	{
		fprintf(stderr,"connect failed\n");
		exit(1);
		
	}
	
	recv(clintfd,buffer,sizeof(buffer),0)
	
	close(clintfd);
	return 0;
	
}
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值