TCP、UDP 网络编程
实验目的
1.使用 TCP 进行套接字通信
2.使用 UDP 进行套接字通信 实验原理
1.TCP
2.UDP
3.用到的 API
(1)int socket(int domain, int type, int protocol);
根据指定的地址族、数据类型和协议来分配一个 socket 的描述字及其所用的资源。 domain:协议族,常用的有 AF_INET、AF_INET6、AF_LOCAL、AF_ROUTE 其中 AF_INET 代表使用 ipv4 地址
type:socket 类型,常用的 socket 类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、
SOCK_SEQPACKET 等
protocol:协议。常用的协议有,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC 等
(2)int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
把一个地址族中的特定地址赋给 socket
sockfd:socket 描述字,也就是 socket 引用
addr:要绑定给 sockfd 的协地址
addrlen:地址的长度
通常服务器在启动的时候都会绑定一个众所周知的地址(如 ip 地址+端口号),用于提供服务,客 户就可以通过它来接连服务器;而客户端就不用指定,有系统自动分配一个端口号和自身的 ip 地 址组合。这就是为什么通常服务器端在 listen 之前会调用 bind(),而客户端就不会调用,而是在 connect()时由系统随机生成一个
(3)int listen(int sockfd, int backlog);
监听 socket
sockfd:要监听的 socket 描述字
backlog:相应 socket 可以排队的最大连接个数
(4)int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
连接某个 socket
sockfd:客户端的 socket 描述字
addr:服务器的 socket 地址
addrlen:socket 地址的长度
(5)int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
TCP 服务器监听到客户端请求之后,调用 accept()函数取接收请求
sockfd:服务器的 socket 描述字
addr:客户端的 socket 地址
addrlen:socket 地址的长度
(6)ssize_t read(int fd, void *buf, size_t count);
读取 socket 内容
fd:socket 描述字
buf:缓冲区
count:缓冲区长度
(7)ssize_t write(int fd, const void *buf, size_t count); 向 socket 写入内容,其实就是发送内容
fd:socket 描述字
buf:缓冲区
count:缓冲区长度
(8)int close(int fd);
socket 标记为以关闭 ,使相应 socket 描述字的引用计数-1,当引用计数为 0 的时候,触发 TCP 客户 端向服务器发送终止连接请求。
实验内容
1.TCP
(1)server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#define PORT 11111
#define LISTENQ 10 // 可排队的最大连接数
int main() {
//TCP、UDP都只有一种协议满足条件,可以将 protocol 的值设为 0,系统会自动推演出应该使用什么协议
int fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);//AF_INNT Ipv4地址 创建套接字
struct sockaddr_in addr;
addr.sin_family = AF_INET; //使用IPv4地址
addr.sin_port = htons(PORT); //绑定端口PORT
// serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //具体的IP地址
addr.sin_addr.s_addr = INADDR_ANY; //INADDR_ANY 任意地址
//将套接字绑定到 固定IP和固定端口
bind(fd, (struct sockaddr*)&addr, sizeof(addr));//绑定 进行端口和IP地址的绑定
// 将套接字设置为监听模式,等待连接到来
listen(fd, LISTENQ);//监听 //监听队列为LISTENQ
int fd_;
struct sockaddr_in addr_;
int len = sizeof(addr_);
int cnt = 0;
while(1){
printf("等待连接.......\n");
if ((fd_ = accept(fd, (struct sockaddr*)&addr_, &len)) < 0) {//调用 accept 接受请求
printf("error\n");
}
else{
char read_buf[1024] = { 0 };
char *write_buf = "Hello,客户端 client!";
int recvlen = 0;
cnt ++;
printf("success:%d\n", cnt);
// 通过read()、write()接受和发送数据
/*
read(fd_, read_buf,sizeof(read_buf));
printf("%s\n", read_buf);
write(fd_, write_buf,strlen(write_buf));
*/
// 通过recv()、send()接受和发送数据,接收并打印客户端数据
if( ( recvlen = recv(fd_, read_buf, sizeof(read_buf), 0) ) > 0){
printf("%s\n", read_buf);
}
// 发送服务器端数据
send(fd_, write_buf, strlen(write_buf), 0);
}
}
// 关闭连接
close(fd_);
close(fd);
return 0;
}
(2)client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#define PORT 11111
#define IP "127.0.0.1"
int main() {
int fd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字 流格式套接字(SOCK_STREAM)
// SOCK_STREAM 是一种可靠的、双向的通信数据流,数据可以准确无误地到达另一台计算机,如果损坏或丢失,可以重新发送。
//向服务器(特定的IP和端口)发起请求
struct sockaddr_in addr;
addr.sin_family = AF_INET; //使用IPv4地址
addr.sin_port = htons(PORT); //端口
addr.sin_addr.s_addr = inet_addr(IP); //具体的IP地址
//inet_pton(AF_INET, IP, &(addr.sin_addr.s_addr)); //访问的服务器IP 具体的IP地址
// 向服务器发出连接请求
printf("发起连接.......\n");
if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {//建立连接
printf("error\n");
}
else {
char read_buf[1024] = { 0 };
char *write_buf = "Hello,服务器 server!";
int recvlen = 0;
printf("success\n");
// 通过write()、read()发送和接受数据
/* write(fd, write_buf, strlen(write_buf));
read(fd, read_buf, sizeof(read_buf) -1 );
printf("%s\n", read_buf);
*/
// 通过recv()、send()发送和接受数据,接收并打印客户端数据
send(fd, write_buf, strlen(write_buf), 0);
// 接收并打印客户端数据
if( ( recvlen = recv(fd, read_buf, sizeof(read_buf), 0) )){
printf("%s\n", read_buf);
}
}
//关闭套接字
close(fd);
return 0;
}
2.UDP
(1)server_.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#define PORT 11111
#define BUFFSIZE 1024
int main() {
int fd = socket(AF_INET, SOCK_DGRAM, 0);//参数和 tcp 不同
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
addr.sin_addr.s_addr = INADDR_ANY;
bind(fd, (struct sockaddr*)&addr, sizeof(addr));//无需 listen
char buff[BUFFSIZE];
socklen_t len = sizeof(addr);
int cnt = 0;
//接收数据,把数据原封不动发回给客户
while(1){
int n = recvfrom(fd, buff, BUFFSIZE, 0, (struct sockaddr*)&addr, &len);
if( n == 0 ){
break;
}
cnt ++;
sprintf(buff, "%s:%d", buff, cnt);
sendto(fd, buff, n, 0, (struct sockaddr*)&addr, len);
}
close(fd);
return 0;
}
(2)client_.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#define PORT 11111
#define BUFFSIZE 1024
int main() {
int fd = socket(AF_INET, SOCK_DGRAM, 0); // 数据报格式套接字(SOCK_DGRAM)
// 数据报套接字是一种不可靠的、不按顺序传递的、以追求速度为目的的套接字
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
char buff[BUFFSIZE] = "服务器被连接的次数:";
struct sockaddr_in addr_;
socklen_t len = sizeof(addr);
//发送数据,打印从服务器发回的数据
sendto(fd, buff, BUFFSIZE, 0, (struct sockaddr*)&addr, len);
recvfrom(fd, buff, BUFFSIZE, 0, (struct sockaddr*)&addr_, &len);
printf("%s\n", buff);
close(fd);
return 0;
}
实验结果
TCP:
UDP: