// tcp_ser.c
#include <myheader.h>
#define SER_PORT 8888
#define SER_IP "192.168.125.109"
int newfd, sfd;
int main(int argc, const char *argv[])
{
//1、为通信创建一个端点
sfd = socket(AF_INET, SOCK_STREAM, 0);
//参数1:说明使用的是ipv4通信域
//参数2:说明使用的是tcp面向连接的通信方式
//参数3:由于参数2中已经指定通信方式,填0
if(-1 == sfd){
perror("socket");
return 1;
}
printf("socket success sfd = %d\n", sfd);
//2、绑定ip和端口号
//2.1、准备地址信息结构体
struct sockaddr_in sin = {
.sin_family = AF_INET,//通信域
.sin_port = htons(SER_PORT),//端口号
.sin_addr = { inet_addr(SER_IP) },//ip地址
};
//2.2、绑定工作
if(-1 == bind(sfd, (struct sockaddr*)&sin, sizeof(sin))){
perror("bind");
return 1;
}
puts("bind success");
//3、将套接字设置为被动监听状态
if(-1 == listen(sfd, 128)){
perror("listen");
return 1;
}
//4、阻塞等待客户端的连接
//4.1、定义用于接受客户端信息的容器
struct sockaddr_in cin;
socklen_t addrlen = sizeof(cin);
newfd = accept(sfd, (struct sockaddr*)&cin, &addrlen);
if(-1 == newfd){
perror("accpet");
return 1;
}
printf("[%s:%d]:connected!\n", inet_ntoa(cin.sin_addr),
ntohs(cin.sin_port));
//5、与客户端进行相互通信
while(1){
char rbuf[128] = {};// 读取消息内容的容器
//从套接字中读取数据
int res = recv(newfd, rbuf, sizeof(rbuf), 0);
if(0 == res){
puts("client disconnecting...");
break;
}
//将读取的消息展示出来
printf("[%s:%d]:%s\n", inet_ntoa(cin.sin_addr),
ntohs(cin.sin_port), rbuf);
//将消息处理并回复客户端
strcat(rbuf, "---> OK");
//将消息发送给客户端
send(newfd, rbuf, strlen(rbuf), 0);
puts("send over");
}
//6、关闭套接字
close(newfd);
close(sfd);
return 0;
}
// tcp_cli.c
#include <myheader.h>
#define SER_PORT 8888
#define SER_IP "192.168.125.109"
#define CLI_PORT 6666
#define CLI_IP "192.168.125.109"
int main(int argc, const char *argv[])
{
//1、创建用于通信的套接字文件描述符
int cfd = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == cfd){
perror("socket");
return 1;
}
printf("cfd = %d\n", cfd);
//2、绑定IP地址和端口号
//2.1、填充客户端地址信息结构体
struct sockaddr_in cin = {
.sin_family = AF_INET,
.sin_port = htons(CLI_PORT),
.sin_addr = { inet_addr(CLI_IP) },
};
//2.2、绑定
if(-1 == bind(cfd, (struct sockaddr*)&cin, sizeof(cin))){
perror("bind error");
return 1;
}
puts("bind success");
//3、连接服务器
//3.1、准备对端地址信息结构体
struct sockaddr_in sin = {
.sin_family = AF_INET,
.sin_port = htons(SER_PORT),
.sin_addr = { .s_addr = inet_addr(SER_IP) },
};
//3.2、连接服务器
if(-1 == connect(cfd, (struct sockaddr*)&sin, sizeof(sin))){
perror("connect");
return 1;
}
puts("connect success");
//4、数据收发
while(1){
char buf[128] = {};
//从终端上获取要发送的数据
fgets(buf, sizeof(buf), stdin);
buf[strlen(buf) - 1] = '\0';
//将数据发送给服务器
send(cfd, buf, strlen(buf), 0);
puts("send over");
bzero(buf, sizeof(buf));
//接收服务器发来的信息
recv(cfd, buf, sizeof(buf), 0);
puts(buf);
}
close(cfd);
return 0;
}
//udp_ser.c
#include <myheader.h>
#define SER_PORT 8888
#define SER_IP "192.168.125.109"
int main(int argc, const char *argv[])
{
//1、创建用于通信的套接字文件描述符
int sfd = socket(AF_INET, SOCK_DGRAM, 0);
if(-1 == sfd){
perror("socket");
return 1;
}
printf("sfd = %d\n", sfd);
//2、绑定IP地址和端口号
//2.1、填充地址信息结构体
struct sockaddr_in sin = {
.sin_family = AF_INET,
.sin_port = htons(SER_PORT),
.sin_addr = { inet_addr(SER_IP) },
};
if(-1 == bind(sfd, (struct sockaddr*)&sin, sizeof(sin))){
perror("bind");
return 1;
}
puts("bind success");
//3、数据收发
struct sockaddr_in cin;
socklen_t addrlen = sizeof(cin);
while(1){
//清空容器
char buf[128] = {};
recvfrom(sfd, buf, sizeof(buf), 0, (struct sockaddr*)&cin, &addrlen);
printf("[%s:%d]:%s\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port),
buf);
//将接受到的数据添加OK回复回去
strcat(buf, "---> OK");
if(-1 == sendto(sfd, buf, strlen(buf), 0, (struct sockaddr*)&cin,
sizeof(cin))){
perror("write");
return 1;
}
puts("send ok");
}
return 0;
}
//udp_cli.c
#include <myheader.h>
#define SER_PORT 8888
#define SER_IP "192.168.125.109"
#define CLI_PORT 9999
#define CLI_IP "192.168.125.109"
int main(int argc, const char *argv[])
{
//1、创建用于通信的套接字文件描述符
int cfd = socket(AF_INET, SOCK_DGRAM, 0);
if(-1 == cfd){
perror("socket");
return 1;
}
printf("cfd = %d\n", cfd);
/*//2、绑定IP地址和端口号
//2.1、填充地址信息结构体
struct sockaddr_in cin = {
.sin_family = AF_INET,
.sin_port = htons(CLI_PORT),
.sin_addr = { inet_addr(CLI_IP) },
};
if(-1 == bind(cfd, (struct sockaddr*)&cin, sizeof(cin))){
perror("bind");
return 1;
}
puts("bind success");
*/
//3、数据收发
//填充服务器的地址信息结构体
struct sockaddr_in sin = {
.sin_family = AF_INET,
.sin_port = htons(SER_PORT),
.sin_addr = { inet_addr(SER_IP) },
};
while(1){
//清空容器
char buf[128] = {};
//从终端上获取信息
fgets(buf, sizeof(buf), stdin);
buf[strlen(buf) - 1] = '\0';
//将消息发送给服务器
int ret = sendto(cfd, buf, strlen(buf), 0, (struct sockaddr*)&sin, sizeof(sin));
printf("ret = %d\n", ret);
puts("send over");
//接受服务器发来的消息
recvfrom(cfd, buf, sizeof(buf), 0, NULL, NULL);
puts(buf);
}
//4、关闭套接字
close(cfd);
return 0;
}
- IP地址由网络号与主机号组成,用于标识一个计算机在网络中的位置
- 根据前缀的长度不同,划分为A、B、C、D、E五类IP地址
- A类,前缀0,8位网络号,24位主机号,表示范围1.0.0.0 ~ 127.255.255.255,被保留,不再供给,但少数大型公司拥有
- B类,前缀10,16位网络号,16位主机号,表示范围128.0.0.0 ~ 191.255.255.255,网络运营商拥有
- C类,前缀110,24位网络号,8位主机号,表示范围192.0.0.0 ~ 223.255.255.255,家庭网,校园网,企业网
- D类,前缀1110,全为网络号,表示范围224.0.0.0 ~ 239.255.255.255,组播IP
- E类,前缀1111,全为网络号,表示范围240.0.0.0 ~ 255.255.255.255,保留及实验室使用
- 200.0.0.1属于C类地址
- 端口号是2字节的无符号整数,标识了网络通信中的单个主机中的进程,借用端口号可以使得数据交付给正确的进程
- 字节序就是字节的序列,在通过网络通信时,发送接收的多字节整型数据需要考虑主机字节序与网络字节序的相互转换,由于不同计算机使用的架构、CPU处理方式不同,可能存在大小端存储,但是网络上一定是大端存储,可以使用转换函数实现转换
- 物理层、数据链路层、网络层、传输层、会话层、表示层、应用层
- http、tftp应用层,ip网络层,udp传输层,arp数据链路层
- TCP:面向连接的、可靠的、有差错和拥塞控制的传输协议,传输的数据不错误、不重复、不丢失、不失序,存在粘包现象,即将相邻多个较小且发送间隔短的数据,则粘成一个包传输,由于需要应答和重传,效率较低,数据收发不同步,使用场景是要求传输质量高或大量数据传输时,比如用户登录和大型文件下载等。UDP:无连接的、不可靠的、尽最大努力交付的传输协议,传输的数据可能会重复,丢失,失序,若单次数据量超过上限则直接删除,收发同步,使用场景发送小尺寸数据或接收数据应答困难的情况,比如广播、音视频通话等。
- TCP服务端创建流程
- 创建socket套接字,获取套接字文件描述符
- bind绑定ip地址及端口号
- listen设置监听状态
- accept接收客户端连接请求,返回用于读写的描述符,获取客户端ip及端口
- 读写数据:send、recv
- close关闭套接字描述符和读写描述符
- UDP服务端创建流程
- 创建socket套接字,获取套接字文件描述符
- bind绑定ip地址及端口号
- 读写数据:sendto、recvfrom
- close关闭套接字