目录
1.认识socket编程
网络通信的本质
什么是socket编程?
如何进行socket编程?
2.基于UDP的socket编程
服务器端程序编写步骤
1.创建socket
2.将本地信息和网络信息进行绑定
3.接收数据
4.发送数据
客户端程序编写步骤
1.创建socket
2.发送数据
3.接受数据
3.基于TCP的socket编程
服务器端程序编写步骤
1.创建流式套接字
2.绑定本地信息和网络信息
3.将流式套接字设置为监听状态
4.获取连接
5.接收消息
6.发送消息
客户端程序编写步骤
1.创建套接字
2.发起连接
3.发送消息
4.接收消息
1.认识socket编程
网络通信的本质
介绍socket编程之间,我们先来看看网络通信的本质。
网络通信的本质是什么呢?我们需要明确一点,网络通信需要IP地址和端口号。IP地址在标识网络中唯一的一台主机,端口号标识主机中唯一的一个进程,IP地址加端口号就能标识网络中唯一的一台主机上唯一的一个进程。也就是说,通过IP地址和端口号,就可以找到整个网络中唯一的一个进程。所以网络通信的本质就是进程间通信。
更何况,用户在使用设备进行通信的时候,使用的是设备上的应用服务,应用服务本身就是启动的进程。
什么是socket编程?
网络通信的本质是进程间通信,是一种需要依靠IP地址和端口号的进程间通信,我们把这种基于IP地址和端口号的进程间通信叫做socket通信。socket的中文意思是插座,socket通信的模式类似于插板插座这样的模式,通信双方都必须要知道对方的IP地址和端口号就相当于把插板和插线接通,这样才能进行通信。而基于这种模式进行网络通信的编程就是socket编程。(Socket,通常也称为“套接字”)
如何进行socket编程?
进行网络编程需要依靠网络层的网络传输协议,而网络传输协议是在操作系统内部实现的,用户不能直接访问操作系统内部的代码数据,这个时候,操作系统就要提供系统调用接口供用户使用,从而进行网络编程。这些接口就是socket编程的接口。
socket编程接口是传输层供给应用层的编程接口,位于TCP/IP四层模型中的应用层与传输层之间,是应用层与传输层之间的桥梁。而传输层常用的通信协议有UDP协议和TCP协议,于是socket编程便有了基于UDP协议的编程和基于TCP协议的编程。
2.基于UDP的socket编程
当我们进行网络编程时,往往是基于CS模式(客户端服务器端模式),所以需要编写客户端程序和服务器端程序。
进行socket编程时往往需要包含这些头文件:
服务器端程序编写步骤
1.创建socket
示例代码:
socket函数介绍:
参数:
1.domain用于表明通信的类型。网络通信本质上是一种进程间通信,进程间通信有本地通信和网络通信。网络通信往往将这个参数设置为AR_INET。
2.type这个参数表明socket的类型,我们现在讲解的是基于UDP的通信,UDP是一种面向数据报的协议,所以,这个参数往往被设置成SOCK_DGRAM。
3.这个参数用于表明协议的类型。但是前面两个参数已经表明了协议的类型,所以这个参数通常设置为0。
返回值:
1.创建socket成功返回一个文件描述符,与该文件描述符所关联的文件通常用于进行网络通信中数据的收发。
2.创建socket失败返回-1,并设置全局的错误码,表明错误的原因。
2.将本地信息和网络信息进行绑定
示例代码:
bind函数介绍:
在绑定信息前,需要填充struct sockaddr_in结构,作为参数传递时,需要强转为struct sockaddr 类型。
参数:
1.sockfd表明要绑定的本地信息,也就是用于网络通信的文件。
2.addr表明自己的IP地址和端口号。这是一个结构体类型,所以我们需要填充结构体中的字段,具体可参考上面代码实例。
3.addrlen表明addr指向结构体的大小,通常使用sizeof操作符获得。
返回值:
1.成功时:如果
bind
函数成功地将套接字与指定的IP地址和端口号绑定,它会返回0
。2.失败时:如果
bind
函数调用失败,它会返回-1
,并且会设置全局变量errno
以指示具体的错误原因。
3.接收数据
服务器端程序需要先收数据,再发消息进行响应。
示例代码:
recvfrom函数介绍:
UDP是面向数据报的,文件的读写是面向字节流的,所以不能直接使用文件读写的接口。
参数:
1.sockfd 指明从哪个文件中进行读取数据。
2.buf 表明读取的数据存放的缓冲区。
3.len 表明了接收数据的最大长度,以字节为单位。
4.flags 表明了接收数据的类型,通常设置为0。
5.src_addr 是一个输出型参数,用户获取发送方的网络信息(IP地址和端口号)
6.addrlen 是一个指向
socklen_t
变量的指针,该变量在调用时应该包含src_addr
指向的缓冲区的大小。在函数返回时,它将被设置为实际存储在src_addr
中的地址的实际大小。返回值:
- 成功时,
recvfrom
返回接收到的字节数。- 如果连接已正常关闭,则返回0。
- 如果发生错误,则返回-1,并设置
errno
以指示错误类型。
4.发送数据
示例代码:
sendto函数介绍:
参数:
1.sockfd 表明通过哪个文件中发送数据。
2.buf 表明发送数据的地址。
3.len 指定了
buf
中数据的长度,即要发送的字节数。4.flags 指定了发送操作的行为,通常设置为0。
5.dest_addr 表明接收方的网络信息。
6.addrlen 是
dest_addr
指向的地址结构的大小,以字节为单位。返回值:
- 成功时,
sendto
返回发送的字节数。这可能与请求发送的字节数相同,也可能不同(例如,在某些非阻塞或错误情况下)。- 如果发生错误,则返回-1,并设置
errno
以指示错误类型。
客户端程序编写步骤
1.创建socket
这一步和服务器端的一样。客户端创建好socket之后,直接就可以进行数据的收和发了。
2.发送数据
当客户端创建socket套接字之后,直接就可以进行数据的收发了;需要明确的是,客户端需要先发送数据,然后接收数据。
示例代码:
这一步和服务器端也是一样的,参考服务器端即可。
3.接受数据
示例代码:
使用同服务器端。
3.基于TCP的socket编程
服务器端程序编写步骤
1.创建流式套接字
示例代码:
创建流式套接字使用的接口还是socket(),只不过第二个参数需要写成 SOCK_STREAM,这是因为TCP是面向字节流的协议,SOCK_STREAM表明创建流式套接字。
2.绑定本地信息和网络信息
示例代码:
使用上和服务器端的bind完全相同。
3.将流式套接字设置为监听状态
TCP是有连接的协议,客户端和服务器端需要建立连接才能进行通信,将流式套接字设置为监听状态的目的是用于监听连接的状态。
示例代码:
listen函数介绍:
参数:
sockfd
:是socket
函数返回的文件描述符,代表了一个打开的套接字。backlog
:指定了系统应该为相应套接字排队的最大连接个数。如果队列满了,则新的连接请求将被拒绝。这个参数的值至少为0,但具体能支持的最大值依赖于系统实现。返回值:
- 成功时,
listen
函数返回0。- 出错时,返回-1,并设置相应的errno以指示错误原因。
4.获取连接
listen函数将socket返回的套接字设置为监听套接字之后,监听套接字就会监听连接建立的状态,当有连接建立时,accept函数就会从监听套接字中获取连接,并返回一个新的文件描述符,用于建立该连接的客户端和服务器之间的通信。
服务器只有一个,但是客户端可以有很多,多个客户端和一个服务器之间要进行通信,就要求服务器中需要为每个连接建立用于通信套接字。
示例代码:
accept函数介绍:
参数:
- sockfd:服务器端套接字描述符,这是由
socket
函数创建并已经通过bind
函数绑定到特定地址的监听套接字。- addr:一个指向
sockaddr
结构体的指针,用于存储连接的对端(即客户端)的地址信息。如果调用者对此不关心,可以将此参数设置为NULL
。- addrlen:一个指向
socklen_t
类型的指针,用于指定addr
缓冲区的大小。在调用accept
之前,应该将addrlen
初始化为addr
缓冲区的大小,函数返回时,addrlen
会被设置为实际地址结构的长度。返回值:
- 成功时,
accept
函数返回一个新的文件描述符(套接字描述符),这个新的文件描述符用于与客户端进行通信。- 出错时,返回-1,并设置
errno
以指示错误原因。
5.接收消息
因为TCP是面向字节流的协议,所以我们可以使用文件读写接口 read 和 write。
示例代码:
read函数介绍:
参数:
- fd:文件描述符(file descriptor),是一个非负整数,代表了一个打开的文件、管道或套接字。它是通过之前对
open
、pipe
、socket
等系统调用的调用获得的。- buf:指向缓冲区的指针,该缓冲区用于存储从文件描述符指向的资源中读取的数据。
- count:请求读取的字节数。这是尝试从文件中读取的最大字节数。
返回值:
- 成功:返回实际读取的字节数,这个值可能小于请求读取的字节数(
count
),尤其是在到达文件末尾(EOF)或发生某些类型的非阻塞 I/O 操作时。- 失败:返回
-1
,并设置errno
以指示错误原因。
6.发送消息
代码示例:
write函数介绍:
参数:
- fd:文件描述符(file descriptor),是一个非负整数,代表了一个打开的文件、管道或套接字。
- buf:指向缓冲区的指针,该缓冲区包含了要写入文件描述符指向的资源中的数据。
- count:要写入的字节数。
返回值:
- 成功:返回实际写入的字节数。这个值可能与请求写入的字节数(
count
)相同,也可能不同,特别是在写入非阻塞文件描述符或管道时,如果没有足够的空间来保存所有请求的数据,则可能只写入部分数据。- 失败:返回
-1
,并设置errno
以指示错误原因。
客户端程序编写步骤
1.创建套接字
客户端中创建套接字使用的还是socket函数,需要注意的是,第二个参数设置为SOCK_STREAM,表明是基于TCP协议进行的通信。
2.发起连接
TCP是有连接的协议,客户端想要和服务器端进行通信时,首先要发起建立连接的请求,服务器端的监听套接字监听到请求建立连接成功之后,accept函数就会从监听套接字中获取连接,并分配一个文件描述符,该文件描述符所关联的文件用于建立连接的双方进行通信。这也就解释了客户端的为什么要设置监听套接字和获取连接的行为。
示例代码:
connect函数介绍:
参数:
sockfd
:这是由socket
函数返回的文件描述符,代表一个套接字。addr
:这是一个指向sockaddr
结构的指针,该结构包含了服务器端的地址和端口号信息。addrlen
:这个参数指定了addr
参数所指向的地址结构体的长度。返回值:
- 成功时,返回0。
- 出错时,返回-1,并设置全局变量
errno
以指示错误类型。
3.发送消息
实例代码:
send函数介绍:
参数:
sockfd
:socket函数创建的文件描述符。表明向哪个文件描述符中写入数据。buf
:指向包含要发送数据的缓冲区的指针。len
:要发送的字节数。flags
:调用标志,通常设置为0,返回值:
- 成功时,返回实际发送的字节数。这个值可能小于请求发送的字节数,因为TCP是一个流式协议,不保证数据包的原子性。
- 出错时,返回-1,并设置
errno
以指示错误原因。
4.接收消息
示例代码:
recv函数介绍:
参数:
- sockfd:套接字文件描述符,是之前通过
socket
函数创建,表明从哪个文件中读取数据。- buf:指向接收数据的缓冲区的指针。
- len:缓冲区的大小,即可以接收的最大字节数。
- flags:一组标志,用来修改
recv
函数的行为。最常用的标志是0
,表示正常接收数据。返回值:
- 成功:返回实际接收到的字节数。如果连接正常关闭,且没有数据可读,则返回
0
。- 失败:返回
-1
,并设置errno
以指示错误原因。