Linux网络编程之socket编程(二)

本次内容将介绍TCP客户/服务器模型,回射客户/服务器,以及一系列函数:socket, bind, listen, accept, connect等。

1.TCP客户/服务器模型(C/S模型)

(1)模型如下:C/S模型

2.回射客户/服务器

(1)模型如下回射/客户模型

3.相关函数

(1)socket函数

  • 包含头文件<sys/socket.h>
  • 功能:创建一个套接字用于通信
  • 原型:int socket(int domain, int type, int protocol);
  • 参数:
    • domain:指定通信协议族(protocol family)
    • type:指定socket类型, 流式套接字SOCK_STREAM, 数据报套接字SOCK_DGRAM, 原始套接字SOCK_RAW
    • protocol:协议类型
  • 返回值:成功返回非负整数,它与文件描述符类似,我们把它称为套接口描述字,简称套接字。失败返回-1。

(2)bind函数

  • 包含头文件<sys/socket.h>
  • 功能:绑定一个本地地址到套接字
  • 原型:int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • 参数:
    • sockfd:socket函数返回的套接字
    • addr:要绑定的地址
    • addrlen:地址长度
  • 返回值:成功返回0;失败返回-1

(3)listen函数

  • 包含头文件<sys/socket.h>
  • 功能:将套接字用于监听进入的连接
  • 原型:int listen(int sockfd, int backlog);
  • 参数:
    • sockfd:socket函数返回的套接字
    • backlog:规定内核为此套接字排队的最大连接个数,一般使用SOMAXCONN宏
  • 返回值:成功返回0;失败返回-1

1)一旦调用listen函数之后,该套接字就变成了被动套接字,否则默认是主动套接字:
被动套接字:接受连接——当有连接时调用accept(服务器端)
主动套接字:发起连接——需要调用connect(客户端)

2)一般来说,listen函数应该在调用socket和bind函数之后,调用函数accept之前调用。(服务器端)

3)对于给定的监听套接口,内核要维护两个队列
a)已由客户发出并达到服务器,服务器正在等待完成TCP三路握手的队列
b)已完成连接队列

4)针对上述过程,有如下示意图:listen函数

其中accept之后,就会将其条目从已完成队列中移除,以便有更多的客户端能够发起连接。

(4)accept函数

  • 包含头文件<sys/socket.h>
  • 功能:从已完成连接队列返回第一个连接,如果已完成连接队列为空,则阻塞。
  • 原型:int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
  • 参数:
    • sockfd:服务器套接字
    • addr:将返回对等方的套接字地址
    • addrlen:返回对等方的套接字地址长度
  • 返回值:成功返回非负整数,失败返回-1

(5)connect函数

  • 包含头文件<sys/socket.h>
  • 功能:建立一个连接至addr所指定的套接字
  • 原型:int connect(int sockfd, const struct sockaddr * addr, socklen_t addrlen);
  • 参数:
    • sockfd:未连接的套接字
    • addr:要连接套接字地址
    • addrlen:第二个参数addr长度
  • 返回值:成功返回0;失败返回-1
4.回射客户服务器实例

(1)服务器端,echosrv.c

(2)客户端,echocli.c

编译之后,先运行服务器再运行客户端,在客户端输入消息可得到如下输出:

建立连接之后,当从客户端输入“hello world!”,服务器端会收到来自客户端发送的消息,之后服务器端再将此消息回射给客户端,客户端打印输出。

附:
(1)memset函数

  • 包含头文件<string.h>
  • 函数原型:void* memset(void *s, int ch, size_t n);
  • 函数功能:将s中当前位置后面的n个字节 (typedef unsigned int size_t )用 ch 替换并返回 s。作用是在一段内存块中填充某个给定的值,它是对较大的结构体数组进行清零操作的一种最快方法。

WARNING:该函数对数组操作时只能用于数组的置0或-1,其他值无效。

(2)fgets函数和fputs函数

char * fgets( char * buf, int n, FILE * fp );  // 从fp读取字符串到缓冲区buf中
int fputs( const char * str, FILE * fp );     // 将缓冲区str中的数据写入fp中

1)fgets中buf为存储字符串的地址,n为读取字符串的长度,fp为文件的指针,返回值为空指针时表示获取失败或结束。该函数一次最多只读取一行,遇到\n就会停止读取,若有多行需要循环读取。

2)fputs中str为要写入文件的字符串,fp为要操作的文件,返回值为0表示成功。写入的字符串也是以结束符\n为结束的,所以多行写入需要重复操作。

《Linux网络编程之socket编程(二)》有1个想法

发表评论

电子邮件地址不会被公开。 必填项已用*标注