现在将拥有的数据整理一下,首先HTTP消息封包已经由应用程序打包完成,服务器IP地址也已经透过DNS[1]请求机制获得。在两个前提条件都满足的状况下,我们就可以着手思考要怎麽将这些数据发给对方服务器的应用程序
发送数据其实是调用多个socket库函式达成的,藉由委托多个函式API进行一连串的任务交互,每个任务完成的项目不同,有建立连接部分、断开连线等等,这些操作的用意就是为了保证双方是否接收到消息与回应是否正常[2]
为了使双方应用程序之间建立一条专属的沟通管道,我们调用了socket库函式,很多书上将建立socket形容成搭建一条无形的通道,双方可透过这条通道来实现消息的收发操作,不过并不是说建立socket後计算机才被允许与网路进行通讯,其实早在建立socket之前计算机就可以向网路收发消息了,建立socket比较像是彼此确定我们该走哪一条传输通道找到对方的应用程序
顺着这个思路这小节将介绍应用程序是如何调用socket库函式向下层委托收发。我喜欢用一个网路订房的比喻来形容建立socket连线的步骤,就像我们是使用手机app进行订房,委托系统进行操作,真正的流程实际上是不得而知的,这就像站在应用层的视角看待socket连线[3]
目的: 依照指定类型创建socket
就下载这个订房app吧!
我们先来看看socket create部分:
int socket(int domain, int type, int protocol);
socket(AF_INET, SOCK_STREAM, 0); // 选择 TCP
socket(AF_INET, SOCK_STREAM, 6); // 还是 TCP
socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // 依然是 TCP
socket(AF_INET, SOCK_DGRAM, 0); // 这次是 UDP
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/socket.h>
// #define AF_INET 2
// #define SOCK_STREAM 1
int main(int argc, char *argv[]){
uint32_t socket_identifier = 0;
/*创建socket*/
socket_identifier = socket(AF_INET , SOCK_STREAM , 0);
switch(socket_identifier){
case 1: // 正数
case 2:
...
case n:
printf("socket create successfully!\n");
break;
case -1:
printf("socket create error!\n");
}
return 0;
}
socket创建成功一般都会回传一个大於0的标示符,若回传负数则代表socket创建异常
目的: 使两个应用程序建立连线通道
就用这个帐号登入app吧
来看看连现阶段吧:
int connect(int fd, struct sockaddr *server, int addrlen);
connect函数是客户端发起的请求,目的是为了与服务器建立连线,介绍参数前,先来讲讲sockaddr这个结构体,它里面装的主要就是socket连线需要的消息,这里暂且以IPv4为例:
struct in_addr{
ip_addr_t s_addr;
};
struct sockaddr{
unsigned char sin_family; //AF_INET所以是IPv4
unsigned short sin_port; // 应用程序端口号
struct in_addr sin_addr; // 服务器IP地址
unsigned char sin_zero[8]; // 不会用到
};
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <malloc.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define SERV_PORT 8080
typedef sockaddr* info;
int main(argc, char* argv[]){
info serv=(info)calloc(0, sizeof(sockaddr));
uint8_t resp;
/*创建socket部分*/
/*填入socket消息*/
serv->sin_family = AF_INET;
serv->sin_port = htons(SERV_PORT);
inet_pton(AF_INET, "127.0.0.1", serv->sin_addr);
resp = connect(socket_identifier, (info)serv, sizeof(sockaddr));
if(resp < 0)
printf("socket connect error!\n");
return 0;
}
藉由创建socket函式API,我们取得本地端的socket编号socket_identifier[4],接下来的任务就是要跟服务器上的应用程序进行连接,IP地址可以帮助我们找到服务器地址,而端口号[5]则可以帮我们找到执行在服务器上的应用程序
当我们成功建立连接後,资料就可以透过socket在两个应用程序之间流通,接着我们可以透过使用read()/recv()来获取资料,使用write()/send()来传输资料。read()/write与recv()/send()的不同只差在recv()/send()的输入参数多了一个描述符flag,这个描述符提供操作更多的细节控制选项,不过我们以下还是使用通用的收发socket API → read()/write
目的: 将资料写入 Socket 中并发送出去
就是这间了,赶紧下单!
ssize_t write(int fd, const void *buf, size_t nbyte);
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <malloc.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
define MAX 1024
char* buf=(char*)malloc(max); // buffer
int main(argc, char* argv[]){
ssize_t s_write;
/*创建socket部分*/
/*socket连线部分*/
/*socket write*/
strcpy(buf, "socket test");
s_write = write(socket_identifier, buf, MAX);
if(s_write < 0){
printf("socket write error!\n");
}
else{
printf("socket write data length=%d\n", s_read); // 送出了多少资料长度
}
...
return 0;
}
透过调用write函式将资料发送出去,回传值可以判断发送的资料长度,若是buffer大小为0会返回0,失败则回传-1。它跟待会要介绍的接收消息read()其实就是两个死对头,一个急着将资料压到buffer里,一个忙着将资料拿出来发出去
目的: 透过连线中的 Socket读取资料
系统提示~您已经下订成功!
ssize_t read(int fd, void* buf, size_t nbyte);
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <malloc.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
define MAX 1024
char* buf=(char*)malloc(max); // buffer
int main(argc, char* argv[]){
ssize_t s_read;
/*创建socket部分*/
/*socket连线部分*/
/*socket read*/
s_read = read(socket_identifier, buf, MAX);
if(s_read < 0){
printf("socket read error!\n");
}
else{
printf("socket read data length=%d\n", s_read); // 读取buffer内资料长度
}
...
return 0;
}
藉由操作read()可以透过回传值得知读取状况,负数代表有错误产生,0或者正数代表读取buffer的资料长度
假如我们得到的回传值是0可能有几个特别意思:
目的: 关闭socket
若完成订房,请登出帐号
int close(int fd);
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <malloc.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main(argc, char* argv[]){
int s_close;
/*创建socket部分*/
/*socket连线部分*/
/*socket的收发操作*/
/*关闭socket*/
s_close = close(socket_identifier);
(s_close < 0) ? printf("socket close error!") : printf("close successfully!");
return 0;
}
藉由socket断开双方之间的通讯,执行成功返回0,若发生错误则返回-1
[1] :网路是怎样连接的(四)DNS
[2] :传输层使用TCP协议
[3] :关於socket连线部分将在介绍传输层时介绍
[4] :标示符是应用程序用来识别众多本地端socket用
[5] :客户端服务端通讯间用来识别众多对方socket用
<<: Day 23 利用transformer自己实作一个翻译程序(五) Positional encoding
>>: Day 23 ASP.NET Core Identity 说明
当我们在 Mac 上打开和使用应用程序(如Google Chrome 浏览器等)或其他文件时,系统会...
好的,今天我们要来看的就是我们的精华啦-聊天室。 原本我们在设计邀约流程的时候是。 (原本设想的流程...
今天要介绍的内容是如何在 React.js 中撰写我们的 CSS 样式?除了相关套件的应用之外,还...
传送文字 在设定回覆讯息的部分,如果是文字讯息的话,是这样写: events.message.typ...
本篇章主要是先从技术层面来解释 URI 与 URL 及比较少使用到却与生活息息相关的 URN,让读者...