UC成长之路13
Posted 达少Rising
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UC成长之路13相关的知识,希望对你有一定的参考价值。
回顾:
UC成长之路1
UC成长之路2
UC成长之路3
UC成长之路4
UC成长之路5
UC成长之路6
UC成长之路7
UC成长之路8
UC成长之路9
UC成长之路10
UC成长之路11
UC成长之路12
一、基于TCP网络编程模型和实现
-
传输层有两种:TCP和UDP
-
TCP面向连接的、可靠的、安全的但效率低
-
UDP面向包的、不可靠的但效率高
-
建立TCP连接与断开:建立连接过程称为著名的三次握手,断开四次挥手
-
服务器端编程模型
//1、创建一个套接字端点,返回一个文件描述符。lfd
socket(2)
//2、将lfd和服务器的ip地址和端口号绑定
bind(2)
//3、将lfd设置成被动连接模式,监听客户端连接的到来。有客户端连接的到来,放入未决连接队列中
listen(2)
//4、从未决连接队列中取出一个客户端连接,返回和客户端连接的文件描述符。使用这个文件描述符和客户端通讯。未决连接队列中没有数据,阻塞等待客户端的连接到来
accept(2)
while(1){
//5从客户端获取数据
read()
//6、处理获取到的数据
//7、将处理结果回送给客户端
write()
//8、关闭本次连接
close()
}
- 客户端的编程模型
//1、创建一个通讯端点,返回一个文件描述符
socket(2)
//2、使用这个文件描述符向服务器发起连接
connect(2)
//3、向服务器发送消息
write()
//4、等待服务器的相应消息
read()
//5、处理服务器的相应消息
//6、关闭和服务器的连接,借宿通讯
close()
- socket(2)
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
//功能:创建一个通讯端点,返回一个文件描述符
//参数
//domain:
//1)AF_INET IPv4 Internet protocols ip(7)
//2)AF_INET6 IPv6 Internet protocols ipv6(7)
//...
//type
//1)SOCK_STREAM:TCP
//2)SOCK_DGRAM:UDP
//...
//protocol:0
//返回值:成功返回一个用于新的socket的文件描述符;错误返回-1,errno被设置
- bind(2)
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
//功能:给socket绑定一个名字
//参数:
//sockfd:指定了具体的socket(像手机的SIM卡插槽)
//addr:指定了服务器的地址和端口号(像SIM卡)
//addrlen:制定了addr的有效空间大小
//返回值:成功返回0;错误返回-1,error被设置
struct sockaddr {
sa_family_t sa_family;
char sa_data[14];
};
- listenl(2)
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int listen(int sockfd, int backlog);
//功能:在指定socket上建立连接
//参数:
//sockfd:socket(2)的返回值
//backlog:指定了未决连接队列的最大长度
//返回值:成功返回0;错误返回-1,error被设置
- accept(2)
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
//功能:在指定的socket上接收连接
//参数:
//sockfd:指定了socket(2)。socket(2)的返回值
//addr:如果addr是NULL,addrlen也要设置为空;
//addr指定的地址空间里填充客户端的地址家族的内容
//addrlen:值-结果参数,指定了addr的有效空间大小
//返回值:错误返回-1,errno被设置;成功返回一个非负整数,一个文件描述符,
//和客户端的连接描述符,使用这个连接描述符和客户端通讯。
- connect(2)
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
//功能:在一个socket上发起一个连接,将socket连接到addr指向的地址上
//参数:
//sockfd:指定了socket
//addr:指定了地址
//addrlen:指定了addr空间的大小
//返回值:成功返回0;错误返回-1,errno被设置
- IPV4家族、IPV6家族
- 通用家族 struct sockaddr
struct sockaddr {
sa_family_t sa_family;
char sa_data[14];
};
- IPV4家族地址,使用
man 7 ip
查看
struct sockaddr_in {
sa_family_t sin_family; /* address family: AF_INET */
in_port_t sin_port; /* port in network byte order */
struct in_addr sin_addr; /* internet address */
};
/* Internet address. */
struct in_addr {
uint32_t s_addr; /* address in network byte order */
};
- 配置端口号,配置ip地址
- 网络字节序与主机字节序相互转换, htonl(3)
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
//h:host
//n:net
//s:short
//l:long
//to
- ip地址的转化:字符串与无符号的长整型转换,inet_pton(3), inet_ntop(3)
#include <arpa/inet.h>
int inet_pton(int af, const char *src, void *dst);
//功能:text-->binary
//参数:
//af:AF_INET or AF_INET6
//src:字符串格式的ip,待转换的
//dst:存放转换后的结果
//返回值:成功返回1;af无效返回-1,errno被设置;src无效返回0
#include <arpa/inet.h>
const char *inet_ntop(int af, const void *src,
char *dst, socklen_t size);
//功能:binary--->text
//参数:
//af:AF_INET or AF_INET6
//src:ip地址(binary)
//dst:转换后的ip地址(text)存放到这个地址指定的空间里
//size:指定dst缓冲区可用的字节数
//返回值:错误返回NULL,errno被设置;成功返回dst指向的地址,转换后的结果存放这个空间里
eg:编写基于TCP的服务器端和客户端程序
- 服务器端负责将客户端发送过来的字符串转换为大写,server.c
- 客户端负责向服务器发送字符串,然后将服务器转换后的字符串输出到显示器,client.c
- server.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <ctype.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(void)
{
struct sockaddr_in serv;
int cfd;//connect fd
char buf[128];
//創建1個socket,返回該socket的文件描述符ldf
int lfd = socket(AF_INET, SOCK_STREAM, 0);
if(lfd==-1){
perror("socket");
return -1;
}
//初始化serv的成員
serv.sin_family=AF_INET;
serv.sin_port=htons(1024);
//INADDR_ANY代表本機所有的ip地址
serv.sin_addr.s_addr=htonl(INADDR_ANY);
//將lfd綁定到local的ip地址和端口
int b = bind(lfd, (struct sockaddr *)&serv, sizeof(serv));
if(b==-1){
perror("bind");
return -1;
}
//將lfd設置成被動鏈接模式,監聽客戶端連接的到來。如果有客戶端的連接到來,將該連接放入未決連接隊列中
listen(lfd, 5);
while(1){
//從未決連接隊列中取出第一個連接進行處理,如果沒有未決連接,阻塞等待,有,返回一個文件描述符
cfd = accept(lfd, NULL, NULL);
if(cfd==-1){
perror("accept");
return -1;
}
//這時候,三次握手已經完成,數據的處理
//讀取客戶端的請求
int r = read(cfd, buf, 128);
int i;
//處理客戶端的請求
for(i=0; i<r; i++){
buf[i]=toupper(buf[i]);//將字符轉換爲大寫
}
//響應客戶端,將處理信息響應給客戶端
write(cfd, buf, r);
//關閉和客戶端的連接,結束這個連接
close(cfd);
}
return 0;
}
- client.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(void)
{
struct sockaddr_in serv;//服務器的ip地址和端口號
char *msg = "this is a test...\\n";
char buf[128];
//創建一個socket,返回該socket的文件描述副符
int lfd = socket(AF_INET, SOCK_STREAM, 0);
if(lfd==-1){
perror("socket");
return -1;
}
//初始化服務器的ip地址和端口號
serv.sin_family=AF_INET;//IPV4
serv.sin_port=htons(1024);
//服務器的ip地址,127.0.0.1,text-->binary
inet_pton(AF_INET, "127.0.0.1", &serv.sin_addr);
//使用lfd向服務器發起連接,如果這個函數執行成功,三次握手成功
int conn=connect(lfd, (struct sockaddr*)&serv, sizeof(serv));
if(conn){
perror("connect");
return -1;
}
//向服務器發送字符串
write(lfd, msg, strlen(msg));
//等待服務器的響應信息,如果沒有響應阻塞等待
int r=read(lfd, buf, 128);
//將獲取到的響應信息輸出到顯示器
write(1, buf, r);
//關閉和服務器的連接,結束通訊
close(lfd);
return 0;
}
- 改进eg
server.c
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <ctype.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(void)
{
struct sockaddr_in serv, clie;
int cfd;//connect fd
char buf[128];
//創建1個socket,返回該socket的文件描述符ldf
int lfd = socket(AF_INET, SOCK_STREAM, 0);
if(lfd==-1){
perror("socket");
return -1;
}
//初始化serv的成員
serv.sin_family=AF_INET;
serv.sin_port=htons(1024);
//INADDR_ANY代表本機所有的ip地址
serv.sin_addr.s_addr=htonl(INADDR_ANY);
//將lfd綁定到local的ip地址和端口
int b = bind(lfd, (struct sockaddr *)&serv, sizeof(serv));
if(b==-1){
perror("bind");
return -1;
}
//將lfd設置成被動鏈接模式,監聽客戶端連接的到來。如果有客戶端的連接到來,將該連接放入未決連接隊列中
listen(lfd, 5);
while(1){
socklen_t cli_len = sizeof(clie);
//從未決連接隊列中取出第一個連接進行處理,如果沒有未決連接,阻塞等待,有,返回一個文件描述符
cfd = accept(lfd, (struct sockaddr *)&clie, &cli_len);
if(cfd==-1){
perror("accept");
return -1;
}
char IP[64];
//binary-->text
printf("%s\\n", inet_ntop(AF_INET, &clie.sin_addr, IP, 64));
//這時候,三次握手已經完成,數據的處理
//讀取客戶端的請求
int r = read(cfd, buf, 128);
int i;
//處理客戶端的請求
for(i=0; i<r; i++){
buf[i]=toupper(buf[i]);//將字符轉換爲大寫
}
//響應客戶端,將處理信息響應給客戶端
write(cfd, buf, r);
//關閉和客戶端的連接,結束這個連接
close(cfd);
}
return 0;
}
client.c
perror("connect");
return -1;
}
//向服務器發送字符串
write(lfd, msg, strlen(msg));
//等待服務器的響應信息,如果沒有響應阻塞等待
int r=read(lfd, buf, 128);
//將獲取到的響應信息輸出到顯示器
write(1, buf, r);
//關閉和服務器的連接,結束通訊
close(lfd);
return 0;
}
- 封装eg
t_net.h
#ifndef T_NET_H_
#define T_NET_H_
/*include file*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
/*類型的聲明*/
typedef struct sockaddr_in SA4;
typedef struct sockaddr SA;
/*函數的聲明,創建socket,將返回的描述符綁定到本地地址*/
int socket_b(int port);
int trans(int fd);//客戶端的業務處理
#endif
t_net.c
#include <stdio.h>
#include "t_net.h"
int socket_b(int port)
{
SA4 serv;
//創建1個socket,返回該socket的文件描述符ldf
int lfd = socket(AF_INET, SOCK_STREAM, 0);
if(lfd==-1){
perror("socket");
return -1;
}
//初始化serv的成員
serv.sin_family=AF_INET;
serv.sin_port=htons(port);
//INADDR_ANY代表本機所有的ip地址
serv.sin_addr.s_addr=htonl(INADDR_ANY);
//將lfd綁定到local的ip地址和端口
int b = bind(lfd, (SA *)&serv, sizeof(serv));
if(b==-1){
perror("bind");
return -1;
}
return lfd;
}
//業務的處理
int trans(int fd)
{
char buf[128];
//讀取客戶端的請求
int r=read(fd,buf,128);
int i;
//處理客戶端的請求
for(i=0;i<r;i++){
buf[i]=toupper(buf[i]);//將字符轉換爲大寫
}
write(fd, buf, r);
return 0;
}
serverp.c
#include <stdio.h>
#include <unistd.h>
#include "t_net.h"
int main(void)
{
SA4 clie;
int cfd;//connect fd
int lfd=socket_b(1024);
if(lfd==-1) return -1;
//將lfd設置成被動鏈接模式,監聽客戶端連接的到來。如果有客戶端的連接到來,將該連接放入未決連接隊列中
listen(lfd, 5);
while(1){
socklen_t cli_len = sizeof(clie);
//從未決連接隊列中取出第一個連接進行處理,如果沒有未決連接,阻塞等待,有,返回一個文件描述符
cfd = accept(lfd, (SA *)&clie, &cli_len);
if(cfd==-1){
perror("accept");
return -1;
}
char IP[64];
//binary-->text
printf("%s\\n", inet_ntop(AF_INET, &clie.sin_addr, IP, 64));
//這時候,三次握手已經完成,數據的處理
trans(cfd);//業務的處理
//關閉和客戶端的連接,結束這個連接
close(cfd);
}
return 0;
}
client.c
inet_pton(AF_INET, argv[1], &serv.sin_addrSpark成长之路(13)-DataSet与DataFrame
开发成长之路(13)-- Linux网络服务端编程(通识篇)