1.TCP编程模型
tcp特点:基于连接的通信,也就是要想通信必须首先建立连接。
编程模型
服务器 | 客户机 |
---|---|
创建套接字 | 创建套接字 |
绑定IP地址 | |
监听端口 | |
等待连接 | 连接服务器 |
收/发数据 | 收/发数据 |
结束连接 | 结束连接 |
- 三次握手含义:客户机请求连接,服务器回复连接,连接建立客户机发送数据。
注意:
- 绑定地址就是将服务器ip地址和端口绑定在一起,也就是这个服务器只用这个固定的端口进行通信。(从系统中选定一个ip地址来与套接字绑定在一起)
- 监听端口就是监听绑定ip的这个端口,
2.相关函数
服务器端
创建一个套接字
函数名:socket
函数原型:int socket(int domain,int type,int protocol)
函数功能:创建一个套接字
所属头文件:sys/socket.h
返回值:成功返回套接字描述符,失败返回-1
参数说明:domain:套接字通讯领域, type:套接字类型, protocol:所属协议绑定地址
函数名:bind
函数原型:int bind(int sockfd,const struct sockaddr *addr,socklen_t len)
函数功能:关联地址和套接字
所属头文件:sys/socket.h
返回值:成功返回0,失败返回-1
参数说明:sockfd:要关联套接字的描述符,addr套接字结构的地址,len:套接字的长度
注意:
1.套接字的数据结构
2.字符型的IP地址与整型的IP地址(网络字节序)之间的转换
in_addr_t inet_addr(const char *cp)
功能:将字符串形式的IP地址转换成整数型的IP地址(网络字节序);
范例:in_addr.saddr = inet_addr(“192.168.1.1”);char *inet_ntoa(struct in_addr)
功能:将整数形式的IP地址转换成字符型的IP地址。
3.网络字节序与主机字节序
网络字节序:即通过网络发送数据时要把小端模式转化成大端模式;在本机接收时要把大端模式转化成本机模式(若本机是大端模式就不需要转化)
客户端本机字节序:可能是大端或者小端,要是小端时必须在发送前转化为大端。
网络字节序:在网络中传输的模式,规定为大端模式
服务器本机字节序:可能是大端或者小端,要是小端时必须在接收时,必须将其(网络字节序是大端)转化为小端
4.主机字节序与网络字节序的转换函数
uint32_t htonl(uint32_t hostlong)
功能:将32位的数据从主机字节序转换为网络字节序
范例:in_addr.saddr = htonl(INADDR_ANY)uint_t htons(unit16_t hostshort);
功能:将16位的数据从主机字节序转化为网络字节序
范例:in_addr.saddr = htonl(INADDR_ANY)uint32_t htonl(uint32_t netlong)
功能:将32位的数据从网络字节序转换为主机字节序uint_t htons(unit16_t netlong);
功能:将16位的数据从网络字节序转化为主机字节序设置监听端口
函数名:listen
函数原型:int listen(int sockfd,int backlog)
函数功能:宣告他愿意接收连接请求或者设置服务器可以连接多少个客户机请求
所属头文件:sys/socket.h
返回值:成功返回0,失败返回-1
参数说明:sockfd:套接字的描述符,backlog:客户机的数目等待连接
函数名:accept
函数原型:int accept(int sockfd,struct sockaddr addr,socklen_t *len)
函数功能:获得连接请求并建立连接
所属头文件:sys/socket.h
返回值:成功返回新的套接字描述符(后面的发送和接收数据将用这个新的套接字来同信),失败返回-1
参数说明:sockfd:套接字描述符,addr:客户机地址(注意:该参数并不是用来传值的,而是用来收取客户机的地址的),len:地址长度的地址
注意:该函数会导致服务器的阻塞,也就是当没有客户机连接时,服务器一直处于等待状态。
发送数据
函数名:send
函数原型:int send(int sockfd,const void *buf,size_t nbytes,int flags)
函数功能:发送数据
所属头文件:sys/socket.h
返回值:成功返回发送的字节数,失败返回-1
参数说明:sockfd:套接字描述符,buf:发送字节缓存地址,nbytes:发送字节数,flags:,接收数据
函数名:recv
函数原型:ssize_t recv(int sockfd,void *buf,size_t nbytes,int flags)
函数功能:接收数据
所属头文件:sys/socket.h
返回值:成功返回字节长度,若无可用数据或对等方已经按序结束,返回0,若出错返回-1
参数说明:和send参数含义相同。关闭连接
函数名:close
函数原型:int close(int fd)
函数功能:关闭文件
所属头文件:unistd.h
返回值:成功返回0,失败返回-1
参数说明:fd:所要关闭文件的描述符
客户机
除了连接服务器函数不一样,其他函数一样
- 连接服务器
函数名:connect
函数原型:int connect(int sockfd,const struct sockaddr *addr,socklen_t len)
函数功能:使客户机与服务器建立连接
所属头文件:sys/socket.h
返回值:成功返回0,失败返回-1
参数说明:sockfd:套接字描述符 ,addr:服务器地址 ,len:地址长度。
3.实例编程
TCP通讯程序设计:实现服务器与客户机相互发送和接收的功能。
- 服务器程序
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main()
{
int sockfd;
int n_sockfd;
int fd;
ssize_t num;
char sv_buf[128];
char rv_buf[128];
int sin_size;
unsigned short int portnum = 5000;
struct sockaddr_in sockaddr_sv, sockaddr_ct;
//1.创建套接字
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == sockfd)
{
printf("creat sockfd fail!\n");
exit(1);
}
//2.绑定IP地址
sockaddr_sv.sin_family = AF_INET;
sockaddr_sv.sin_port = htons(portnum);
sockaddr_sv.sin_addr.s_addr = htonl(INADDR_ANY);
bzero(&sockaddr_sv.sin_zero,8);
if(-1 == bind(sockfd,(struct sockaddr *) &sockaddr_sv,sizeof(struct sockaddr)))
{
printf("bind port fail!\n");
exit(1);
}
//3.监听端口
if(-1 == listen(sockfd,5))
{
printf("listen port fail!\n");
exit(1);
}
//4.等待连接
sin_size = sizeof(struct sockaddr);
n_sockfd = accept(sockfd, (struct sockaddr *)&sockaddr_ct,&sin_size);
printf("server get connection from %s\n",inet_ntoa(sockaddr_ct.sin_addr));
//5.发送/接收数据
while (1)
{
fgets(sv_buf,128,stdin);
send(n_sockfd, sv_buf, strlen(sv_buf), 0);
fd = fork();//创建一个进程用来接收信息
if (0 == fd)
{
while(1)
{
num = recv(n_sockfd, rv_buf, 128, 0);
rv_buf[num] = '\0';
printf("%s",rv_buf);
}
}
}
//6.关闭连接
close(sockfd);
}
- 客户机程序
#include <stdio.h>
#include <strings.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main()
{
int fd;
int sockfd;
int n_sockfd;
ssize_t num = 0;
char ct_buf[128];
char st_buf[128];
//int sin_size;
unsigned short int portnum = 5000;
struct sockaddr_in sockaddr_sv;
//1.创建套接字
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == sockfd)
{
printf("creat sockfd is fail!");
}
//2.等待连接
sockaddr_sv.sin_family = AF_INET;
sockaddr_sv.sin_port = htons(portnum);
sockaddr_sv.sin_addr.s_addr = inet_addr("192.168.1.100");
bzero(&sockaddr_sv.sin_zero,8);
connect(sockfd,(struct sockaddr *)&sockaddr_sv,sizeof(struct sockaddr));
while (1)
{
num = recv(sockfd, ct_buf, 128, 0);
ct_buf[num] = '\0';
printf("%s",ct_buf);
fd = fork();//创建一个进程用来发送信息
if (0 == fd)
{
while(1)
{
fgets(st_buf,128,stdin);
send(sockfd, st_buf, strlen(st_buf), 0);
}
}
}
//3.关闭连接
close(sockfd);
}