1:TCP客户/服务器模型(c/s)
这个就是建立连接的过程。图片摘自https://www.bilibili.com/video/BV1eb411F74G?p=7
2:回射客户/服务器模型
3:socket, bind, listen, accept, connect
实现上面所示的功能,需要的函数:
socket函数
//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
*/
在使用的时候可以使用man 函数名来查看使用方式,和选取合适的参数。例如 man socket:
bind函数
//bind函数
/*
包含头文件<sys/socket.h>
功能:绑定一个本地地址到套接字
原型:
int bind(itn sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数:
sockfd: socket函数返回的套接字
addr:要绑定的地址
addrlen: 地址长度
返回值:成功返回0,失败返回-1;
*/
listen函数
//listen函数
/*
包含头文件<sys/socket.h>
功能:将套接字用于监听进入的连接
原型:
int listen(itn sockfd, int backlog);
参数:
sockfd: socket函数返回的套接字。
backlog: 规定内核为此套接字排队的最大连接个数。规定了并发的连接数
返回值:成功返回0,失败返回-1;
*/
在这里涉及到了两种套接字,分别为被动套接字和主动套接字,listen函数是将主动套接字变为被动套接字。listen函数应该在调用socket和bind函数之后,调用函数accept之前调用。
对于给定的监听套接口,内核要维护两个队列:
已由客户发出并到达服务器,服务器正在等待完成相应的TCP三次握手过程:
已完成连接的的队列。(在accept以后就会被移除该队列,一遍更多的客户进来)
accept函数
//accept函数
/*
含头文件<sys/socket.h>
功能:从已完成连接队列返回第一个连接,如果已完成连接队列为空,则阻塞。
原型:
int accept(int sockfd, struct sockadder *addr, socklen_t *addrlen);
参数:
sockfd: 服务器套接字。
addr: 将返回对等方的套接字地址
addrlen: 返回对等方的套接字地址长度。
返回值:成功返回非负整数,失败返回-1;
*/
connect函数
//connect函数
/*
含头文件<sys/socket.h>
功能:建立一个连接至addr所指定的套接字
原型:
int connect(int sockfd, const struct sockadder *addr, socklen_t addrlen);
参数:
sockfd: 为连接套接字。
addr: 要连接的套接字地址
addrlen: 第二个参数addr长度。
返回值:成功返回0,失败返回-1;
*/
回射客户/服务器模型例子:
服务端程序:
include <iostream>
#include <stdio.h>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
using namespace std;
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
}while(0);
int main()
{
//创建套接字
int creat_socket;
creat_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(creat_socket < 0)
{
ERR_EXIT("socket");
}
//分配套接字地址
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET; //表示使用的IPV4
servaddr.sin_port = htons(3453);
//servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_addr.s_addr = inet_addr("192.168.204.131");
//绑定套接字地址
if( bind(creat_socket, (struct sockaddr*)&servaddr, sizeof(servaddr) ) < 0 )
{
ERR_EXIT("bind");
}
//等待连接请求状态
if( listen(creat_socket,SOMAXCONN) < 0 )
{
ERR_EXIT("listen");
}
//允许连接
struct sockaddr_in linkaddr;
socklen_t linklen = sizeof(linkaddr);
int conn = accept(creat_socket, (struct sockaddr *)&linkaddr, &linklen);
if(conn < 0)
{
ERR_EXIT("accept");
}
//输出连接到的IP地址和端口号
printf("id = %s ,", inet_ntoa(linkaddr.sin_addr));
printf("port = %d\n", ntohs(linkaddr.sin_port));
//数据交换
char recvbuf[1024];
while(1)
{
memset(recvbuf,0,sizeof(recvbuf));
int ret = read(conn,recvbuf, sizeof(recvbuf));
fputs(recvbuf,stdout);
write(conn, recvbuf, ret);
memset(recvbuf, 0, sizeof(recvbuf));
}
//断开连接
close(conn);
close(creat_socket);
return 0;
}
客户端程序:
#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
using namespace std;
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0);
int main()
{
//创建套接字
int creat_socket;
creat_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(creat_socket < 0)
{
ERR_EXIT("socket");
}
//分配套接字地址
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET; //表示使用的IPV4
servaddr.sin_port = htons(3453);
servaddr.sin_addr.s_addr = inet_addr("192.168.204.131");
//请求连接
if (connect(creat_socket, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0)
{
ERR_EXIT("connect");
}
// 4. 数据交换
char recvbuf[1024];
char sendbuf[1024];
while (fgets(sendbuf, sizeof sendbuf, stdin) != NULL) // 键盘输入获取
{
// memset(recvbuf, 0, sizeof recvbuf);
// memset(sendbuf, 0, sizeof sendbuf);
write(creat_socket, sendbuf, sizeof sendbuf); // 写入服务器
int ret = read(creat_socket, recvbuf, sizeof recvbuf); // 服务器读取
fputs(recvbuf, stdout); // 服务器返回数据输出
// 清空
memset(recvbuf, 0, sizeof recvbuf);
memset(sendbuf, 0, sizeof sendbuf);
}
// 5. 断开连接
close(creat_socket);
return 0;
}
需要在linux下运行。