并发服务器使用面向连接协议的步骤:
- 主1、创建套接字并将其绑定到所提供服务的熟知地址上。让该套接字保持为无连接的
- 主2、将该端口设置为被动模式
- 主3、反复调用accept以便接收来自客户的下一个连接请求,并创建新的从线程或者进程来处理响应
- 从1、由主线程传递来的连接请求开始
- 从2、用该连接与客户进行交互;读取请求并发回响应
- 从3、关闭连接并退出
在实现过程中,要用单程进程实现并发,需要注意的是在fork()之后,分别对父子进程进行的处理。
父子进程分别拥有一个主套接字和一个从套接字
所以,在处理时,要把父进程中的从套接字close()掉,把子进程中的主套接字close()掉。
当然,这里的close不是真正关闭掉套接字,而是让主套接字在子进程中消失,从套接字在父进程中消失。
服务器端(linux)代码:
//server_linux.c
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <sys/signal.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#define QLEN 32
#define BUFSIZE 1024
//extern int errno;
// int errexit(const char* format, ...);
unsigned short portbase = 0;
int passivesock(/*const char* service,*/ const char *transport, int qlen){
struct servent *pse;
struct protoent *ppe;
struct sockaddr_in sin;
int s, type;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = htons(8888);
//if(pse = getservbyname(service, transport))
// sin.sin_port = htons(ntohs((unsigned short)pse->s_port) + portbase);
//else if((sin.sin_port = htons((unsigned short)atoi(service))) == 0)
// printf("can not get \" %s \" service entry \n", service);
//if((ppe = getprotobyname(transport)) == 0)
// printf("can not get \" %s \" protocol entry \n", transport);
//if(strcmp(transport, "udp") == 0)
// type = SOCK_DGRAM;
//else
type = SOCK_STREAM;
s = socket(PF_INET, type, 0/*ppe->p_proto*/);
if(s < 0){
printf("can not create socket...");
}
if(bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0){
printf("can not bind the port...");
}
if(type == SOCK_STREAM && listen(s, qlen) < 0)
printf("can not listen the port...");
return s;
}
int passiveTCP(/*const char* service, */ int qlen){
return passivesock(/*service, */"tcp", QLEN);
}
int processFile(int ssock){
char filename[100];
char filepath[100];
//char *filepath = "C:\Users\liky\Desktop\windowToLinux.txt";
FILE *fp;
char *buffer;
int fileTrans;
memset(filename,'\0',sizeof(filename));
memset(filepath,'\0',sizeof(filepath));
buffer = (char *)malloc(sizeof(char)*BUFSIZE);
bzero(buffer,BUFSIZE);
int lenfilepath = recv(ssock,filepath,100,0);
printf("filepath :%s\n",filepath);
if(lenfilepath < 0){
printf("recv error!\n");
return 1;
}else{
int i=0,k=0;
for(i=strlen(filepath);i>=0;i--)
{
if(filepath[i]!='/'){
k++;
}else
break;
}
strcpy(filename,filepath+(strlen(filepath)-k)+1);
}
printf("filename :%s\n",filename);
fp = fopen(filename,"w");
if(fp!=NULL){
while(fileTrans = recv(ssock,buffer,BUFSIZE,0)){
if(fileTrans<0){
printf("recv error!\n");
break;
}
int writelength = fwrite(buffer,sizeof(char),fileTrans,fp);
if(writelength < fileTrans){
printf("write error!\n");
break;
}
bzero(buffer,BUFSIZE);
}
printf("recv finished!\n");
fclose(fp);
}else{
printf("filename is null!\n");
return 1;
}
close(ssock);
printf("the process is killed\n");
//kill(spid, SIGTERM);
return 0;
}
//清除僵尸进程
void reaper(){
int status;
while(wait3(&status, WNOHANG, (struct rusage*)0) >= 0)
;
}
int main(int argc, char *argv[]){
char* service;
struct sockaddr_in fsin;
unsigned int alen;
int msock;
int ssock;
switch (argc)
{
case 1:
break;
case 2:
service = argv[1];
break;
default:
printf("usage: TCP port error...");
}
msock = passiveTCP(/*service, */ QLEN);
// signal
(void)signal(SIGCHLD, reaper); //子进程结束时发出的信号
while(1){
alen = sizeof(fsin);
sleep(2);
ssock = accept(msock, (struct sockaddr *)&fsin, &alen);
if(ssock < 0){
printf("accpet error...\n");
continue;
}else{
printf("accpet successful\n");
}
switch(fork()){
case 0:
(void) close(msock);
printf("a new process is created\n");
//处理函数
exit(processFile(ssock));
case 1:
(void) close(ssock);
break;
case -1:
printf("fork error...");
}
}
}
客户端(linux)代码:(此处写的比较随意)
//client_linux.c
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <string.h>
#include <netinet/in.h>
#include <errno.h>
#include <memory.h>
#include <stdlib.h> //for malloc
#define BUFFER_SIZE 1024
int main()
{
int sockcd;
struct sockaddr_in server;
char filepath[100];//file to translate
FILE *fp;
int lenpath; //filepath length
char *buffer;
int fileTrans;
buffer = (char *)malloc(sizeof(char)*BUFFER_SIZE);
bzero(buffer,BUFFER_SIZE);
//memset(buffer,0,sizeof(buffer));
if((sockcd = socket(AF_INET,SOCK_STREAM,0))<0)
{
printf("socket build error!\n");
}
memset(&server,0,sizeof(server));
server.sin_family= AF_INET;
server.sin_port = htons(8888);
if(inet_pton(AF_INET,"127.0.0.1",&server.sin_addr)<0)
{
printf("inet_pton error!\n");
}
if(connect(sockcd,(struct sockaddr*)&server,sizeof(server))<0)
{
printf("connect error!\n");
}//connect with server
printf("file path:\n");
scanf("%s",filepath);//get filepath
fp = fopen(filepath,"r");//opne file
if(fp==NULL)
{
printf("filepath not found!\n");
return 0;
}
printf("filepath : %s\n",filepath);
lenpath = send(sockcd,filepath,strlen(filepath),0);// put file path to sever
if(lenpath<0)
{
printf("filepath send error!\n");
}
else
{
printf("filepath send success!\n");
}
sleep(1);
while((fileTrans = fread(buffer,sizeof(char),BUFFER_SIZE,fp))>0)
{
printf("fileTrans =%d\n",fileTrans);
if(send(sockcd,buffer,fileTrans,0)<0)
{
printf("send failed!\n");
break;
}
bzero(buffer,BUFFER_SIZE);
//memset(buffer,0,sizeof(buffer));
}
fclose(fp);
close(sockcd);
return 0;
}
此外,还可以在windows平台下设置客户端,实现跨平台传输:
不过要注意,由于linux和windows中目录形式不同,所以要将server代码中的 filepath[i]!='/' 改为 filepath[i]!='\\'
//client_windows.c
#pragma comment(lib,"ws2_32.lib")
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <winsock2.h>
#include <errno.h>
#include <memory.h>
#include <stdlib.h>
#define BUFFER_SIZE 1024
int main()
{
SOCKET sockcd;
struct sockaddr_in server;
char filepath[100];//file to translate
FILE *fp;
int lenpath; //filepath length
char *buffer;//file buffer
int fileTrans;
buffer = (char *)malloc(sizeof(char)*BUFFER_SIZE);
memset(buffer,0,BUFFER_SIZE);
WSADATA WSAData;
if(WSAStartup(MAKEWORD(2,0),&WSAData)==SOCKET_ERROR) //WSAStartup()函数对Winsock DLL进行初始化
{
printf("Socket initialize fail!\n");
//continue;
} //客户端进程创建套接字
if((sockcd=socket(AF_INET,SOCK_STREAM,0))==SOCKET_ERROR) //创建流套接字(与服务端保持一致)
{
printf("Socket create fail!\n");
WSACleanup();
//continue;
}
memset(&server,0,sizeof(server));
server.sin_family= AF_INET;
server.sin_port = htons(8888);
//server.sin_addr.s_addr = htonl("127.0.0.1");
server.sin_addr.S_un.S_addr = inet_addr("192.168.251.131"); //此处是你的linux的本地地址
if(connect(sockcd,(struct sockaddr*)&server,sizeof(server)) == SOCKET_ERROR)
{
printf("connect error!\n");
closesocket(sockcd);
WSACleanup();
}//connect with server
printf("file path:\n");
scanf("%s",filepath);//get filepath
fp = fopen(filepath,"r");//opne file
if(fp==NULL)
{
printf("filepath not found!\n");
return -1;
}
printf("filepath : %s\n",filepath);
lenpath = send(sockcd,filepath,strlen(filepath),0);// put file path to sever
if(lenpath<0)
{
printf("filepath send error!\n");
}
else
{
printf("filepath send success!\n");
}
Sleep(100);
while((fileTrans = fread(buffer,sizeof(char),BUFFER_SIZE,fp))>0)
{
printf("fileTrans =%d\n",fileTrans);
if(send(sockcd,buffer,fileTrans,0) < 0)
{
printf("send failed!\n");
break;
}
memset(buffer,0,BUFFER_SIZE);
}
fclose(fp);
closesocket(sockcd);
WSACleanup();
return 0;
}
文件自动保存到server同一目录下。