网络协议(二) Socket
网络协议(二) Socket
- 6. socket
- 6.1 socket 简介
- 6.2 socket 特点
- 6.3 socket 对比 HTTP
- 6.4 socket 建立连接过程
- 6.5 socket 建立连接代码实现
- 6.5.1 客户端代码
- 6.5.2 服务器端代码
6. socket
6.1 socket 简介
socket是为了实现以上的通信过程而建立成来的通信管道,其真实的代表是客户端和服务器端的一个通信进程,双方进程通过socket进行通信,而通信的规则采用指定的协议。
socket只是一种连接模式,不是协议,socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,我们才能使用TCP/IP协议。tcp、udp,简单的说(虽然不准确)是两个最基本的协议,很多其它协议都是基于这两个协议如,http就是基于tcp的,.用socket可以创建tcp连接,也可以创建udp连接,这意味着,用socket可以创建任何协议的连接,因为其它协议都是基于此的。
6.2 socket 特点
socket | 优点 | 缺点 |
---|---|---|
socket | 传输数据为字节级,传输数据可自定义,数据量小(对于手机应用讲:费用低) | 需对传输的数据进行解析,转化成应用级的数据 |
socket | 传输数据时间短,性能高 | 对开发人员的开发水平要求高 |
socket | 适合于客户端和服务器端之间信息实时交互 | 相对于Http协议传输,增加了开发量 |
socket | 可以加密,数据安全性强 |
6.3 socket 对比 HTTP
6.4 socket 建立连接过程
在前面已经讲解过了TCP的三次握手建立连接,四次挥手断开连接的过程。
这里socket的建立连接和断开连接起始就是这个过程的代码实现。回顾tcp三次握手过程
第一次握手:客户端尝试连接服务器,向服务器发送syn包,syn=j,客户端进入SYN_SEND状态等待服务器确认
第二次握手:服务器接收客户端syn包并确认(ack=j+1),同时向客户端发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手
流程图:
- 根据tcp的三次握手,socket也定义了三次握手,也许是参考tcp的三次握手,一些计算机大神们画出了socket的三次握手的模型图
- socket建立连接客户端流程
(1)打开一通信通道,并连接到服务器所在主机的特定端口;
(2)向服务器发服务请求报文,等待并接收应答;继续提出请求…
(3)请求结束后关闭通信通道并终止。
- socket建立连接服务器端流程
(1)打开一通信通道并告知本地主机,它愿意在某一公认地址上的某端口(如FTP的端口可能为21)接收客户请求;
(2)等待客户请求到达该端口;
(3)接收到客户端的服务请求时,处理该请求并发送应答信号。接收到并发服务请求,要激活一新进程来处理这个客户请求(如UNIX系统中用fork、exec)。新进程处理此客户请求,并不需要对其它请求作出应答。服务完成后,关闭此新进程与客户的通信链路,并终止。
(4)返回第(2)步,等待另一客户请求。
(5)关闭服务器
6.5 socket 建立连接代码实现
6.5.1 客户端代码
- swift代码
dispatch_sync(dispatch_get_global_queue(0, 0), ^{// 处理耗时操作的代码块...// 创建socket/*1.AF_INET: ipv4 执行ip协议的版本2.SOCK_STREAM:指定Socket类型,面向连接的流式socket 传输层的协议3.IPPROTO_TCP:指定协议。 IPPROTO_TCP 传输方式TCP传输协议返回值 大于0 创建成功*/_clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);// 建立连接(与服务器)/*终端里面 命令模拟服务器 netcat nc -lk 12345参数一:套接字描述符参数二:指向数据结构sockaddr的指针,其中包括目的端口和IP地址参数三:参数二sockaddr的长度,可以通过sizeof(struct sockaddr)获得返回值 int -1失败 0 成功*/struct sockaddr_in addr;/* 填写sockaddr_in结构*/addr.sin_family = AF_INET;addr.sin_port=htons(8080);addr.sin_addr.s_addr = inet_addr("192.168.0.99");int connectResult = connect( _clientSocket, (const struct sockaddr *)&addr, sizeof(addr));// 发送数据(到服务器)/*第一个参数指定发送端套接字描述符;第二个参数指明一个存放应用程式要发送数据的缓冲区;第三个参数指明实际要发送的数据的字符数;第四个参数一般置0。成功则返回实际传送出去的字符数,失败返回-1,*/char * str = "itcast";ssize_t sendLen = send( _clientSocket, str, strlen(str), 0);// 接送数据(从服务器)/*第一个参数socket第二个参数存放数据的缓冲区第三个参数缓冲区长度。第四个参数指定调用方式,一般置0返回值 接收成功的字符数*/char *buf[1024];ssize_t recvLen = recv( _clientSocket, buf, sizeof(buf), 0);NSLog(@"---->%ld",recvLen);});
// [self test];
}
- c 语言实现代码
#include<stdio.h>
#include<stdlib.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<string.h>
#include<unistd.h>
#define BUFFER_SIZE 1024 int main(int argc, const char * argv[])
{ struct sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_port = htons(11332); server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); bzero(&(server_addr.sin_zero), 8); int server_sock_fd = socket(AF_INET, SOCK_STREAM, 0); if(server_sock_fd == -1) { perror("socket error"); return 1; } char recv_msg[BUFFER_SIZE]; char input_msg[BUFFER_SIZE]; if(connect(server_sock_fd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_in)) == 0) { fd_set client_fd_set; struct timeval tv; while(1) { tv.tv_sec = 20; tv.tv_usec = 0; FD_ZERO(&client_fd_set); FD_SET(STDIN_FILENO, &client_fd_set); FD_SET(server_sock_fd, &client_fd_set); select(server_sock_fd + 1, &client_fd_set, NULL, NULL, &tv); if(FD_ISSET(STDIN_FILENO, &client_fd_set)) { bzero(input_msg, BUFFER_SIZE); fgets(input_msg, BUFFER_SIZE, stdin); if(send(server_sock_fd, input_msg, BUFFER_SIZE, 0) == -1) { perror("发送消息出错!\n"); } } if(FD_ISSET(server_sock_fd, &client_fd_set)) { bzero(recv_msg, BUFFER_SIZE); long byte_num = recv(server_sock_fd, recv_msg, BUFFER_SIZE, 0); if(byte_num > 0) { if(byte_num > BUFFER_SIZE) { byte_num = BUFFER_SIZE; } recv_msg[byte_num] = '\0'; printf("服务器:%s\n", recv_msg); } else if(byte_num < 0) { printf("接受消息出错!\n"); } else { printf("服务器端退出!\n"); exit(0); } } } //} } return 0;
}
6.5.2 服务器端代码
#include<stdio.h>
#include<stdlib.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<string.h>
#include<unistd.h>
#define BACKLOG 5 //完成三次握手但没有accept的队列的长度
#define CONCURRENT_MAX 8 //应用层同时可以处理的连接
#define SERVER_PORT 11332
#define BUFFER_SIZE 1024
#define QUIT_CMD ".quit"
int client_fds[CONCURRENT_MAX];
int main(int argc, const char * argv[])
{ char input_msg[BUFFER_SIZE]; char recv_msg[BUFFER_SIZE]; //本地地址 struct sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_port = htons(SERVER_PORT); server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); bzero(&(server_addr.sin_zero), 8); //创建socket int server_sock_fd = socket(AF_INET, SOCK_STREAM, 0); if(server_sock_fd == -1) { perror("socket error"); return 1; } //绑定socket int bind_result = bind(server_sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)); if(bind_result == -1) { perror("bind error"); return 1; } //listen if(listen(server_sock_fd, BACKLOG) == -1) { perror("listen error"); return 1; } //fd_set fd_set server_fd_set; int max_fd = -1; struct timeval tv; //超时时间设置 while(1) { tv.tv_sec = 20; tv.tv_usec = 0; FD_ZERO(&server_fd_set); FD_SET(STDIN_FILENO, &server_fd_set); if(max_fd <STDIN_FILENO) { max_fd = STDIN_FILENO; } //printf("STDIN_FILENO=%d\n", STDIN_FILENO); //服务器端socket FD_SET(server_sock_fd, &server_fd_set); // printf("server_sock_fd=%d\n", server_sock_fd); if(max_fd < server_sock_fd) { max_fd = server_sock_fd; } //客户端连接 for(int i =0; i < CONCURRENT_MAX; i++) { //printf("client_fds[%d]=%d\n", i, client_fds[i]); if(client_fds[i] != 0) { FD_SET(client_fds[i], &server_fd_set); if(max_fd < client_fds[i]) { max_fd = client_fds[i]; } } } int ret = select(max_fd + 1, &server_fd_set, NULL, NULL, &tv); if(ret < 0) { perror("select 出错\n"); continue; } else if(ret == 0) { printf("select 超时\n"); continue; } else { //ret 为未状态发生变化的文件描述符的个数 if(FD_ISSET(STDIN_FILENO, &server_fd_set)) { printf("发送消息:\n"); bzero(input_msg, BUFFER_SIZE); fgets(input_msg, BUFFER_SIZE, stdin); //输入“.quit"则退出服务器 if(strcmp(input_msg, QUIT_CMD) == 0) { exit(0); } for(int i = 0; i < CONCURRENT_MAX; i++) { if(client_fds[i] != 0) { printf("client_fds[%d]=%d\n", i, client_fds[i]); send(client_fds[i], input_msg, BUFFER_SIZE, 0); } } } if(FD_ISSET(server_sock_fd, &server_fd_set)) { //有新的连接请求 struct sockaddr_in client_address; socklen_t address_len; int client_sock_fd = accept(server_sock_fd, (struct sockaddr *)&client_address, &address_len); printf("new connection client_sock_fd = %d\n", client_sock_fd); if(client_sock_fd > 0) { int index = -1; for(int i = 0; i < CONCURRENT_MAX; i++) { if(client_fds[i] == 0) { index = i; client_fds[i] = client_sock_fd; break; } } if(index >= 0) { printf("新客户端(%d)加入成功 %s:%d\n", index, inet_ntoa(client_address.sin_addr), ntohs(client_address.sin_port)); } else { bzero(input_msg, BUFFER_SIZE); strcpy(input_msg, "服务器加入的客户端数达到最大值,无法加入!\n"); send(client_sock_fd, input_msg, BUFFER_SIZE, 0); printf("客户端连接数达到最大值,新客户端加入失败 %s:%d\n", inet_ntoa(client_address.sin_addr), ntohs(client_address.sin_port)); } } } for(int i =0; i < CONCURRENT_MAX; i++) { if(client_fds[i] !=0) { if(FD_ISSET(client_fds[i], &server_fd_set)) { //处理某个客户端过来的消息 bzero(recv_msg, BUFFER_SIZE); long byte_num = recv(client_fds[i], recv_msg, BUFFER_SIZE, 0); if (byte_num > 0) { if(byte_num > BUFFER_SIZE) { byte_num = BUFFER_SIZE; } recv_msg[byte_num] = '\0'; printf("客户端(%d):%s\n", i, recv_msg); } else if(byte_num < 0) { printf("从客户端(%d)接受消息出错.\n", i); } else { FD_CLR(client_fds[i], &server_fd_set); client_fds[i] = 0; printf("客户端(%d)退出了\n", i); } } } } } } return 0;
}
网络协议(二) Socket相关推荐
- 网络协议 11 - Socket 编程(下):眼见为实耳听为虚
网络协议 11 - Socket 编程(下):眼见为实耳听为虚 原文:网络协议 11 - Socket 编程(下):眼见为实耳听为虚 系列文章传送门: 网络协议 1 - 概述 网络协议 2 - IP ...
- 网络协议之:socket协议详解之Datagram Socket
文章目录 简介 什么是Datagram Socket 使用socat来创建UDP服务 使用ss命令来监控Datagram Sockets 使用nc建立和UDP Socket的连接 总结 简介 上一篇文 ...
- 网络协议之:socket协议详解之Socket和Stream Socket
文章目录 简介 Socket是什么 Stream Socket 使用socat创建一个TCP服务器 使用ss检查TCP连接 使用nc连接socket 总结 简介 不管是在普通的网络编程中还是在nett ...
- 网络协议之:socket协议详解之Unix domain Socket
文章目录 简介 什么是Unix domain Socket 使用socat来创建Unix Domain Sockets 使用ss命令来查看Unix domain Socket 使用nc连接到Unix ...
- 网络协议之socket协议详解之Unix domain Socket
简介 之前的文章我们讲到了Socket中的Stream Socket和Datagram Socket,和有连接的Stream Socket不同,Datagram Socket是无连接的.有连接的Str ...
- 网络协议 11 - Socket 编程(下)
之前我们基本了解了网络通信里的大部分协议,一直都是在"听"的过程.很多人都会觉得,好像看懂了,但关了页面回忆起来,好像又什么都没懂.这次咱们就"真枪实弹"的码起 ...
- 网络协议(十四):WebSocket、WebService、RESTful、IPv6、网络爬虫、HTTP缓存
网络协议系列文章 网络协议(一):基本概念.计算机之间的连接方式 网络协议(二):MAC地址.IP地址.子网掩码.子网和超网 网络协议(三):路由器原理及数据包传输过程 网络协议(四):网络分类.IS ...
- 网络协议OSI、TCP/IP协议、Socket套接字和第三方AsyncSock的使用等解析
一.网络协议定义 1.OSI参考模型:全称(Open System Interconnection), 开放式系统互联参考模型.是一个逻辑上的定义,一个规范,它把网络协议从逻辑上分为七层,只要目的是为 ...
- 趣谈网络协议笔记-二(第十三讲)
趣谈网络协议笔记-二(第十三讲) 套接字Socket:Talk is cheap, show me the code 前言 这只是笔记,是为了整理刘超大神的极客时间专栏的只是而存在的! 经常会在网络上 ...
最新文章
- http status 404 – 未找到_HTTP状态码
- Genymotion 模拟器 VirtualBox
- Android监听程序自身被卸载
- nonce和timestamp在Http安全协议中的作用
- spark task和stage划分原理
- VTK:InfoVis之KMeansClustering
- Exchange 邮件投递被拒的问题分析
- 嵌套集合模型(Nested set model)介绍
- 阶乘和(信息学奥赛一本通-T1173)
- js学习 字符串常用方法
- css的font修改颜色,css的font字体颜色如何设置
- 关于PPPOE拨号分配给用户32位掩码,且IP与网关相同的问题
- HTML学生个人网站作业设计:动漫网站设计——蜡笔小新(9页) HTML+CSS+JavaScript 简单DIV布局个人介绍网页模板代码 DW学生个人网站制作成品下载
- 厉害了,Pandas表格还能五彩斑斓的展示数据,究竟是怎么做到的呢?
- c语言句子后移两位加密,用C语言实现对输入的引英文句子进行加密
- 基于javaweb+mysql的超市进销存管理系统(java+SpringBoot+Html+Layui+echarts+mysql)
- 测试正则表达式的小方法
- Java基础-Java概述(第一、二章)
- 阿里的“无用”和“有用”
- OPENGL-学习计算机图形学