回顾:
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_addr);
//使用lfd向服務器發起連接,如果這個函數執行成功,三次握手成功
int conn=connect(lfd, (SA *)&serv, sizeof(serv));
if(conn==-1){
perror("connect");
return -1;
}
//向服務器發送字符串
write(lfd, msg, strlen(msg));
//等待服務器的響應信息,如果沒有響應阻塞等待
int r=read(lfd, buf, 128);
//將獲取到的響應信息輸出到顯示器
write(1, buf, r);
//關閉和服務器的連接,結束通訊
close(lfd);
return 0;
}
- eg:客户端和服务器一次连接,传输多个字符串。如果客户端要结束这次连接,就输入exit。
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 <string.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];
while(1){
memset(buf, 0, 128);
//讀取客戶端的請求
int r=read(fd,buf,128);
int i;
//處理客戶端的請求
for(i=0;i<r;i++){
buf[i]=toupper(buf[i]);//將字符轉換爲大寫
}
write(fd, buf, r);
if(strcmp(buf,"EXIT")==0) break;
}
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;
}
clientp.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "t_net.h"
int main(int argc, char* argv[])
{
SA4 serv;//服務器的ip地址和端口號
char buf[128];
char msg[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, argv[1], &serv.sin_addr);
//使用lfd向服務器發起連接,如果這個函數執行成功,三次握手成功
int conn=connect(lfd, (SA *)&serv, sizeof(serv));
if(conn==-1){
perror("connect");
return -1;
}
while(gets(msg)){
//向服務器發送字符串
write(lfd, msg, strlen(msg));
memset(buf, 0, 128);
//等待服務器的響應信息,如果沒有響應阻塞等待
int r=read(lfd, buf, 128);
if(strcmp(buf,"EXIT")==0) break;
//將獲取到的響應信息輸出到顯示器
write(1, buf, r);
printf("\n");
}
//關閉和服務器的連接,結束通訊
close(lfd);
return 0;
}
二、并发服务器的实现
- 并发的实现:多路复用、线程、进程
- 父进程
- 从未决连接队列中取一个连接
- 创建子进程
- 关闭客户端的连接描述符
- 回收子进程的资源,waitpid()
- 子进程
- close(lfd)
- 处理客户端的具体业务
- close(cfd)
- exit(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 <string.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];
while(1){
memset(buf, 0, 128);
//讀取客戶端的請求
int r=read(fd,buf,128);
int i;
//處理客戶端的請求
for(i=0;i<r;i++){
buf[i]=toupper(buf[i]);//將字符轉換爲大寫
}
write(fd, buf, r);
if(strcmp(buf,"EXIT")==0) break;
}
return 0;
}
servers.c
#include <stdio.h>
#include <unistd.h>
#include "t_net.h"
#include <stdlib.h>
#include <sys/wait.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));
//創建子進程
pid_t pid=fork();
if(pid==-1){
perror("fork");
return -1;
}
if(pid==0){//子進程的任務
close(lfd);
//這時候,三次握手已經完成,數據的處理
trans(cfd);//業務的處理
//關閉和客戶端的連接,結束這個連接
close(cfd);
exit(0);
}
else{//父進程的任務
close(cfd);
//非阻塞
waitpid(-1, NULL, WNOHANG);
}
}
return 0;
}
clientp.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "t_net.h"
int main(int argc, char* argv[])
{
SA4 serv;//服務器的ip地址和端口號
char buf[128];
char msg[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, argv[1], &serv.sin_addr);
//使用lfd向服務器發起連接,如果這個函數執行成功,三次握手成功
int conn=connect(lfd, (SA *)&serv, sizeof(serv));
if(conn==-1){
perror("connect");
return -1;
}
while(gets(msg)){
//向服務器發送字符串
write(lfd, msg, strlen(msg));
memset(buf, 0, 128);
//等待服務器的響應信息,如果沒有響應阻塞等待
int r=read(lfd, buf, 128);
if(strcmp(buf,"EXIT")==0) break;
//將獲取到的響應信息輸出到顯示器
write(1, buf, r);
printf("\n");
}
//關閉和服務器的連接,結束通訊
close(lfd);
return 0;
}
补充:setsockopt(2)解决ip地址重用问题(地址重用是因为传输层以下需要在内核态实现,刚结束使用的地址资源还没被回收,马上重用该地址就会发生地址重用问题)
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int getsockopt(int sockfd, int level, int optname,
void *optval, socklen_t *optlen);
int setsockopt(int sockfd, int level, int optname,
const void *optval, socklen_t optlen);
三、基于UDP的编程模型及其实现
- 使用UDP进行网络通讯,根本不需要建立连接
- 模型
//服务端:
//1.创建一个socket通讯端点
//2.将这个socket绑定到本地地址
while(1){
//3.使用recvfrom阻塞等待客户端数据的到来
//4.处理从客户端获取的数据
//5.将结果响应给客户端
}
//客户端:
//1.创建一个socket
//2.使用这个socket向服务器发送消息
//3.阻塞等待服务器的响应消息
//4.处理响应消息
//5.关闭socket,结束客户端的进程
- sendto(2) \ recvfrom(2)
#include <sys/types.h>
#include <sys/socket.h>
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
//功能:在socket上发送一条消息
//参数
//sockfd:指定了socket,在这个socket上发送消息
//buf:指定了存储要发送的数据的地址
//len:要发送的数据的长度
//flags:0(还有其他的可以看man帮助)
//dest_addr:指定了目标地址
//addrlen:指定了dest_addr指向的地址空间大小
//返回值:成功返回发送出去的字节数;错误返回-1,errno被设置
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
//功能:从socket上接收一条消息
//参数
//sockfd:指定了socket,在这个socket上接收消息
//buf:指定了地址,用这个地址开始空间存储消息
//len:指定了buf空间的大小
//flags:0(还有其他的可以看man帮助)
//src_addr:指定的地址空间里填充客户端的地址家族的内容
//addrlen:值-结果参数,指定了addr的有效空间大小
//返回值:成功返回接收的字节数;错误返回-1
eg:编程实现UDP通讯的服务端和客户端
userver.c
#include <stdio.h>
#include "t_net.h"
#include <ctype.h>
int main(void)
{
SA4 serv, clie;
char buf[128];
socklen_t cli_len;
//創建基於udp的socket
int fd=socket(AF_INET, SOCK_DGRAM, 0);
if(fd==-1){
perror("socket");
return -1;
}
//初始化服務器的ip地址和端口號
serv.sin_family=AF_INET;
serv.sin_port=htons(4000);
serv.sin_addr.s_addr=htonl(INADDR_ANY);
//將fd綁定到本地的ip地址和端口號
int b=bind(fd, (SA *)&serv, sizeof(serv));
if(b==-1){
perror("bind");
return -1;
}
while(1){
cli_len=sizeof(SA4);
//從客戶端獲取數據
int rcv=recvfrom(fd, buf, 128, 0, (SA *)&clie, &cli_len);
if(rcv==-1){
perror("recvfrom");
return -1;
}
int i;
for(i=0; i<rcv; i++){
buf[i]=toupper(buf[i]);
}
//將處理結果發送給客戶端
int s=sendto(fd, buf, rcv, 0, (SA *)&clie, sizeof(SA4));
if(s==-1){
perror("sendto");
return -1;
}
}
return 0;
}
uclient.c
#include <stdio.h>
#include "t_net.h"
#include <string.h>
#include <unistd.h>
int main(int argc, char* argv[])
{
char *msg="this is a test...\n";
SA4 serv;
char buf[128];
//創建一個socket,基於udp的
int fd=socket(AF_INET, SOCK_DGRAM, 0);
if(fd==-1){
perror("socket");
return -1;
}
//初始化服務器的ip地址和端口號
serv.sin_family=AF_INET;
serv.sin_port=htons(4000);
inet_pton(AF_INET, argv[1], &serv.sin_addr);
//向服務器發送消息
int s=sendto(fd,msg,strlen(msg),0,(SA *)&serv,sizeof(SA4));
if(s==-1){
perror("sendto");
return -1;
}
//阻塞等待服務器的響應消息
int rcv=recvfrom(fd,buf,128,0,NULL,NULL);
if(rcv==-1){
perror("recvfrom");
return -1;
}
//將服務器的響應消息輸出到顯示器
write(1, buf, rcv);
return 0;
}