Internet Socket学习记录

Internet Socket学习记录

socket:即套接字,计算机之间约定好的一种通信方式,用于实现数据传输。
在linux系统中,一切都是以文件形式存在,网络连接也是文件。通过socket()函数打开连接,再使用write()和read()分别对远程计算机进行数据的写和读。
在window系统中,socket就是一个网络连接,且有专门针对 socket 而设计的数据传输函数。

一、套接字类型

根据数据传输类型可以分为 流格式套接字数据报格式套接字
1、 流格式套接字

SOCK_STREAM,是一种面向连接的、可靠的、双向的通信数据流,支持重发。
是你吗,TCP?
没戳!流格式套接字使用了TCP协议,使得数据在传输过程中能够保持有序、稳定,且数据的发送和接收不同步。发送端可以一次性将数据发出,也可以分批次发出,接收端同样可以根据自己的节奏将数据进行读取。可以这么灵活的原因就是套接字中包含了缓冲区,只要发送的数据没有把缓冲区占满,接收端就可以看情况决定啥时候读取数据,读多少数据。
应用场景:http协议

2、数据报格式套接字

SOCK_DGRAM,基于UDP协议,数据传输唯快不破,每次传输的数据大小有限制(能传,只能传一点点O.O),数据在传输过程中可能丢失或损坏,数据的发送和接收同步。
典型应用:视频、语音通话等强调实时性的场景。

二、套接字在网络模型中的位置

TCP和UDP协议都是传输层协议,再往上就是应用层,socket就是处于二者之间的这么一个位置。说白了,socket就是接口,让用户进程能够凭借接口使用TCP/UDP协议。而在网络中,如何找到对应的网络进程呢?通常使用三元组(主机ip+协议+端口)来定位网络进程。

在这里插入图片描述

三、套接字实现客户端和服务端的通信过程

在这里插入图片描述
其中,建立连接的过程如下图所示:
在这里插入图片描述
服务器监听是否有收到连接请求,当一个客户端发起连接connect()后,会向服务器发送SYN J包,并进入阻塞状态;
服务器收到SYN J包后,调用accept()向客户端发送 SYN K,ACK J+1,然后阻塞;
客户端接收到SYN K,ACK J+1后,connect返回,结束阻塞,并回复 ACK K+1 给服务器;
服务器接收到回复后,accept返回,三次握手过程结束。

886的过程如下图,以客户端先发起关闭连接请求为例。

在这里插入图片描述

四、相关函数

创建套接字socket()
函数定义:int socket (int domain, int type, int protocol);
函数返回:socket描述符,在进行bind()之前,该描述符存在于协议族空间中。
参数含义:
1.domain:协议族,决定了socket的地址类型,常用的有AF_INET( 32位IPV4地址 + 16位端口号 )、AF_INET6、AF_UNIX(绝对路径)、AF_ROUTE等。
2.type:socket的类型,如SOCK_STREAM、SOCK_DGRAM、SOCK_PACKET等。
3.protocol:指定的协议,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、SOCK_SEQPACKET等。
ps:socket()创建后默认为主动型。

地址绑定bind()
函数定义:int bind (int sockfd, const struct sockaddr *addr ,socklen_t addrlen);
函数返回:socket描述符,在进行bind()之前,该描述符存在于协议族空间中。
参数含义:
1.sockfd:socket()的返回值,唯一标识了一个socket,bind()函数的作用就是给这个描述字绑定一个名字。
2. const struct sockaddr *addr:和sockfd所要绑定的协议地址相关,如sockaddr_in 、sockaddr_in6 、sockaddr_un 等。
3.addrlen:地址长度。
ps:1). 通常服务器的地址是通过bind()来绑定生成,给客户端提供服务;而客户端则通常是在请求连接connect()时,由系统自动分配一个。
2). 进行地址绑定的时候需考虑字节序的问题,主机字节序需要和网络字节序(大端模式)一致。

监听listen()
函数定义:int listen (int sockfd, int backlog);
函数返回:监听是否成功,-1失败。
参数含义:
1.sockfd:socket描述字。
2. backlog:对应socket可以排队的最大连接个数。
ps:可将socket()变为被动型。

请求连接connect()
函数定义:int listen (int sockfd,const struct sockaddr *addr,socklen_t addrlen);
函数返回:连接是否成功,-1失败。
参数含义:
1.sockfd:客户端socket描述字。
2. const struct sockaddr *addr:服务端socket地址。
3. 地址长度。

accept()
函数定义:int accept (int sockfd,const struct sockaddr *addr,socklen_t addrlen);
函数返回:由内核自动生成的一个全新的描述字,代表与返回客户的TCP连接。
参数含义:
1.sockfd:服务器 socket描述字。
2. const struct sockaddr *addr:客户端socket地址。
3. 地址长度。

I/O操作函数
1.read()/write()
2.recv()/send()
3.readv()/writev()
4.readv()/writev()
5.recvfrom()/sendto()

关闭连接close()
函数定义:int close(int sockfd);
函数返回:
参数含义:sockfd:发起断开端的socket描述字。

五、服务端,客户端实现

服务器端:

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

#define MAXLINE 4096

int main(int argc,char** argv)
{
	
	int listenfd,connfd;
	struct sockaddr_in servaddr;
	char buff[MAXLINE];
	int n;

	if((listenfd=socket(AF_INET,SOCK_STREAM,0))==-1){
		printf("create socket error:%s(errno:%d)\n",strerror(errno),errno);
		exit(0);
	}
	
	memset(&servaddr,0,sizeof(servaddr));
	servaddr.sin_family=AF_INET;
	servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
	servaddr.sin_port=htons(6666);

	if(bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr))==-1){
		
		printf("bind socket error:%s(errno:%d)\n",strerror(errno),errno);
		exit(0);
	}

	if(listen(listenfd,10)==-1){

		printf("listen socket error:%s(errno:%d)\n",strerror(errno),errno);
		exit(0);
	}

	printf("=========waitting for client's request===========");

	while(1){
		if((connfd = accept(listenfd,(struct sockaddr*)NULL,NULL))==-1){
		printf("accept socket error:%s(errno:%d)\n",strerror(errno),errno);
		continue;
		}
	n = recv(connfd,buff,MAXLINE,0);
	buff[n]='\0';
	printf("recv msg from client:%s\n",buff);
	close(connfd);

	}
	close(listenfd);

	return 0;
}

	客户端
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<unistd.h>
#include<arpa/inet.h>

#define MAXLINE 4096

int main(int argc,char** argv)
{
	
	int sockfd,n;
	struct sockaddr_in servaddr;
	char recvline[MAXLINE],sendline[MAXLINE];
	
	if( argc !=2){
	printf("usage:./client<ipaddress>\n");
	exit(0);
	}

	if(( sockfd = socket(AF_INET,SOCK_STREAM,0))<0){
	printf("create socket error:%s(errno:%d)\n",strerror(errno),errno);
	exit(0);
	}
	
	memset(&servaddr,0,sizeof(servaddr));
	servaddr.sin_family=AF_INET;
	servaddr.sin_port=htons(6666);
	if(inet_pton(AF_INET,argv[1],&servaddr.sin_addr)<=0){

	printf("inet_pton error for %s\n",argv[1]);
	exit(0);
	}
	
	
	if( connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr))<0){
	printf("connect error:%s(errno:%d)\n",strerror(errno),errno);
	exit(0);
	}

	printf("send msg to server:\n");
	fgets(sendline,MAXLINE,stdin);
	if(send(sockfd,sendline,strlen(sendline),0)<0){

	printf("send error:%s(errno:%d)\n",strerror(errno),errno);
	exit(0);
	}

	close(sockfd);

	return 0;
}

客户端程序的输入为服务器所在的IP地址

本文参考:https://blog.csdn.net/pashanhu6402/article/details/96428887

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值