什么是TCP/IP
是Internet最基本的协议、Internet国际互联网络的基础,由网络层的IP协议和传输层的TCP协议组成
IP转换函数
inet_addr()
将点分十进制数的 IP 地址转换成为网络字节序的 32 位二进制数值。
返回值:成功,返回 32 位二进制的网络字节序地址。不成功返回 0.
参数 cp:存放输入的点分十进制数 IP 地址字符串。
参数 inp:传出参数,保存网络字节序的 32 位二进制数值。
inet_aton()
将点分十进制数的 IP 地址转换成为网络字节序的 32 位二进制数值。
返回值:成功,则返 回 1,不成功返回 0.
参数 cp:存放输入的点分十进制数 IP 地址字符串。
参数 inp:传出参数,保存网络字节序的 32 位二进制数值。
inet_ntoa()
将网络字节序的 32 位二进制数值转换为点分十进制的 IP 地址。
端口号
一个是16位无符号整形,0-65535
区分一台主机接收到的数据包应该转交给哪个进程来进行处理
端口号转化
原因:网络中传输的数据必须按网络字节序,即大端字节序
主机 -----> 网络
uint16_t htons(uint16_t hostshort);
将一个无符号短整型的主机数值转换为网络字节顺序
网络 ------> 主机
uint16_t ntohs(uint16_t netshort);
将一个16位数由网络字节顺序转换为主机字节顺序。
地址名字转化
主机名或者域名 ------> IP地址
gethostbyname():
用于将域名(www.baidu.com)或主机名转换为 IP 址。
参数 hostname 指向存放域名或主机名的字符串。
IP地址 ------> 主机名或者域名
gethostbyaddr():
用于将 IP 地址转换为域名或主机名。
参数
addr 是一个 IP 地址,此时这个 ip 地址不是普通的字符串,而是要通过函数 inet_aton()转换。
len 为 IP 地址的长度, AF_INET 为 4。
family 可用 AF_INET:Ipv4 或 AF_INET6: Ipv6。
核心结构体
struct hostent {
char *h_name; /*正式主机名*/
char **h_aliases; /*主机别名*/
int h_addrtype; /*主机 IP 地址类型 IPv4 为 AF_INET*/
int h_length; /*主机 IP 地址字节长度,对于 IPv4 是 4 字节,即 32 位*/
char **h_addr_list; /*主机的 IP 地址*/ }
struct sockaddr_in {
short int sin_family; /*地址族*/
unsigned short int sin_port; /*端口号*/
struct in_addr sin_addr; /*IP 地址*/
unsigned char sin_zero[8]; /*未使用*/ };
struct in_addr {
in_addr_t s_addr; /* 32 位 IPv4 地址,网络字节序 */ };
struct sockaddr {
//此结构体不常用
unsigned short sa_family; /*地址族*/
char sa_data[14]; /*14 节的协议地址,包含该 socket 的 IP 地址和端口号 */ };
TCP建立连接过程
服务器
1.创建监听套接字,用socket函数
int socket(int domain, int type, int protocol);
-
参数
-
domain: 网域 AF_INET :IPv4 AF_INET6 :IPv6
-
type:选择传输协议 tcp /udp SOCK_STREAM ;tcp SOCK_DGRAM : udp
-
protocol: 基本废弃,一般赋0
-
-
返回值
-
成功返回描述网络套接字sockfd,失败返回-1
-
2.编写服务器地址信息
3.将服务器的地址信息与监听套接字绑定,用bind函数
int bind(int sockfd, const struct sockaddr *my_addr, socklen_t addrlen);
-
参数
-
sockfd 为前面 socket 的返回值
-
my_addr:封装ip地址和端口
-
addrlen:sockaddr 的结构体长度。 通常是计算 sizeof(struct sockadd);
-
-
返回值
-
成功则返回 0,失败返回-1
-
4.开始监听(设置允许的最大连接数),listen函数
int listen(int sockfd, int backlog);
-
参数
-
sockfd 为前面 socket 的返回值.
-
backlog 指定同时能处理的最大连接要求,通常为 10 或者 5。 最大值可设至 128
-
-
返回值
-
成功则返回 0,失败返回-1
-
5.等待来自客户端的连接请求,accept函数
int accept(int sockfd, struct sockaddr *addr,socklen_t *addrlen);
-
参数
-
sockfd 为前面 socket 的返回值.
-
addr:用于接受客户端的ip地址和端口号
-
addrlen:第二个参数大小
-
-
返回值
-
返回新的套接字描述符(通信套接字),专门用于与建立的客户端通信,失败-1
-
6.收发数据
ssize_t send(int s, const void *buf, size_t len, int flags);
-
参数
-
s:新的套接字,即前面 accept 的返回值.
-
buf:要发送的数据缓冲区
-
len: 数据长度
-
flags: 一般赋0 .阻塞
-
-
返回值
-
成功返回真正发送的数据长度,失败-1
-
ssize_t recv(int s, void *buf, size_t len, int flags);
-
参数
-
s:新的套接字,即为前面 accept 的返回值
-
buf:存放接收数据的缓冲区
-
len: 数据长度
-
flags: 一般赋0 .阻塞
-
-
返回值
-
成功返回真正接收的数据长度,失败-1
-
7.关闭套接字(网络连接)
close(sockfd)
客户端
1.创建通信套接字,用socket函数
2.编写服务器地址信息,即设置要连接的服务器的ip地址和端口等属性
3.连接服务器,用connect函数
int connect(int sockfd, const struct sockaddr*serv_addr, socklen_t addrlen);
-
参数
-
sockfd 为前面 socket 的返回值
-
serv_addr 为结构体指针变量,存储着远程服务器的 IP 与端口号信息。
-
addrlen 表示结构体变量的长度
-
-
返回值
-
成功则返回 0,失败返回-1
-
4.收发数据,send和recv或者read和write
5.关闭通信套接字(网络连接)
close
/*server.c*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <time.h>
#define PORT 8888
#define IP "127.0.0.1"
int main(int argc, char *argv[])
{
int sock_fd,clien_fd;
struct sockaddr_in seraddr,clientaddr;
socklen_t len;
char buf[200] = "\0";
//1.创建监听套接字
sock_fd = socket(AF_INET,SOCK_STREAM,0);
if(sock_fd < 0)
{
perror("socket error\n");
return -1;
}
printf("创建监听套接字成功\n");
memset(&seraddr,0,sizeof(seraddr));
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(PORT);
seraddr.sin_addr.s_addr = inet_addr(IP);
//inet_aton("192.168.15.2",&seraddr.sin_addr);//将点分式ip地址 转换为2进制
//还可以用这个函数,直接将IP地址回写到seraddr结构体成员中的sin_addr
//seraddr.sin_addr.s_addr = htonl(INADDR_ANY); //IP号(htonl(INADDR_ANY)自动获取本机的IP号)
//2.绑定
if((bind(sock_fd,(struct sockaddr*)&seraddr, sizeof(seraddr))) < 0)
{
perror("bind error\n");
exit(-1);
}
printf("绑定套接字成功\n");
//3.监听
if((listen(sock_fd,5)) < 0)
{
perror("listen error\n");
exit(-1);
}
printf("监听成功........\n");
len = sizeof(clientaddr);
//4.连接
if((clien_fd = accept(sock_fd,(struct sockaddr *)&clientaddr,&len)) < 0)
{
perror("accept error\n");
exit(-1);
}
printf("客户端连接成功\n");
//5.发送接收
while(1)
{
memset(buf,0,sizeof(buf));
recv(clien_fd,buf,sizeof(buf),0);
printf("RECV CLIENT %s : %s\n",inet_ntoa(clientaddr.sin_addr),buf);
memset(buf,0,sizeof(buf));
printf("please input >:");
scanf("%s",buf);
send(clien_fd,buf,sizeof(buf),0);
}
//6.关闭套接字
close(clien_fd);
close(sock_fd);
return 0;
}
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#define PORT 8888
#define IP "127.0.0.1"
int main(int argc, char *argv[])
{
int sock_fd;
struct sockaddr_in seraddr;
socklen_t len;
char buf[200] = "\0";
sock_fd = socket(AF_INET,SOCK_STREAM,0);
if(sock_fd < 0)
{
perror("socket error\n");
exit(-1);
}
memset(&seraddr,0,sizeof(seraddr));
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(PORT);
seraddr.sin_addr.s_addr = inet_addr(IP);
if(connect(sock_fd,(struct sockaddr*)&seraddr,sizeof(seraddr)) < 0)
{
perror("connet error\n");
exit(-1);
}
printf("成功连接服务器\n");
while(1)
{
printf("please input >:");
scanf("%s",buf);
send(sock_fd,buf,sizeof(buf),0);
memset(buf,0,sizeof(buf));
recv(sock_fd,buf,sizeof(buf),0);
printf("RECV SERVER: %s\n",buf);
}
close(sock_fd);
return 0;
}