2023.6.28 正式开始学习网络编程。 每一章每一节的笔记都会记录在博客中以便复习。
第1章
1.1理解网络编程和套接字
网络编程又叫套接字编程。所谓网络编程,就是编写程序使两台连网的计算机相互交换数据。 为什么叫套接字编程? 我们平常将插头插入插座上就能从电网中获取电力,同样的道理,为了与远程计算机进行数据传输,需要连接到因特网,而编程中的“套接字”就是用来链接网络的工具。
服务器端创建的套接字又叫服务器端套接字或者监听套接字。 其请求连接的套接字创建过程分为四步:
- 调用socket函数创建套接字
- 调用bind函数分配IP地址和端口号
- 调用listen函数转为可接受请求状态
- 调用accept函数受理连接请求
请求连接的客户端套接字创建过程如下两步:
- 调用socket函数创建套接字
- 调用connect函数向服务器端发送连接请求
值得注意的是:创建完套接字,并不会马上区分为服务器端或客户端。 如果接下来紧接着调用bind、listen等函数则成为服务器端套接字;如果调用connect函数则称为客户端套接字。
接下来在linux环境中编译并执行上述两个实例:hello_server.c文件和hello_client.c文件。
分别对客户端和服务端程序进行编译:
gcc hello_server.c -o hserver
gcc hello_client.c -o hclient
该命令中的-o是用来指定可执行文件名的可选参数,因此,编译后将生成可执行文件hserver和hclient。
运行:
./hserver 9190
./hclient 127.0.0.1 9190
运行的时候,首先在 9190 端口启动服务,然后 heserver 就会一直等待客户端进行响应,当客户端监听位于本地的 IP 为 127.0.0.1 的地址的9190端口时,客户端就会收到服务端的回应,输出`Hello World!`。
ps:执行过程中输入的127.0.0.1是本地计算机的IP地址。 如果在同一台计算机中同时运行服务器端和客户端,将采用这种连接方式。 但如果服务器端和客户端在不同计算机中运行,则应采用服务器端所在计算机的IP地址。
1.2基于Linux的文件操作
底层访问和文件描述符
在linux中,socket也是文件的一种,因此在网络数据传输过程中可以使用文件I/O相关的函数。而Windows需要区分socket和文件,因此在Windows中需要调用特殊的数据传输相关函数。
每当生成文件或套接字,操作系统将返回分配给他们的整数,(即文件描述符),这个整数将成为程序员与操作系统之间良好沟通的渠道,实际上,文件描述符是为了方便称呼系统创建的文件或套接字而赋予的数。 文件描述符也成为文件句柄,“句柄”是Windows中的术语,Linux平台则使用“描述符”。
打开文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *path, int flag);
/*
成功时返回文件描述符,失败时返回-1
path : 文件名的字符串地址
flag : 文件打开模式信息
*/
关闭文件
#include <unistd.h>
int close(int fd);
/*
成功时返回 0 ,失败时返回 -1
fd : 需要关闭的文件或套接字的文件描述符
*/
若调用此函数同时传递文件描述符参数,则关闭(终止)响应文件。另外需要注意的是,此函数不仅可以关闭文件,还可以关闭套接字。再次证明了「Linux 操作系统不区分文件与套接字」的特点。
将数据写入文件
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t nbytes);
/*
成功时返回写入的字节数 ,失败时返回 -1
fd : 显示数据传输对象的文件描述符
buf : 保存要传输数据的缓冲值地址
nbytes : 要传输数据的字节数
*/
在此函数的定义中,size_t 是通过 typedef 声明的 unsigned int 类型。对 ssize_t 来说,ssize_t 前面多加的 s 代表 signed ,即 ssize_t 是通过 typedef 声明的 signed int 类型。
创建新文件并保存数据:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
void error_handling(char *message);
int main()
{
int fd;
char buf[] = "Let's go!\n";
// O_CREAT | O_WRONLY | O_TRUNC 是文件打开模式,将创建新文件,并且只能写。如存在 data.txt 文件,则清空文件中的全部数据。
fd = open("data.txt", O_CREAT | O_WRONLY | O_TRUNC);
if (fd == -1)
error_handling("open() error!");
printf("file descriptor: %d \n", fd);
// 向对应 fd 中保存的文件描述符的文件传输 buf 中保存的数据。
if (write(fd, buf, sizeof(buf)) == -1)
error_handling("write() error!");
close(fd);
return 0;
}
void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
运行后会生成一个data.txt
的文件,里面有Let's go!
读取文件中的数据
与之前的write()
函数相对应,read()函数
用来输入(接收)数据:
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t nbytes);
/*
成功时返回接收的字节数(但遇到文件结尾则返回 0),失败时返回 -1
fd : 显示数据接收对象的文件描述符
buf : 要保存接收的数据的缓冲地址值。
nbytes : 要接收数据的最大字节数
*/
下面代码通过 read() 函数读取 data.txt 中保存的数据:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#define BUF_SIZE 100
void error_handling(char *message);
int main()
{
int fd;
char buf[BUF_SIZE];
fd = open("data.txt", O_RDONLY);
if (fd == -1)
error_handling("open() error!");
printf("file descriptor: %d \n", fd);
if (read(fd, buf, sizeof(buf)) == -1)
error_handling("read() error!");
printf("file data: %s", buf);
close(fd);
return 0;
}
void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
实验结果:
low.open.c创建文件并保存文件数据,运行之后返回文件描述符3。用cat命令输出data.txt的文件内容,可以确认确实向文件传输了数据。
low.read.c程序则通过read函数读取了data.txt中保存的数据。运行之后打印文件描述符以及文件的内容。
fd_seri.c 程序同时创建文件和套接字:
fd1 = socket(PF_INET, SOCK_STREAM, 0);
fd2 = open("test.dat", O_CREAT | O_WRONLY | O_TRUNC);
fd3 = socket(PF_INET, SOCK_DGRAM, 0);
然后分别打印其文件描述符得到3、4、5,描述符从3开始以由小到大的顺序编号,因为0、1、2是分配给标注能I/O的描述符。如下图:
1.5 习题
1.套接字在网络编程中的作用是什么?为何称它为套接字?
套接字是一种用于在网络上进行通信的编程接口。套接字允许不同计算机上的进程通过网络进行数据传输。它提供了一种机制,使得计算机之间可以建立连接、发送和接收数据。套接字使得应用程序能够利用网络进行通信,实现客户端和服务器之间的数据交换。
它被称为套接字,是因为它类比于电话通信中的插座,作为网络通信的端点连接点。
2.在服务器端创建套接字后,会依次调用listen函数 和accept函数。请比较并说明二者作用。
listen函数用于将套接字设置为被动监听模式,以接受客户端的连接请求。
accept函数用于接受客户端的连接请求,并创建一个新的套接字来处理与该客户端的通信。
3.Linux中,对套接字数据进行I/O时可以直接使用文件I/O相关函数;而在Windows中则不可以,原因为何?
这是因为在Unix-like系统中,包括Linux,一切皆文件的思想被广泛采用,将各种资源(包括套接字)都抽象为文件描述符的形式,统一了数据的读写接口。
然而,在Windows操作系统中,套接字不被视为文件描述符,因此不能直接使用文件I/O相关函数进行数据的读写操作。
4.创建套接字后一般会给它分配地址, 为什么?为了完成地址分配需要调用哪个函数?
在网络编程中,创建套接字后需要给它分配地址,主要是为了使其他计算机能够找到并与该套接字建立连接。通过分配地址,可以指定套接字的IP地址和端口号,从而唯一标识该套接字在网络中的位置。文章来源:https://www.toymoban.com/news/detail-516367.html
为了完成地址分配,需要调用bind函数。bind函数用于将套接字与特定的IP地址和端口号进行绑定。文章来源地址https://www.toymoban.com/news/detail-516367.html
到了这里,关于《TCP IP网络编程》第一章的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!