一、相关函数
1、inet_ntop() //网络字节序IP转字符串
SYNOPSIS
#include <arpa/inet.h>
const char *inet_ntop(int af, const void *src,char *dst, socklen_t size);
==> af : 地址族 AF_INET ==> src : 需要转换的网络字节序的IP变量的地址 ==> dst : 转换之后的IP存放的位置 ==> size : 第三个参数的size
2、Send()
NAME send — send a message on a socket
SYNOPSIS
#include <sys/socket.h>
ssize_t send(int socket, const void *buffer, size_t length, int flags);
==> socket : TCP套接字 ==> buffer : 需要发送的数据 ==> length : 发送的数据的大小 ==> flags : 数据属性 0 (如果flags赋值0,那么send就相当于write)
3、Recv()
NAME recv — receive a message from a connected socket
SYNOPSIS
#include <sys/socket.h>
ssize_t recv(int socket, void *buffer, size_t length, int flags);
==> socket : TCP套接字 ==> buffer : 需要接收的数据存放的缓冲区 ==> length : 接收的数据的大小 ==> flags : 数据属性 0 (如果flags赋值0,那么recv就相当于read)
二、服务器连接多个客户端
实现一个服务器同时连接多个客户端,可以接收每一个客户端的信息。
==> 分析: 服务器在初始化套接字之后,TCP套接字会升级为监听套接字,监听套接字可以循环的被客户端连接,每次客户端连接成功,accept函数会返回一个会话ID。
==> 实现方案: 服务器初始化监听套接字之后,主线程循环的等待客户端连接,每次有一个客户端成功连接,那就创建一条线程去循环接收这个会话ID里面的内容
例子: 设计一个服务器端程序,服务器可以最多同时连接20个客户端,每次有客户端成功连接,那就打印”客户端ip[192.168.15.xx],port[xxx]连接成功!”, 然后这个客户发送信息都可以接收到。
例子1:
实现一个服务器转发信息功能。服务器循环接收客户端的连接,客户端连接成功之后可以给服务器发送数据,服务器接收到数据之后就给每一个连接的客户端进行转发。
服务器
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
int cli_num;
int talkfd[20];
void*Recv_Msg(void *arg)
{
int fd = *(int *)arg;
char buf[1024];
while(1)
{
memset(buf,0, sizeof(buf));
if(recv(fd, buf, sizeof(buf), 0) < 0)
{
break;
}
printf("recv[%d]:%s", fd, buf);
for(int i=0; i<cli_num; i++)
{
send(talkfd[i], buf, sizeof(buf), 0);
}
}
printf("client [%d] is disconnect!\n", fd);
close(fd);
}
int main(int argc, char *argv[])
{
if(argc != 2)
{
printf("按照格式执行程序:%s <port>\n", argv[0]);
return -1;
}
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0)
{
perror("socket failed");
return -1;
}
struct sockaddr_in server_addr, client_addr;
socklen_t addrlen = sizeof(server_addr);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(atoi(argv[1]));
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(sockfd, (struct sockaddr *)&server_addr, addrlen) < 0)
{
perror("bind failed");
return -1;
}
listen(sockfd, 16);
pthread_t tid;
char ip_addr[20] = {0};
while(1)
{
if(cli_num >= 20)
{
printf("客户端已满!\n");
sleep(1);
continue;
}
talkfd[cli_num] = accept(sockfd, (struct sockaddr *)&client_addr, &addrlen);
if(-1 == talkfd[cli_num])
{
printf("服务器炸了!\n");
continue;
}
inet_ntop(AF_INET, &(client_addr.sin_addr), ip_addr, sizeof(ip_addr));
printf("client ip[%s], port[%d] is connect talkfd:%d\n", ip_addr, ntohs(client_addr.sin_port), talkfd[cli_num]);
pthread_create(&tid, NULL, Recv_Msg, (void *)&talkfd[cli_num]);
cli_num++;
}
close(sockfd);
return 0;
}
客户端
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
pthread_t tid1, tid2;
void *Recv_Msg(void *arg)
{
int fd = *(int *)arg;
char buf[1024];
while(1)
{
memset(buf, 0, sizeof(buf));
if( read(fd, buf, sizeof(buf)) <= 0)
{
pthread_cancel(tid2);
pthread_exit(NULL);
}
printf("readbuf:%s", buf);
if( strcmp("QUIT_TALK\n", buf) == 0 )
{
pthread_cancel(tid2);
pthread_exit(NULL);
}
}
}
void *Send_Msg(void *arg)
{
int fd = *(int *)arg;
char buf[1024];
while(1)
{
memset(buf, 0, sizeof(buf));
fgets(buf, sizeof(buf), stdin);
write(fd, buf, sizeof(buf));
if( strcmp("QUIT_TALK\n", buf) == 0 )
{
pthread_cancel(tid1);
pthread_exit(NULL);
}
}
}
int main(int argc, char *argv[])
{
if(argc != 3)
{
printf("请按照格式执行程序:%s <port> <IP>\n", argv[0]);
return -1;
}
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == sockfd)
{
perror("socket failed");
return -1;
}
struct sockaddr_in server_addr;
socklen_t addrlen = sizeof(server_addr);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(atoi(argv[1]));
server_addr.sin_addr.s_addr = inet_addr(argv[2]);
if(connect(sockfd, (struct sockaddr *)&server_addr, addrlen) < 0 )
{
perror("connect failed");
return -1;
}
pthread_create(&tid1, NULL, Recv_Msg, (void *)&sockfd);
pthread_create(&tid2, NULL, Send_Msg, (void *)&sockfd);
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
close(sockfd);
return 0;
}
左上为服务器,其他3为客户端,在3个终端页面运行同一个客户端代码就能产生3个客户端。
三、TCP传输控制协议实现文件传输
TCP传输协议是一个可靠的传输协议,在通信之前需要先建立连接。
1、TCP通信中的三次握手与四次分手
2、实现文件传输的步骤
1)发送方
==> 给发送方文件路径名 (判断文件是否存在,不存在就结束,如果存在,那就获取文件信息: 文件名字,大小,属性…)
==> 连接到接收方 --> connect
==> 第一个数据包包含文件的相关信息 (文件名,文件大小…)
==> 循环发送文件内容 --> 直到文件内容被发送完毕,结束。
==> 断开连接,结束程序
2)接收方
==> 初始化网络连接 --> accept();
==> 接收第一个数据包,对数据包进行解析,得到文件的相关信息
==> 在本地创建一个同名文件
==> 循环接收数据,写入新文件 (直到接收到的数据大小等于文件大小时停止接收)
==> 断开连接,结束程序。
例子2:
tcp传输文件 接收方
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == sockfd)
{
perror("socket failed");
return -1;
}
struct sockaddr_in server_addr, client_addr;
socklen_t addrlen = sizeof(server_addr);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(9999);
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(sockfd, (struct sockaddr *)&server_addr, addrlen) < 0)
{
perror("bind failed");
return -1;
}
listen(sockfd, 1);
int talkfd;
long filesize, recvsize = 0;
size_t ret;
char filename[32] = {0};
char ip_addr[20] = {0};
char MsgBuf[1024] = {0};
talkfd = accept(sockfd, (struct sockaddr *)&client_addr, &addrlen);
inet_ntop(AF_INET, &(client_addr.sin_addr), ip_addr, sizeof(ip_addr));
printf("发送方 ip[%s], port[%d]\n", ip_addr, ntohs(client_addr.sin_port));
recv(talkfd, MsgBuf, sizeof(MsgBuf), 0);
sscanf(MsgBuf, "%ld:%s", &filesize, filename);
printf("接收的文件名:%s, 文件大小:%ld\n",filename, filesize);
FILE *fp = fopen(filename, "w");
if(NULL == fp)
{
perror("fopen failed");
return -1;
}
while(1)
{
memset(MsgBuf, 0, sizeof(MsgBuf));
ret = recv(talkfd, MsgBuf, sizeof(MsgBuf), 0);
fwrite(MsgBuf, 1, ret, fp);
recvsize += ret;
if(recvsize >= filesize)
break;
}
fclose(fp);
close(talkfd);
close(sockfd);
return 0;
}
发送方
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
#include <sys/stat.h>
int main(int argc, char *argv[])
{
if(argc != 2)
{
printf("安装格式执行:%s <文件路径名>\n", argv[0]);
return -1;
}
if( access(argv[1], F_OK) )
{
printf("发送的文件不存在!\n");
return -1;
}
struct stat buf;
stat(argv[1], &buf);
printf("发送的文件:%s, 大小:%ld\n", argv[1], buf.st_size);
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == sockfd)
{
perror("socket failed");
return -1;
}
struct sockaddr_in recv_addr;
socklen_t addrlen = sizeof(recv_addr);
recv_addr.sin_family = AF_INET;
recv_addr.sin_port = htons(9999);
recv_addr.sin_addr.s_addr = inet_addr("192.168.15.3");
if(connect(sockfd, (struct sockaddr *)&recv_addr, addrlen) < 0 )
{
perror("connect failed");
return -1;
}
char MsgBuf[1024];
memset(MsgBuf, 0, sizeof(MsgBuf));
sprintf(MsgBuf, "%ld:%s", buf.st_size, argv[1]);
send(sockfd, MsgBuf, sizeof(MsgBuf), 0);
FILE *fp = fopen(argv[1], "r");
if(NULL == fp)
{
perror("fopen failed");
return 0;
}
size_t ret;
while(1)
{
ret = fread(MsgBuf, 1, sizeof(MsgBuf), fp);
send(sockfd, MsgBuf, ret, 0);
if(ret <= 0)
break;
}
printf("文件发送结束!\n");
fclose(fp);
close(sockfd);
return 0;
}
四、UDP传输协议实现通信
1、UDP协议特点
·面向非连接 (不需要建立连接 --> 效率高) ·不可靠传输协议 (不保证数据一定正确到达)
==> 一般在网络环境不太稳定的情况下需要使用UDP协议
2、UDP通信实现流程 – 寄信
==> 例子: 设计UDP通信的发送端和接收端功能代码。
相关函数:
1)Sendto() //–> 发送一个UDP数据包
SYNOPSIS
#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);
==> sockfd : 套接字 ==> buf : 需要发送的数据内容 ==> len : 需要发送的数据大小 ==> flags : 数据标志 0 ==> dest_addr : 对方的IP地址结构体 ==> addrlen : 地址结构体大小 返回值: 成功返回实际发送的字节数,失败返回-1
2)recvform() //–> 接收数据
SYNOPSIS
#include <sys/socket.h>
ssize_t recvfrom(int socket, void *restrict buffer, size_t length,
int flags, struct sockaddr *restrict address,
socklen_t *restrict address_len);
==> sockfd : 套接字 ==> buf : 存放接收的的数据内容的缓冲区 ==> len : 想要接收的数据的大小 ==> flags : 数据标志 0 ==> dest_addr : 发送的IP地址结构体 ==> addrlen : 地址结构体大小 返回值: 成功返回实际接收的字节数,失败返回-1
|