您的位置:首页 > 理论基础 > 计算机网络

MS(三) socket server client 网络编程

2015-09-17 17:19 513 查看

一些参数说明

/*****************************************************************************************************---------------------------------------------------------------------------------------------------------------------sockaddr_in结构体struct sockaddr_in{short sin_family;/*Addressfamily一般来说AF_INET(地址族)PF_INET(协议族)*/unsigned short sin_port;/*Portnumber(必须要采用网络数据格式,普通数字可以用htons()函数转换成网络数据格式的数字)*/struct in_addr sin_addr;/*Internetaddress*/unsigned char sin_zero[8];/*Samesizeasstructsockaddr没有实际意义,只是为了 跟SOCKADDR结构在内存中对齐*/};在linux下:struct in_addr sin_addr;in_addr结构typedef struct in_addr{unsigned long s_addr;};---------------------------------------------------------------------------------------------------------------------#include<sys/socket.h>int setsockopt(int sockfd, int level, int optname, const void* optval, socklen_t* optlen);//sockfd要设置的目的套接字//level套接字的控制层次//optname optval optlen是三个相关的参数,通过不同的搭配可以设置不同的功能 http://www.cnblogs.com/coder2012/archive/2013/04/02/2995889.html -----------------------------------------------------------------------------------------------------------------------server_addr.sin_addr.s_addr = htonl(INADDR_ANY);sin_addr.s_addr是ip地址。作为服务器,你要绑定【bind】到本地的IP地址上进行监听【listen】,但是你的机器上可能有多块网卡,也就有多个IP地址,这时候你要选择绑定在哪个IP上面。如果指定为INADDR_ANY,那么系统将绑定默认的网卡【即IP地址】。作为客户端,你要连接【connect】到远端的服务器,也是要指定远端服务器的(ip, port)对。当然,在这种情况下,不可能将IP地址指定为INADDR_ANY,系统会疯掉的。-----------------------------------------------------------------------------------------------------------------------socket(AF_INET, SOCK_STREAM, 0);AF_INET IPV4SOCK_STREAM SOCK_STREAM //提供面向连接的稳定数据传输,即TCP协议int socket(int domain, int type, int protocol);domain:即协议域,又称为协议族(family)type:指定socket类型protocol:故名思意,就是指定协议常用的协议有:IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议当protocol为0时,会自动选择type类型对应的默认协议。 http://blog.csdn.net/linbounconstraint/article/details/48495259 -------------------------------------------------------------------------------------------------------------------------bind()int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);sockfd:即socket描述符addr:一个const struct sockaddr *指针,指向要绑定给sockfd的协议地址。这个地址结构根据地址创建socket时的地址协议族的不同而不同。addrlen:对应的是地址的长度---------------------------------------------------------------------------------------------------------------------------listen() connect()如果作为一个服务器,在调用socket()、bind()之后就会调用listen()来监听这个socket如果客户端这时调用connect()发出连接请求,服务器端就会接收到这个请求。int listen(int sockfd, int backlog);int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);listen函数的第一个参数即为要监听的socket描述字,第二个参数为相应socket可以排队的最大连接个数。socket()函数创建的socket默认是一个主动类型的,listen函数将socket变为被动类型的,等待客户的连接请求。connect函数的第一个参数即为客户端的socket描述字,第二参数为服务器的socket地址,第三个参数为socket地址的长度。客户端通过调用connect函数来建立与TCP服务器的连接。------------------------------------------------------------------------------------------------------------------------accept()函数TCP服务器端依次调用socket()、bind()、listen()之后,就会监听指定的socket地址了。TCP客户端依次调用socket()、connect()之后就想TCP服务器发送了一个连接请求。TCP服务器监听到这个请求之后,就会调用accept()函数取接收请求,这样连接就建立好了。之后就可以开始网络I/O操作了,即类同于普通文件的读写I/O操作。int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);accept函数的第一个参数为服务器的socket描述字,第二个参数为指向struct sockaddr *的指针,用于返回客户端的协议地址,第三个参数为协议地址的长度。如果accpet成功,那么其返回值是由内核自动生成的一个全新的描述字,代表与返回客户的TCP连接。注意:accept的第一个参数为服务器的socket描述字,是服务器开始调用socket()函数生成的,称为监听socket描述字;而accept函数返回的是已连接的socket描述字。一个服务器通常通常仅仅只创建一个监听socket描述字,它在该服务器的生命周期内一直存在。内核为每个由服务器进程接受的客户连接创建了一个已连接socket描述字,当服务器完成了对某个客户的服务相应的已连接socket描述字就被关闭。---------------------------------------------------------------------------------------------------------------recv() send()#include <sys/socket.h>ssize_t recv(int sockfd, void *buff, size_t nbytes, int flags);ssize_t send(int sockfd, const void *buff, size_t nbytes, int flags);recv 和send的前3个参数等同于read和write。recv函数sockfd: 接收端套接字描述符buff: 用来存放recv函数接收到的数据的缓冲区nbytes: 指明buff的长度flags: 一般置为0send解析sockfd:指定发送端套接字描述符。buff: 存放要发送数据的缓冲区nbytes: 实际要改善的数据的字节数flags: 一般设置为0 http://www.cnblogs.com/blankqdb/archive/2012/08/30/2663859.html ------------------------------------------------------------------------------------------------------------fread函数原型:size_t fread(void* buff,size_t size,size_t count,FILE* stream)作用:从文件中读入数据到指定的地址中参数:第一个参数为接收数据的指针(buff),也即数据存储的地址第二个参数为单个元素的大小,即由指针写入地址的数据大小,注意单位是字节第三个参数为元素个数,即要读取的数据大小为size的元素个素第四个参数为提供数据的文件指针,该指针指向文件内部数据返回值:读取的总数据元素个数 http://www.cnblogs.com/Romi/archive/2012/02/29/2374769.html ******************************************************************************************/

server服务器

/*******************************************************
-----------------------------------------------
sockaddr_in结构体
struct sockaddr_in
{

short sin_family;
/*Addressfamily一般来说AF_INET(地址族)PF_INET(协议族)*/
unsigned short sin_port;
/*Portnumber(必须要采用网络数据格式,普通数字可以用htons()函数转换成网络数据格式的数字)*/
struct in_addr sin_addr;
/*Internetaddress*/
unsigned char sin_zero[8];
/*Samesizeasstructsockaddr没有实际意义,只是为了 跟SOCKADDR结构在内存中对齐*/

};

在linux下:
struct in_addr sin_addr;
in_addr结构
typedef struct in_addr
{
unsigned long s_addr;
};

------------------------------------------------
#include<sys/socket.h>
int setsockopt(int sockfd, int level, int optname, const void* optval, socklen_t* optlen);

//sockfd要设置的目的套接字
//level套接字的控制层次
//optname optval optlen是三个相关的参数,通过不同的搭配可以设置不同的功能 http://www.cnblogs.com/coder2012/archive/2013/04/02/2995889.html ------------------------------------------------
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);

sin_addr.s_addr是ip地址。
作为服务器,你要绑定【bind】到本地的IP地址上进行监听【listen】,
但是你的机器上可能有多块网卡,也就有多个IP地址,这时候你要选择绑定在哪个IP上面。
如果指定为INADDR_ANY,那么系统将绑定默认的网卡【即IP地址】。

作为客户端,你要连接【connect】到远端的服务器,也是要指定远端服务器的(ip, port)对。
当然,在这种情况下,不可能将IP地址指定为INADDR_ANY,系统会疯掉的。
------------------------------------------------
socket(AF_INET, SOCK_STREAM, 0);
AF_INET         IPV4
SOCK_STREAM     SOCK_STREAM //提供面向连接的稳定数据传输,即TCP协议

int socket(int domain, int type, int protocol);
domain:即协议域,又称为协议族(family)
type:指定socket类型
protocol:故名思意,就是指定协议
常用的协议有:
IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等
它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议
当protocol为0时,会自动选择type类型对应的默认协议。 http://blog.csdn.net/linbounconstraint/article/details/48495259 ------------------------------------------------
bind()

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockfd:即socket描述符
addr:一个const struct sockaddr *指针,指向要绑定给sockfd的协议地址。
这个地址结构根据地址创建socket时的地址协议族的不同而不同。
addrlen:对应的是地址的长度
------------------------------------------------
listen() connect()

如果作为一个服务器,在调用socket()、bind()之后就会调用listen()来监听这个socket
如果客户端这时调用connect()发出连接请求,服务器端就会接收到这个请求。
int listen(int sockfd, int backlog);
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
listen函数的
第一个参数即为要监听的socket描述字,
第二个参数为相应socket可以排队的最大连接个数。
socket()函数创建的socket默认是一个主动类型的,listen函数将socket变为被动类型的,等待客户的连接请求。

connect函数的
第一个参数即为客户端的socket描述字,
第二参数为服务器的socket地址,
第三个参数为socket地址的长度。
客户端通过调用connect函数来建立与TCP服务器的连接。
------------------------------------------------
accept()函数

TCP服务器端依次调用socket()、bind()、listen()之后,就会监听指定的socket地址了。
TCP客户端依次调用socket()、connect()之后就想TCP服务器发送了一个连接请求。
TCP服务器监听到这个请求之后,就会调用accept()函数取接收请求,这样连接就建立好了。
之后就可以开始网络I/O操作了,即类同于普通文件的读写I/O操作。

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
accept函数的
第一个参数为服务器的socket描述字,
第二个参数为指向struct sockaddr *的指针,用于返回客户端的协议地址,
第三个参数为协议地址的长度。
如果accpet成功,那么其返回值是由内核自动生成的一个全新的描述字,代表与返回客户的TCP连接。

注意:
accept的第一个参数为服务器的socket描述字,是服务器开始调用socket()函数生成的,
称为监听socket描述字;而accept函数返回的是已连接的socket描述字。一个服务器通常通常
仅仅只创建一个监听socket描述字,它在该服务器的生命周期内一直存在。内核为每个由服务
器进程接受的客户连接创建了一个已连接socket描述字,当服务器完成了对某个客户的服务
相应的已连接socket描述字就被关闭。
------------------------------------------------
recv() send()

#include <sys/socket.h>
ssize_t recv(int sockfd, void *buff, size_t nbytes, int flags);
ssize_t send(int sockfd, const void *buff, size_t nbytes, int flags);
recv 和send的前3个参数等同于read和write。

recv函数
sockfd: 接收端套接字描述符
buff:  用来存放recv函数接收到的数据的缓冲区
nbytes: 指明buff的长度
flags:  一般置为0

send解析
sockfd:指定发送端套接字描述符。
buff:  存放要发送数据的缓冲区
nbytes: 实际要改善的数据的字节数
flags: 一般设置为0 http://www.cnblogs.com/blankqdb/archive/2012/08/30/2663859.html ------------------------------------------------
fread
函数原型:size_t fread(void* buff,size_t size,size_t count,FILE* stream)
作用:从文件中读入数据到指定的地址中
参数:
第一个参数为接收数据的指针(buff),也即数据存储的地址
第二个参数为单个元素的大小,即由指针写入地址的数据大小,注意单位是字节
第三个参数为元素个数,即要读取的数据大小为size的元素个素
第四个参数为提供数据的文件指针,该指针指向文件内部数据
返回值:读取的总数据元素个数 http://www.cnblogs.com/Romi/archive/2012/02/29/2374769.html 
***********************************************************/

#include <stdio.h>      // for printf
#include <unistd.h>     //linux/unix的系统调用  for read,write,getpid等
#include <netinet/in.h> // for sockaddr_in
#include <sys/types.h>  // for socket
#include <sys/socket.h> // for socket
#include <stdio.h>      // for printf
#include <stdlib.h>     // for exit
#include <string.h>     // for bzero

#define PORT_NUM 6666      //端口号
#define LISTEN_MAX 20
#define FILENAME_LEN 512   //文件名长度
#define BUF_MAX_LEN 1024   //buf数组的长度

int main(int argc, char **argv)
{

/*
1.新建一个server主机地址信息
a.server的主机信息(AF_INET IPV4,端口号,sin_addr.s_addr INADDR_ANY)
b.创建socket
*/

//设置一个socket地址结构server_addr,代表服务器internet地址, 端口
//sockaddr_in 包含在<netinet/in.h>.  sockaddr_in是一个struct
struct sockaddr_in server_addr;
bzero(&server_addr, sizeof(server_addr));
//extern void bzero(void *s, int n); 置字节字符串s的前n个字节为零且包括'\0'

//AF_INET(又称 PF_INET)是 IPv4 网络协议的套接字类型,AF_INET6 则是 IPv6 的;而 AF_UNIX 则是 Unix 系统本地通信
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT_NUM);
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);

//建立socket
//创建用于internet的流协议(TCP)socket,用socket_server_fd代表服务器socket
int socket_server_fd = socket(AF_INET, SOCK_STREAM, 0);
if( socket_server_fd < 0) //if(socket_server_fd == -1)
{
perror("socket create failed\n");
exit(1);
}

int opt =1;
setsockopt(socket_server_fd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));

/*
2.bind
把socket和socket地址结构联系起
*/
//把socket和socket地址结构联系起
socklen_t server_addrlen = sizeof(server_addr);
if ( bind(socket_server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0 )
//if ( bind(socket_server_fd, (struct sockaddr *)&server_addr, server_addrlen) == -1)
{
perror("bind failed\n");
exit(1);
}

/*
3.listen 监听客户端的connect
*/
if( listen(socket_server_fd, LISTEN_MAX) < 0)
{
perror("listen failed\n");
exit(1);

}

/*
4.while(1) 循环 处理收到的请求
*/
while(1)
{

//建立client主机信息 定义客户端的socket地址结构client_addr
//struct sockaddr_in client_addr;
//bzero(&client_addr, sizeof(client_addr));
//client_addr.sin_family = AF_INET;
//client_addr.sin_port = htons(PORT_NUM);
//client_addr.sin_addr.s_addr = htonl(INADDR_ANY);

//接受一个到server_socket代表的socket的一个连接
//如果没有连接请求,就等待到有连接请求--这是accept函数的特性
//accept函数返回一个新的socket,这个socket(server_client_fd)用于同连接到的客户的通信
//server_client_fd代表了服务器和客户端之间的一个通信通道
//accept函数把连接到的客户端信息填写到客户端的socket地址结构client_addr中

/*
5.accept() 建立server和client的链接,返回一个可用于信息交换的描述符server_client_fd
*/

////定义客户端的socket地址结构client_addr
struct sockaddr_in client_addr;
socklen_t client_addrlen = sizeof(client_addr);
int server_client_fd = accept(socket_server_fd, (struct sockaddr *)&client_addr, &client_addrlen);
if(server_client_fd < 0) //if(server_client_fd == -1)
{
perror("accept failed\n");
exit(1);
}

//int fread_len = fread(filename, sizeof(char), FILENAME_LEN, server_client_fd);

//从客户端读取数据
//recv:
//接收client的数据,参数一是accept客户端(client)后产生的描述符
//参数二,是buf,参数三是buf的长度,参数四一般为0,具体参考开头的说明及链接

char buf[BUF_MAX_LEN];  //1024
bzero(buf, BUF_MAX_LEN);//数组初始化,清0
ssize_t recv_len = recv(server_client_fd, buf, BUF_MAX_LEN, 0);
if (recv_len < 0)
{
printf("Server Recieve Data Failed!\n");
break;
}

//client 端 发送要down的文件的文件名,server接收改文件名
//然后从本地打开该文件,并将数据每次以1024的buf大小,发送过去
char filename[FILENAME_LEN]; //512
bzero(filename, FILENAME_LEN);
strncpy(filename, buf, strlen(buf)>FILENAME_LEN ? FILENAME_LEN : strlen(buf));

printf("%s\n",filename);
FILE *fp = fopen(filename, "r");
if(fp == NULL)
{
perror("fopen failed....ll\n");
exit(1);
}

//begin send file  开始发送数据
//fread 函数原型:size_t fread ( void *buffer, size_t size, size_t count, FILE *stream) ;
//参一:用于接收数据的内存地址 参二:要读的每个数据项的字节数,单位是字节
//参三:要读count个数据项,每个数据项size个字节  参四:stream 输入流
printf("send %s...\n", filename);
int file_block_length;
while( (file_block_length = fread(buf, sizeof(char), BUF_MAX_LEN, fp)) > 0 )
{

printf("file_block_length(fread_buf_len) = %d\n",file_block_length);
//发送buff中的字符串到server_client_fd实际是给客户端
//fwrite(buf, sizeof(char), file_block_length, server_client_fd);
//if( write(server_client_fd, buf, file_block_length)  < 0 )

if( send(server_client_fd, buf, file_block_length, 0) < 0 )
{
printf("Send File:\t%s Failed\n", filename);
break;
}

bzero(buf, BUF_MAX_LEN);
}

printf("send %s ok\n", filename);
fclose(fp);  //关闭文件描述符
close(server_client_fd);  //关闭accept客户端client后创建的描述符

}

close(socket_server_fd);  //关闭监听用的socket描述符

return 0;
}

Client 客户端

<pre name="code" class="cpp">/*******************************************************
-----------------------------------------------
sockaddr_in结构体
struct sockaddr_in
{

short sin_family;
/*Addressfamily一般来说AF_INET(地址族)PF_INET(协议族)*/
unsigned short sin_port;
/*Portnumber(必须要采用网络数据格式,普通数字可以用htons()函数转换成网络数据格式的数字)*/
struct in_addr sin_addr;
/*Internetaddress*/
unsigned char sin_zero[8];
/*Samesizeasstructsockaddr没有实际意义,只是为了 跟SOCKADDR结构在内存中对齐*/

};

在linux下:
struct in_addr sin_addr;
in_addr结构
typedef struct in_addr
{
unsigned long s_addr;
};

------------------------------------------------
#include<sys/socket.h>
int setsockopt(int sockfd, int level, int optname, const void* optval, socklen_t* optlen);

//sockfd要设置的目的套接字
//level套接字的控制层次
//optname optval optlen是三个相关的参数,通过不同的搭配可以设置不同的功能 http://www.cnblogs.com/coder2012/archive/2013/04/02/2995889.html ------------------------------------------------
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);

sin_addr.s_addr是ip地址。
作为服务器,你要绑定【bind】到本地的IP地址上进行监听【listen】,
但是你的机器上可能有多块网卡,也就有多个IP地址,这时候你要选择绑定在哪个IP上面。
如果指定为INADDR_ANY,那么系统将绑定默认的网卡【即IP地址】。

作为客户端,你要连接【connect】到远端的服务器,也是要指定远端服务器的(ip, port)对。
当然,在这种情况下,不可能将IP地址指定为INADDR_ANY,系统会疯掉的。
------------------------------------------------
socket(AF_INET, SOCK_STREAM, 0);
AF_INET         IPV4
SOCK_STREAM     SOCK_STREAM //提供面向连接的稳定数据传输,即TCP协议

int socket(int domain, int type, int protocol);
domain:即协议域,又称为协议族(family)
type:指定socket类型
protocol:故名思意,就是指定协议
常用的协议有:
IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等
它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议
当protocol为0时,会自动选择type类型对应的默认协议。 http://blog.csdn.net/linbounconstraint/article/details/48495259 ------------------------------------------------
bind()

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockfd:即socket描述符
addr:一个const struct sockaddr *指针,指向要绑定给sockfd的协议地址。
这个地址结构根据地址创建socket时的地址协议族的不同而不同。
addrlen:对应的是地址的长度
------------------------------------------------
listen() connect()

如果作为一个服务器,在调用socket()、bind()之后就会调用listen()来监听这个socket
如果客户端这时调用connect()发出连接请求,服务器端就会接收到这个请求。
int listen(int sockfd, int backlog);
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
listen函数的
第一个参数即为要监听的socket描述字,
第二个参数为相应socket可以排队的最大连接个数。
socket()函数创建的socket默认是一个主动类型的,listen函数将socket变为被动类型的,等待客户的连接请求。

connect函数的
第一个参数即为客户端的socket描述字,
第二参数为服务器的socket地址,
第三个参数为socket地址的长度。
客户端通过调用connect函数来建立与TCP服务器的连接。
------------------------------------------------
accept()函数

TCP服务器端依次调用socket()、bind()、listen()之后,就会监听指定的socket地址了。
TCP客户端依次调用socket()、connect()之后就想TCP服务器发送了一个连接请求。
TCP服务器监听到这个请求之后,就会调用accept()函数取接收请求,这样连接就建立好了。
之后就可以开始网络I/O操作了,即类同于普通文件的读写I/O操作。

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
accept函数的
第一个参数为服务器的socket描述字,
第二个参数为指向struct sockaddr *的指针,用于返回客户端的协议地址,
第三个参数为协议地址的长度。
如果accpet成功,那么其返回值是由内核自动生成的一个全新的描述字,代表与返回客户的TCP连接。

注意:
accept的第一个参数为服务器的socket描述字,是服务器开始调用socket()函数生成的,
称为监听socket描述字;而accept函数返回的是已连接的socket描述字。一个服务器通常通常
仅仅只创建一个监听socket描述字,它在该服务器的生命周期内一直存在。内核为每个由服务
器进程接受的客户连接创建了一个已连接socket描述字,当服务器完成了对某个客户的服务
相应的已连接socket描述字就被关闭。
------------------------------------------------
recv() send()

#include <sys/socket.h>
ssize_t recv(int sockfd, void *buff, size_t nbytes, int flags);
ssize_t send(int sockfd, const void *buff, size_t nbytes, int flags);
recv 和send的前3个参数等同于read和write。

recv函数
sockfd: 接收端套接字描述符
buff:  用来存放recv函数接收到的数据的缓冲区
nbytes: 指明buff的长度
flags:  一般置为0

send解析
sockfd:指定发送端套接字描述符。
buff:  存放要发送数据的缓冲区
nbytes: 实际要改善的数据的字节数
flags: 一般设置为0 http://www.cnblogs.com/blankqdb/archive/2012/08/30/2663859.html ------------------------------------------------
fread
函数原型:size_t fread(void* buff,size_t size,size_t count,FILE* stream)
作用:从文件中读入数据到指定的地址中
参数:
第一个参数为接收数据的指针(buff),也即数据存储的地址
第二个参数为单个元素的大小,即由指针写入地址的数据大小,注意单位是字节
第三个参数为元素个数,即要读取的数据大小为size的元素个素
第四个参数为提供数据的文件指针,该指针指向文件内部数据
返回值:读取的总数据元素个数 http://www.cnblogs.com/Romi/archive/2012/02/29/2374769.html 
***********************************************************/

#include <arpa/inet.h>
#include <stdio.h>      // for printf
#include <unistd.h>     //linux/unix的系统调用  for read,write,getpid等
#include <netinet/in.h> // for sockaddr_in
#include <sys/types.h>  // for socket
#include <sys/socket.h> // for socket
#include <stdlib.h>     // for exit
#include <string.h>     // for bzero

/*
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
*/

#define PORT_NUM 6666      //端口号
#define LISTEN_MAX 20      //listen() 相应socket可以排队的最大连接个数
#define FILENAME_LEN 512   //文件名长度
#define BUF_MAX_LEN 1024   //buf数组的长度

int main(int argc, char **argv)
{

if (argc != 2)
{
printf("Usage: ./%s ServerIPAddress\n",argv[0]);
exit(1);
}

/*
1.创建一个client的socketaddr_in结构体 client_addr
*/

//设置一个socket地址结构client_addr,代表客户机(client)的internet地址, 端口
struct sockaddr_in client_addr;
bzero(&client_addr, sizeof(client_addr));           //把一段内存区的内容全部设置为0
client_addr.sin_family = AF_INET;                   //internet协议族  IPV4
client_addr.sin_addr.s_addr = htons(INADDR_ANY);    //INADDR_ANY表示自动获取本机地址
client_addr.sin_port = htons(0);                    //0表示让系统自动分配一个空闲端口

/*
2.建立socket() 返回的描述符为client_socket
创建用于internet的流协议(TCP)socket,用client_socket代表客户机socket
*/

//创建用于internet的流协议(TCP)socket,用client_socket代表客户机socket
int client_socket = socket(AF_INET,SOCK_STREAM,0);
if( client_socket < 0)
{
printf("Create Socket Failed!\n");
exit(1);
}

/*
作为client,并不需要bind。
tcp服务端必须有bind, 客户端通常不用bind。
本code虽然绑定,但是注意:client的client_addr中client_addr.sin_port = htons(0);也就是说htons(0)
为0时,0表示让系统自动分配一个空闲端口。也算是就算是bind了,也是让系统自己分配端口号。和不bind
效果一样,所以可以直接不bind。

实际上,客户端的这个端口号是操作系统随机分配的,在分配的时候,操作系统会保证不与现有的端口冲突。
虽然也可以bind。但是客户端用bind的程序很容易出问题操作系统指定的不会冲突的随机端口难道不比你自
己指定的容易冲突的固定端口好。在很多场景下,我们要在一个pc上开启多个客户端进程, 如果指定固定端
口,必然会造成端口冲突,影响通信。所以,我们就不要费力不讨好了,客户端就不要指定端口了,让操作系
统来搞。这样,实际上就是操作系统对客户端的socket进行了隐式的命名(名称中的端口是随机的)。 http://blog.csdn.net/stpeace/article/details/45001255 
3.bind()绑定  把客户机的socket(返回的描述符)和客户机的socket地址结构(client_addr)联系起来
*/
/*

//把客户机的socket和客户机的socket地址结构联系起来
if( bind(client_socket,(struct sockaddr*)&client_addr,sizeof(client_addr)))
{
printf("Client Bind Port Failed!\n");
exit(1);
}
*/

/*
设置服务器的server_addr的信息
*/
//设置一个socket地址结构server_addr,代表服务器的internet地址, 端口
struct sockaddr_in server_addr;
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT_NUM);
if(inet_aton(argv[1], &server_addr.sin_addr) == 0) //服务器的IP地址来自程序的参数
{
printf("Server IP Address Error!\n");
exit(1);
}

/*
4.connect() 连接后,client_socket描述符即是:server和client交换数据的描述符

*/
socklen_t server_addr_length = sizeof(server_addr);
//向服务器发起连接,连接成功后client_socket代表了客户机和服务器的一个socket连接
//客户端通过调用connect函数来建立与TCP服务器的连接
if(connect(client_socket, (struct sockaddr*)&server_addr, server_addr_length) < 0)
{
printf("Can Not Connect To %s!\n",argv[1]);
exit(1);
}

char filename[FILENAME_LEN];    //创建文件名的存储buf
bzero(filename, FILENAME_LEN);  //清空为0
char buf[BUF_MAX_LEN];
bzero(buf, BUF_MAX_LEN);

printf("Please Input File Name On Server(You get's filename):\n");
scanf("%s", filename);

//向服务器发送filename中的数据,即向server发送要down下来的文件的文件名
//strncpy(buf, filename, strlen(filename) > BUF_MAX_LEN ? BUF_MAX_LEN : strlen(filename) );
//send(client_socket, buff, strlen(buff), 0);
send(client_socket, filename, strlen(filename), 0);

//创建文件,为从server接受数据做准备
FILE * fp = fopen(filename, "w");
if(NULL == fp )
{
printf("File:\%s Can Not Open To Write\n", filename);
exit(1);
}

//从服务器接收数据到buf中
bzero(buf,BUF_MAX_LEN);
int recv_buf_length = 0; //从服务器接受到的字节长度
while( recv_buf_length = recv(client_socket, buf, BUF_MAX_LEN, 0) )
{
if(recv_buf_length < 0)
{
printf("Recieve Data From Server %s Failed!\n", argv[1]);
break;
}

int write_length = fwrite(buf, sizeof(char), recv_buf_length, fp);
if( write_length < recv_buf_length)
{
printf("File:%s Write Failed\n", filename);
break;
}
bzero(buf,BUF_MAX_LEN); //写完注意清空buf
}
printf("Recieve File:%s From Server[%s] Finished\n", filename, argv[1]);

fclose(fp);
close(client_socket);//关闭socket

return 0;
}
代码下载:点击打开链接使用:./server                 //启动服务器./client 服务器IP地址    //启动客户端服务器输入要下载的东西,即可。

                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: