1. 服务端客户端编程模式
- 服务端长期暴露于网络,被动等待客户端的连接
- 客户端则发起连接动作,等待服务端做出相应
- 特点:
- 服务端无法自己主动连接客户端
- 客户端只能按照预定义的方式连接客户端
2. TCP的服务端程序实现步骤
-
创建套接字
-
bind函数向套接字分配地址以及端口
函数说明:
#include <sys/socket.h> int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
返回值:成功 0,否则 -1
参数说明:
- sockfd 待分配ip和端口的套接字文件描述符
- addr 地址端口信息的结构体
- addrlen 地址结构体变量长度
-
等待请求连接
调用listen函数后,
套接字进入等待连接请求
。只有调用了listen函数,客户端才能进入可发出连接请求状态,即这时客户端才能调用connect函数(若提前调用将发生错误问题)。函数原型:
#include <sys/types.h> /* See NOTES */ #include <sys/socket.h> int listen(int sockfd, int backlog);
返回值:成功0,失败-1
参数说明:
- sockfd:用于监听的套接字
- backlog:连接请求等待队列的长度,如,该值5,表示最多使5个连接请求进入等待连接的队列
先解释一下等待连接请求状态的含义和连接请求等待队列含义,服务端处于等待连接请求状态是指,客户端请求连接时受理连接前一直处于请求等待状态
由图可知listen函数第一个参数的作用:可以理解成服务器端用于接收连接请求的一个门卫,当客户端有新连接请求时,服务端套接字就会将客户端的连接请求数据
"请到"连接请求等候室,listen函数第二个参数就是这个等候室的大小,等候室被称为连接请求队列。该参数的大小与服务器的性能有联系。
-
处理/受理客户端连接请求
调用listen函数后,如果有新的连接请求,则应按序受理。受理请求即进入可接受数据状态。数据通讯要使用套接字来完成,服务端已经有一个“门卫”套接字,但是这个套接字不能用于数据交换,不然谁来守门?所以服务器端要创建一个新的套接字与客户端套接字进行数据交换。
accept函数用于创建与客户端套接字连接的套接字。
函数原型:
#include <sys/types.h> /* See NOTES */ #include <sys/socket.h> int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
返回值:成功:文件描述符,失败:-1
参数说明:
- sockfd: 服务端套接字文件描述符
- addr:用于保存发起连接请求的客户端地址信息,调用accept函数后,该值会被自动进行填充
- addrlen:第二个结构体的长度
accept函数调用成功后,内部将会自动创建一个用于和客户端套接字连接的套接字,并返回其文件描述符,并自动与发起连接的客户端建立对应的连接。
-
完成数据交换
-
断开套接字
3. 实例
实验目的:测试socket数据交换,listen 8899端口,使用网络调试工具模拟client,与服务端程序进行数据读写,client发送数据到服务端,当服务端接收的数据量大于16字节时,服务端向客户端发送"Hello world!".
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main(int argc ,char* argv[])
{
int server = 0;
struct sockaddr_in saddr = {0};
int client = 0;
struct sockaddr_in caddr = {0};
socklen_t asize = 0;
int len = 0;
char buf[32] = {0};
int r = 0;
server = socket(PF_INET, SOCK_STREAM, 0);
if( server == -1 )
{
printf("server socket error\n");
return -1;
}
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
saddr.sin_port = htons(8899);
if( bind(server, (struct sockaddr*)&saddr, sizeof(saddr)) == -1 )
{
printf("server bind error\n");
return -1;
}
if( listen(server, 1) == -1 )
{
printf("server listen error\n");
return -1;
}
printf("server start success\n");
asize = sizeof(caddr);
client = accept(server, (struct sockaddr*)&caddr, &asize);
if( client == -1 )
{
printf("client accept error\n");
return -1;
}
printf("client: %d\n", client);
len = 0;
do
{
int i = 0;
r = recv(client, buf, sizeof(buf), 0);
if( r > 0 )
{
len += r;
}
printf("recv %s", buf);
} while ( len <16 );
printf("\n");
send(client, "Hello world!", 12, 0);
sleep(1);
close(client);
close(server);
return 0;
}
/*
./a.out
server start success
client: 4
recv 0abcde
recv 1abcde
*/