概述
最近做了一个简单的程序,结合之前学过的东西组装起来的一个小demo
这个小项目是一个简单的Tcp传输文件,在Ubuntu16.04下实现,使用了pthread多线程,能够承受简单的多并发情况。(我还实现了一个小功能 客户机向服务器发送ls 能够列出服务器的文件)
这个程序分为服务器与客户端,编译之后,启动程序传入两个参数,一个是服务器要绑定的ip,一个是服务器要绑定的端口(服务器启动要用自己的ip哦)。下面就是查看本机ip,编译,启动程序的演示。
然后就是客户端,这个程序实现多线程高并发,客户端是不需要开线程的,所以写起来非常的简单。
大致功能如下:
在终端下的程序输入输出 交互逻辑不太好搞 我已经比较尽力了 多客户端我就懒得调试截图上传了。代码贴在下面,服务器记得带上pthread参数。
最近学习写了很多的程序 有空的话 我会选一些比较好的上传的。有问题记得评论 我看见了会及时回复的。
//这是服务器的代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
typedef struct Accept
{
struct sockaddr_in cid_addr;
int cid;
} Accept_t;
int Socket(int domain, int type, int protocol);
void Bind(int sockfd, const char *ip, short Port);
void Listen(int sockfd, int backlog);
void Print_cid(const struct sockaddr_in *addr);
void Print_cid_quit(const struct sockaddr_in *addr);
void *start_routine(void *arg);
int main(int argc, char *argv[])
{
if (argc != 3)
{
printf("参数错误n");
exit(-1);
}
int sid = Socket(AF_INET, SOCK_STREAM, 0); //服务端
int cid = -1; //客户端
Bind(sid, argv[1], atoi(argv[2]));
Listen(sid, 5);
struct sockaddr_in cid_addr;
socklen_t len = sizeof(cid_addr);
pthread_t newthread;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
Accept_t Accept;
while(1)
{
cid = accept(sid, (struct sockaddr *)&cid_addr, &len);
Accept.cid_addr=cid_addr;
Accept.cid=cid;
if (cid < 0)
{
perror("accept:");
exit(-1);
}
Print_cid(&cid_addr);
pthread_create(&newthread,&attr,start_routine,&Accept);
}
return 0;
}
/************************************************
* int Socket(int domain, int type, int protocol)
* 功能:建立socket套接字对象
* 参数:
* domain:域:用作基于什么类型操作
* Name Purpose Man page
* AF_UNIX, AF_LOCAL 局部通信 unix(7) 重点
* AF_INET IPv4网络协议 ip(7) 重点
* AF_INET6 IPv6网络协议 ipv6(7) 重点
* AF_IPX IPX - Novell协议
* AF_NETLINK 内核用户界面设备 netlink(7)
* AF_X25 ITU-T X.25 / ISO-8208协议 x25(7)
* AF_AX25 业余无线电AX.25协议
* AF_ATMPVC 访问原始ATM pvc
* AF_APPLETALK 可路由协议组 ddp(7)
* AF_PACKET 低层包接口 packet(7)
* AF_ALG 内核加密API接口
* type:套接字类型
* SOCK_STREAM:流式套接字 唯一对应 TCP协议
* SOCK_DGRAM:数据报套接字 唯一对应 UDP协议
* SOCK_RAW:原始套接字
* protocol:根据套接字类型,一般默认填0,如果式原始套接字等则需要
* 返回值:
* 成功:返回特殊的文件描述符
* 失败:-1,且errno存储错误类型
* *********************************************/
int Socket(int domain, int type, int protocol)
{
int sid = socket(domain, type, protocol);
if (sid < 0)
{
perror("socket:");
exit(-1);
}
return sid;
}
/************************************************
* 函数名:void Bind(int sockfd, const char *ip, short Port)
* 功能:为任务绑定ip和端口号
* 参数:
* sockfd:通过socket函数拿到的文件特殊描述符
* addr:struct sockaddr的结构体变量的首地址
* addrlen:struct sockaddr的结构体变量的长度
* 返回值:
* 成功:0
* 失败:-1,并且填写errno
* *********************************************/
void Bind(int sockfd, const char *ip, short Port)
{
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(Port);
addr.sin_addr.s_addr = inet_addr(ip);
if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
{
perror("bind:");
exit(-1);
}
}
/************************************************
* 函数名:void Listen(int sockfd, int backlog)
* 功能:监听套接字,将主动套接字变成被动套接字
* 参数:
* sockfd:通过socket函数拿到的文件特殊描述符
* backlog:定义了队列的最大长度,最多客户端同时访问,一般填5
* 如果一个连接请求当队列已满时到达,客户端可能会收到一个错误指示ECO
* NNREFUSED或,如果底层 协议支持重传时,请求可能会被忽略,以便稍
* 后重试连接成功。
* 返回值:
* 成功:0
* 失败:-1,并且填写errno
* *********************************************/
void Listen(int sockfd, int backlog)
{
if (listen(sockfd, backlog) == -1)
{
perror("listen:");
exit(-1);
}
}
/************************************************
* 函数名:Print_cid(const struct sockaddr_in *addr)
* 功能:打印套接字内容
* 参数:
* addr:套接字地址
* 返回值:
* 无
* *********************************************/
void Print_cid(const struct sockaddr_in *addr)
{
printf("类型是%sn", addr->sin_family == AF_INET ? "AFINET" : "AF_INET6");
printf("ip地址为:%sn端口号:%dn", inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
}
/************************************************
* 函数名:void Print_cid_quit(const struct sockaddr_in *addr)
* 功能:当客户机正常退出FTP服务器时 输出套接字信息
* 参数:
* addr:套接字地址
* 返回值:
* 无
* *********************************************/
void Print_cid_quit(const struct sockaddr_in *addr)
{
printf("ip地址为:%st端口号:%dt退出了传输 程序结束n", inet_ntoa(addr->sin_addr), ntohs(addr->sin_port)); //打印ip和端口号
}
/************************************************
* 函数名:void *start_routine(void *arg)
* 功能:服务器创建的线程 每当一个客户机建立会话时 都会新建一个线程
* 参数:
* arg:创建建立连接的套接字
* 返回值:
* 无
* *********************************************/
void *start_routine(void *arg)
{
int fd,trafd;
int flag=0;
Accept_t *Accept=(Accept_t *)arg;
struct sockaddr_in cid_addr =Accept->cid_addr;
int cid=Accept->cid;
char buf[1024]="";
char filenameTemp[1024];
char dirread[1024]="";
int buf_len=0;
while(1)
{
memset(buf,0,sizeof(buf));
buf_len=read(cid,buf,sizeof(buf)-1);
printf("接收到数据长度为%d的数据为%sn", buf_len, buf);
strcpy(filenameTemp,buf);
filenameTemp[buf_len-1]='