TCP网络编程关键函数:
socket()、struct sockaddr_in、htons()、htonl()、socklen_t、bind()、listen()、accept()、recv()、connect()、send()
传输层协议:TCP协议(打电话)面向于有连接的通信方式
例题: 使用网络通信TCP协议,实现不同主机之间的通信
主机A---主机B
Jack.c Rose.c
核心代码 Rose.c 服务器
- 创建未连接TCP套接字
int fd = socket(AF_INET,SOCK_STREAM,0);
- 准备好服务器IP地址,端口号,协议 --> 通通塞到结构体中
struct sockaddr_in
struct sockaddr_in srvaddr;
srvaddr.sin_family = AF_INET; //网际协议
srvaddr.sin_port = htons(atoi(argv[1])); //端口号,atoi是把字符串转换成整型数
/*
之所以需要这些函数是因为计算机数据表示存在两种字节顺序:NBO与HBO
网络字节顺序NBO(Network Byte Order):
按从高到低的顺序存储,在网络上使用统一的网络字节顺序,可以避免兼容性问题。
主机字节顺序(HBO,Host Byte Order):
不同的机器HBO不相同,与CPU设计有关,数据的顺序是由cpu决定的,而与操作系统无关。
如 Intelx86结构下,short型数0x1234表示为34 12, int型数0x12345678表示为78 56 34
12如IBM power PC结构下,short型数0x1234表示为12 34, int型数0x12345678表示为12 34
56 78。
由于这个原因不同体系结构的机器之间无法通信,所以要转换成一种约定的数序,也就是网络字 节顺序,其实就是如同powerpc那样的顺序 。在PC开发中有ntohl和htonl函数可以用来进行网络字节和主机字节的转换。
*/
srvaddr.sin_addr.s_addr = htonl(INADDR_ANY);
/*
IP地址,由于服务器上可能有多个网卡,也就有多个ip地址,所以该ip地址的选项为INADDR_ANY,表示:在本服务器上无论是哪个ip地址接收到数据,只要是这个端口号,服务器都会处理。
*/
INADDR_ANY --> 接收任何地址的数据信息
/* Address to accept any incoming messages. */
#define INADDR_ANY ((unsigned long int) 0x00000000)
- 把地址绑定到未连接套接字上
socklen_t就是struct sockaddr_in大小的数据类型
socklen_t len = sizeof(srvaddr);
int ret = bind(fd,(struct sockaddr*)&srvaddr,len);
- 设置监听套接字
listen(fd,4); 是fd的本身从未连接套接字转换监听套接字
/*
backlog参数就是控制我们的已连接队列里等待accept()取走的连接的最大数目的.注意一点,backlog与这个已排队连接的最大数目未必是完全相等的,不同的系统的实现可能不同.比如backlog=1,系统允许的实际一排队数目可能为2.
*/
- 坐等对方的连接
int connfd = accept(fd,(strutc sockaddr*)&cliaddr,&len);
- 畅聊
recv(connfd,buf,sizeof(buf),0);
- 断开连接
close(connfd);
close(fd);
核心代码 Jack.c 客户端
- 创建未连接TCP套接字
int fd = socket(AF_INET,SOCK_STREAM,0);
- 发起连接
int ret = connect(fd,(struct sockaddr *)&srvaddr,len);
- 畅聊
send(fd,buf,strlen(buf),0);
运行步骤:
同桌Ubuntu 你的Ubuntu
Rose.c Jack.c
192.168.0.10 192.168.0.20
50001 50001
同桌: ping 192.168.0.20
你: ping 192.168.0.10
同桌: ./Rose 50001
你: ./Jack 192.168.0.10 50001
例子1:
Jack.c
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <stdio.h>
#include <linux/in.h> //18.04 16.04 删除这个头文件
#include <strings.h>
#include <string.h>
int main(int argc,char *argv[]) // ./Jack 服务器IP 端口号 ./Jack 192.168.0.2 50001
{
//1. 创建未连接TCP套接字
int fd = socket(AF_INET,SOCK_STREAM,0); // 必须与服务器的类型一致
//2. 准备对方Rose的IP地址,端口号,协议
struct sockaddr_in srvaddr;
srvaddr.sin_family = AF_INET;
srvaddr.sin_port = htons(atoi(argv[2]));
inet_pton(AF_INET,argv[1],&srvaddr.sin_addr);
//3. 发起连接
socklen_t len = sizeof(srvaddr);
int ret = connect(fd,(struct sockaddr *)&srvaddr,len);//连接成功后,fd自身就会变成已连接套接字
if(ret == -1)
printf("connect error!\n");
else
printf("connect ok!\n");
//4. 畅聊
char buf[50];
while(1)
{
bzero(buf,50);
fgets(buf,50,stdin);
send(fd,buf,strlen(buf),0);
if(strncmp(buf,"quit",4) == 0)
break;
}
//5. 挂断
close(fd);
return 0;
}
Rose.ccp
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <stdio.h>
#include <linux/in.h> //18.04 16.04 删除这个头文件
#include <strings.h>
int main(int argc,char *argv[]) // ./Rose 50001
{
//1. 创建一个未连接TCP套接字
int fd = socket(AF_INET,SOCK_STREAM,0);
//2. 准备好服务器的结构体变量,再进行赋值
struct sockaddr_in srvaddr;
srvaddr.sin_family = AF_INET; //网际协议
srvaddr.sin_port = htons(atoi(argv[1])); //端口号
srvaddr.sin_addr.s_addr = htonl(INADDR_ANY); // IP地址
//3. 把服务器的IP地址,协议,端口号绑定到未连接套接字上
socklen_t len = sizeof(srvaddr);
int ret = bind(fd,(struct sockaddr*)&srvaddr,len);
if(ret == -1)
printf("bind error!\n");
//4. 将未连接套接字转换为监听套接字
listen(fd,4);
//5. 坐等电话
struct sockaddr_in cliaddr; //存放来电显示
int connfd = accept(fd,(struct sockaddr*)&cliaddr,&len); //阻塞等待
if(connfd == -1)
printf("accept error!\n");
else
printf("connect ok!\n");
//6. 畅聊
char buf[50];
while(1)
{
bzero(buf,50);
recv(connfd,buf,sizeof(buf),0);
printf("from client:%s",buf);
if(strncmp(buf,"quit",4) == 0)
break;
}
//7. 挂断电话
close(connfd);
close(fd);
return 0;
}
例子2:tcp_chat
Jack.c
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <stdio.h>
#include <linux/in.h> //18.04 16.04 删除这个头文件
#include <strings.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
void *routine(void *arg)
{
int fd = *(int *)arg;
char buf[50];
while(1)
{
bzero(buf,50);
recv(fd,buf,sizeof(buf),0);
printf("from Rose:%s",buf);
if(strncmp(buf,"quit",4) == 0)
{
exit(0);
}
}
}
int main(int argc,char *argv[]) // ./Jack 服务器IP 端口号 ./Jack 192.168.0.2 50001
{
//1. 创建未连接TCP套接字
int fd = socket(AF_INET,SOCK_STREAM,0); // 必须与服务器的类型一致
//2. 准备对方Rose的IP地址,端口号,协议
struct sockaddr_in srvaddr;
srvaddr.sin_family = AF_INET;
srvaddr.sin_port = htons(atoi(argv[2]));
inet_pton(AF_INET,argv[1],&srvaddr.sin_addr);
//3. 发起连接
socklen_t len = sizeof(srvaddr);
int ret = connect(fd,(struct sockaddr *)&srvaddr,len);//连接成功后,fd自身就会变成已连接套接字
if(ret == -1)
printf("connect error!\n");
else
printf("connect ok!\n");
pthread_t tid;
pthread_create(&tid,NULL,routine,(void *)&fd);
//4. 畅聊
char buf[50];
while(1)
{
bzero(buf,50);
fgets(buf,50,stdin);
send(fd,buf,strlen(buf),0);
if(strncmp(buf,"quit",4) == 0)
break;
}
//5. 挂断
close(fd);
return 0;
}
Rose.c
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <stdio.h>
#include <linux/in.h> //18.04 16.04 删除这个头文件
#include <strings.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
void *routine(void *arg)
{
int connfd = *(int *)arg;
char buf[50];
while(1)
{
bzero(buf,50);
fgets(buf,50,stdin);
send(connfd,buf,strlen(buf),0);
if(strncmp(buf,"quit",4) == 0)
{
exit(0);
}
}
}
int main(int argc,char *argv[]) // ./Rose 50001
{
//1. 创建一个未连接TCP套接字
int fd = socket(AF_INET,SOCK_STREAM,0);
//2. 准备好服务器的结构体变量,再进行赋值
struct sockaddr_in srvaddr;
srvaddr.sin_family = AF_INET; //网际协议
srvaddr.sin_port = htons(atoi(argv[1])); //端口号
srvaddr.sin_addr.s_addr = htonl(INADDR_ANY); // IP地址
//3. 把服务器的IP地址,协议,端口号绑定到未连接套接字上
socklen_t len = sizeof(srvaddr);
int ret = bind(fd,(struct sockaddr*)&srvaddr,len);
if(ret == -1)
printf("bind error!\n");
//4. 将未连接套接字转换为监听套接字
listen(fd,4);
//5. 坐等电话
struct sockaddr_in cliaddr; //存放来电显示
int connfd = accept(fd,(struct sockaddr*)&cliaddr,&len); //阻塞等待
if(connfd == -1)
printf("accept error!\n");
else
printf("connect ok!\n");
//5.5 创建线程,用于实现服务器写功能
pthread_t tid;
pthread_create(&tid,NULL,routine,(void *)&connfd);
//6. 畅聊
char buf[50];
while(1)
{
bzero(buf,50);
recv(connfd,buf,sizeof(buf),0);
printf("from client:%s",buf);
if(strncmp(buf,"quit",4) == 0)
break;
}
//7. 挂断电话
close(connfd);
close(fd);
return 0;
}