再次开始网络编程已经是一个学期之后了。
上个学期完成网络编程之后还没有来得及总结,最近重新开始网络编程还遇到了不少的麻烦。
直奔主题:
在服务器端实现的功能:1.通过TCP/IP协议,获取客户端发送的文件。
2.定义了几个简单的指令,对客户端进行控制。
功能特点:能同时处理最多五个客户端的请求。
服务器端的编程:
在开启网络的时候,我们首先要约定好网络协议以及服务器端开启的端口。
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <stdlib.h>
struct sockaddr_in serv_addr; //一个包含服务器端信息的结构体
struct sockaddr_in clnt_addr; //客户端的结构体
int init_networking(int Port)
{
int sock;
sock=socket(PF_INET,SOCK_STREAM,0); //定义一个流式套接字
memset(&serv_addr,0,sizeof(serv_addr)); //为结构体开辟空间
serv_addr.sin_family=AF_INET; //定义协议
serv_addr.sin_addr.s_addr=htonl(INADDR_ANY); //使用本地IP地址
serv_addr.sin_port=htons(Port); //使用的端口号
if(bind(sock,(struct sockaddr*) & serv_addr,
sizeof(serv_addr))==-1) //绑定端口号
perror("bind "); //在 stdlib.h中有定义
if(listen(sock,5)==-1) //在对应端口开始监听,监听的套接字最多为5个
perror("listen ");
return sock; //返回一个本地服务器的套接字
}
#include <pthread.h> //包含对线程函数的定义
主函数:初始化网络,对每个接入服务器的客户端开启一个线程进行相应的处理。
int main(void)
{
int server_sock; //服务器的套接字
server_sock=init_networking(1000); //初始化服务器套接字
pthread_t newthread; //创建一个线程的标志
int client_sock; //定义一个客户端套接字
int clnt_addr_size; //定义客户端IP地址大小
clnt_addr_size=sizeof(clnt_addr);
while(1)
{
client_sock=accept(server_sock,(struct sockaddr*) &clnt_addr,&clnt_addr_size); //接受一个客户端的请求,为客户端分配一个套接字
perror("accept");
if (pthread_create(&newthread , NULL,accept_request, client_sock) != 0)
perror("pthread_create"); //创建一个线程处理之前的客户端请求
}
close(server_sock);
return 0;
}
处理客户端的请求:
一旦客户端连接上服务器,客户端会向服务器发送一个对应的连接模式(控制模式OR图片传输模式)
void accept_request(int client_sock)
{
int mode_select,i;
/× 接收客户端的连接模式的选择位 ×/
recv(client_sock,&mode_select,sizeof(int),MSG_WAITALL);
if(mode_select==1)
{
for(i=0;i<3;i++)
{
convert_picture(client_sock); //进入图像传输模式
}
}
if(mode_select==2)
{
cmd_control(client_sock); //进入控制指令模式
}
}
图片传输和控制函数在此不做详细的解释,因为它的通用性不强,主要是对文件的数据包的组装,以及传输相应的控制指令让客户端有相应的应答。
客户端的设计主要思路:
1.使用两套套接字,一套套接字用于文件传输,另一套套接字用于控制指令传输。
2.在主函数中创建一个控制线程,线程的生命周期和主函数一样,这样能够实时的控制主函数。
3.进程和线程的通讯使用全局变量的方式
特点:
使用两套套接字的好处就是能够将传输数据和控制指令隔离开,能够相互不影响,降低出错的概率。
客户端的实现:
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
int serv_sock,serv_sock2;
int init_convert(const char *IP_addr,int Port)
{
serv_sock=socket(PF_INET,SOCK_STREAM,0); //开启套接字1
serv_sock2=socket(PF_INET,SOCK_STREAM,0);//开启套接字2
if(serv_sock==-1)
perror("socket");
memset(&serv_addr,0,sizeof(serv_addr));
serv_addr.sin_family=AF_INET;
serv_addr.sin_addr.s_addr=inet_addr(IP_addr);
serv_addr.sin_port=htons(Port);
/× 套接字1连接服务器 ×/
if(connect(serv_sock,(struct sockaddr*) & serv_addr,sizeof(serv_addr))==-1)
perror("connect1 ");
/× 套接字2连接服务器 ×/
if(connect(serv_sock2,(struct sockaddr*) & serv_addr,sizeof(serv_addr))==-1)
perror("connect2 ");
int mode;
mode=1;
send(serv_sock,&mode,sizeof(int),MSG_WAITALL); //套接字一用于图片传输
mode=2;
send(serv_sock2,&mode,sizeof(int),MSG_WAITALL);//套接字二用于控制指令传输
return 0;
}
主函数中开启了一个线程,直到主函数进程结束才结束。这样我们就能在程序的生命周期内对程序进行实时的控制。
pthread_t cmd_thread;
if(pthread_create(&cmd_thread , NULL,cmd_convert,NULL) != 0)
perror("pthread_create");
这个进程用于传输控制指令,控制指令与主函数的通信方式,采用全局变量的方式。
不足:在文件传输的过程中,我的函数缺乏高可用性。在线程间的通讯方式可以改为signal的方式。