tcp/ip客户端与服务器

by admin on 2019年9月24日

– 项目简述

  实现客户端调用摄像头,并以帧的形式将每一帧传输到服务端,服务端将图片进行某些处理后再返回给客户端。(客户端与服务端通信代码部分参考《Qt5
开发及实例》)

单击“发送数据”把数据发送到指定IP地址的指定端口号

总体需求: 编写tcp文件服务器和客户端。客户端可以上传和下载文件。

================================================

采用tcp实现聊天服务器和客户端
采用tcp实现聊天服务器和客户端,客户端从命令行输入聊信息,服务器收到聊天信息发送给所有连接到此服务器的客户端。
要求:
此服务器端口号为:8888,
聊天信息格式为:消息头(4字节,标示后续消息体聊天信息的长度) + 消息体
支持100个客户端的连接和发送接收操作。 //多线程
客户端输入exit就断开连接并退出程序

目标:
  完成一个精简TCP服务器,可接收来自多个用户的请求,并返回结果。

– 项目步骤

图片 1

分解需求

服务器要能响应客户端的主动断开请求

思路:
  (1)服务器
      C++ TCP服务器的实现主要由以下几个函数来完成:
        a)socket
     创建服务器监听套接字
  b)bind
     绑定服务器监听信息到套接字上
  c)listen
     开始监听,接收客户端的TCP连接
  d)accept
    
从listen所维护的队列中取出一条已连接的TCP,返回该连接的socket描述字
        e)服务器客户端在连接socket描述字上进行消息通信
  f) close
     关闭打开着的套接字
     
为了更好的服务多个发起请求的客户端,在e步骤上,我们使用fork以派生子进程来独立处理每个客户端的请求。
      if( (childpid=fork())==0)
     {
      close(listenfd);        
//从父进程复制下来的监听socket描述符要关闭。
      //communication(connfd);
      exit(0);
     }
(2)客户端
    客户端的实现主要由以下函数完成:
     a)socket
        创建客户端连接套接字
    b)connect
    向指定服务器发起连接请求
     c)服务器客户端在连接socket描述字上进行消息通信
     d)close
        关闭打开着的套接字

– 客户端的编写

  •   通过Qt Designer画出如下界面

图片 2

  • 在客户端工程文件“client、pro”中加入 QT +=
    network语句,并将环境路径配置好。
  • 在头文件“client.h”中加入如下代码:图片 3图片 4

     1 class Client : public QWidget 2 { 3     Q_OBJECT 4  5 public: 6     explicit Client(QWidget *parent = 0); 7     ~Client(); 8  9 private slots:10     void slotEnter();11     void slotConnected();12     void slotDisconnected();13     void dataReceived();14     void slotSend();15     void nextFrame();16 signals:17     void connectedToServer(void);18 19 private:20     Ui::Client *ui;21     bool status;22     int port;23     QHostAddress *serverIP;24     QString userName;25     QTcpSocket *tcpSocket;26 27     qint64 receiveBlockSize;28 29     Mat frame;30     VideoCapture cap;31     QTimer* timer;32     QImage image;33 34     void ShowImage(QByteArray ba);35     QByteArray picToData(cv::Mat frame);36     QImage Mat2QImage(cv::Mat cvImg);37 };
    

    View Code

  • 在源文件“client.cpp”的构造函数中加入如下代码:图片 3图片 6

     1 Client::Client(QWidget *parent) : 2     QWidget, 3     ui(new Ui::Client) 4 { 5     ui->setupUi(this); 6  7     setWindowFlags(windowFlags()&~Qt::WindowMaximizeButtonHint); 8     setFixedSize(this->width(),this->height; 9 10     status=false;11     port=8010;12     ui->portLineEdit->setText(QString::number;13 14     serverIP=new QHostAddress();15     ui->serverIPLineEdit->setText("127.0.0.1");16     connect(ui->ConnectPushButton,SIGNAL(clicked(bool)),this,SLOT(slotEnter;17 18     receiveBlockSize=0;19 20     timer=new QTimer(this);21     timer->start(30);22 23     cap.open(0);24     if(!cap.isOpened25     {26         QMessageBox::information(this,tr("error"),tr("cam don't open"));27     }28 29     connect(timer,&QTimer::timeout,this,&Client::nextFrame);30 }
    

    View Code

槽函数nextFrame()通过调用openCV,实现获取每一帧图像:

图片 3图片 8

 1 void Client::nextFrame() 2 { 3     cap>>frame; 4     if (!frame.empty 5     { 6         image = Mat2QImage; 7      8         //使图像适应label的大小 9         QPixmap *pixmap = new QPixmap(QPixmap::fromImage;10         pixmap->scaled(ui->capturedImage->size(), Qt::KeepAspectRatio);11         ui->capturedImage->setScaledContents(true);12         ui->capturedImage->setPixmap(*pixmap);13         delete pixmap;//避免内存泄漏14     }15 }

View Code

函数Mat2QImage(cv::Mat cvImg)代码如下:

图片 3图片 10

 1 QImage Client::Mat2QImage(cv::Mat cvImg) 2 { 3     QImage qImg; 4     if(cvImg.channels()==3)                             //3 channels color image 5     { 6  7         cv::cvtColor(cvImg,cvImg,CV_BGR2RGB); 8         qImg =QImage((const unsigned char*)(cvImg.data), 9                      cvImg.cols, cvImg.rows,10                      cvImg.cols*cvImg.channels(),11                      QImage::Format_RGB888);12     }13     else if(cvImg.channels()==1)                    //grayscale image14     {15         qImg =QImage((const unsigned char*)(cvImg.data),16                      cvImg.cols,cvImg.rows,17                      cvImg.cols*cvImg.channels(),18                      QImage::Format_Indexed8);19     }20     else21     {22         qImg =QImage((const unsigned char*)(cvImg.data),23                      cvImg.cols,cvImg.rows,24                      cvImg.cols*cvImg.channels(),25                      QImage::Format_RGB888);26     }27     return qImg;28 }

View Code

  以上代码中,槽函数slotEnter()实现连接和断开服务器功能(但现在断开后再连上数据传输有问题),代码如下:

图片 3图片 12

 1 void Client::slotEnter() 2 { 3     if(!status) 4     { 5         QString ip=ui->serverIPLineEdit->text(); 6         if(!serverIP->setAddress 7         { 8             QMessageBox::information(this,tr("error"), 9                                      tr("server ip address error"));10             return;11         }12 13         tcpSocket=new QTcpSocket(this);14 15         connect(tcpSocket,&QAbstractSocket::connected,this,&Client::slotConnected);16 17         connect(tcpSocket,&QAbstractSocket::disconnected,this,&Client::slotDisconnected);18 19         connect(tcpSocket,&QIODevice::readyRead,this,&Client::dataReceived);20 21         connect(this,&Client::connectedToServer,this,&Client::slotSend);22 23         tcpSocket->connectToHost(*serverIP,port);24 25 26         status=true;27     }28     else29     {30 31         tcpSocket->disconnectFromHost();32 33         status=false;34     }35 }

View Code

槽函数“slotConnected()”是connected()信号的响应槽,当与服务器连接成功后,客户端进行一些操作,具体代码如下:

图片 3图片 14

1 void Client::slotConnected()2 {3     ui->handPushButton->setEnabled(true);4     ui->facePushButton->setEnabled(true);5     ui->ConnectPushButton->setText(tr("Leave"));6     //ui->ConnectPushButton->setEnabled;7     emit connectedToServer();8 }

View Code

槽函数“slotDisconnected()”是disconnected()信号的响应槽,当与服务器断开连接时,进行如下操作:

图片 3图片 16

1 void Client::slotDisconnected()2 {3     ui->handPushButton->setEnabled(false);4     ui->facePushButton->setEnabled(false);5     ui->ConnectPushButton->setText(tr("Enter"));6 }

View Code

  

  当发出connectedToServer()信号后,槽函数slotSend()响应,具体代码如下:

图片 3图片 18

 1 void Client::slotSend() 2 { 3     if(frame.empty 4         return; 5  6     QByteArray byteArrayData=picToData; 7     QByteArray ba; 8     QDataStream out(&ba,QIODevice::WriteOnly); 9     out.setVersion(QDataStream::Qt_5_6);10 11     out<<0;12     out<<byteArrayData;13     out.device()->seek(0);14     out<<-sizeof;15     tcpSocket->write;16 }

View Code

  其中函数picToData(cv::Mat frame)代码如下:

图片 3图片 20

 1 QByteArray Client::picToData(Mat frame) 2 { 3     QImage img((unsigned const char*)frame.data,frame.cols,frame.rows,QImage::Format_RGB888); 4  5     QByteArray block; 6     QBuffer buf(&block); 7     img.save(&buf,"JPEG");//按照JPG解码保存数据 8     QByteArray cc = qCompress(block,1); 9     QByteArray hh;10     hh=cc.toBase64();//base64数据11     return hh;12 }

View Code

  函数dataReceived()当客户端收到服务端数据时响应,代码如下:

图片 3图片 22

 1 void Client::dataReceived() 2 { 3     ui->noteLabel->setText(tr("receive data")); 4     QDataStream in(tcpSocket); 5     QByteArray message;//存放从服务器接收到的字符串 6     in.setVersion(QDataStream::Qt_5_6); 7     if (receiveBlockSize==0) 8     { 9         //判断接收的数据是否有两字节10         //如果有则保存到basize变量中,没有则返回,继续接收数据11         if (tcpSocket->bytesAvailable()<(int)sizeof12         {13             return;14         }15         in>>receiveBlockSize;16     }17     //如果没有得到全部数据,则返回继续接收数据18     if (tcpSocket->bytesAvailable()<receiveBlockSize)19     {20         //qDebug()<<"tcpSocket->bytesAvailable()<receiveBlockSize";21         return;22     }23     in>>message;//将接收到的数据存放到变量中24     ShowImage;25     receiveBlockSize=0;26     emit connectedToServer();27 }

View Code

  最后的emit
connectedToServer();实现每次发送一次数据给服务端后,等待服务端响应后再发送数据,避免一直发数据,同时又接收数据而出现问题。

  最后,将处理后的图像显示,调用函数ShowImage(QByteArray
ba)实现,代码如下:

图片 3图片 24

 1 void Client::ShowImage(QByteArray ba) 2 { 3     QString ss=QString::fromLatin1,ba.size; 4     QByteArray rc; 5     rc=QByteArray::fromBase64(ss.toLatin1; 6     QByteArray rdc=qUncompress; 7     QImage img; 8     img.loadFromData; 9     ui->processedImage->setPixmap(QPixmap::fromImage;10     ui->processedImage->resize(img.size;11     update();12 }

View Code

  

  最后的最后,不要忘了调用析构函数:

图片 3图片 26

1 Client::~Client()2 {3     delete ui;4     delete tcpSocket;5     delete timer;6     delete serverIP;7 }

View Code

 

客户端功能描述:

1)要求支持以下命令:
help: 显示客户端所有命令和说明,
在本地实现即可,help的内容不需要从服务器传回。
list: 显示服务器端可下载文件列表,列表内容需要从服务器端传回。
get <filename>:
根据<filename>下载指定文件,<filename>只包含文件名,如果出现”/”等字符任务是路径,不予支持;下载后的文件存放在当前工作路径下即可。
put <pathname>: 上传文件 <pathname>
必须为客户端本机的合法路径,客户端搜索到文件后推送给服务器
quit: 退出客户端
2)客户端启动后可以反复输入命令,除非用户输入quit才会退出。
3) 每次命令(list/get/put)建立一个连接,命令执行完毕后关闭该连接。

实现:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Threading;

namespace tcpClient
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

        }

        private void Form1_Load(object sender, EventArgs e)
        {
            txtSendMssg.Text = "ceshifasong";
            Thread thrListener = new Thread(new ThreadStart(Listen));
            thrListener.Start();
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            Environment.Exit(0);
        }



        private void btnSend_Click(object sender, EventArgs e)
        {
            TcpClient tcpClient = new TcpClient();
            tcpClient.Connect(IPAddress.Parse("127.0.0.1"), 2014);
            //tcpClient.Connect(IPAddress.Parse("192.168.1.111"), 9300);
            NetworkStream ntwStream = tcpClient.GetStream();
            if (ntwStream.CanWrite)
            {
                Byte[] bytSend = Encoding.UTF8.GetBytes(txtSendMssg.Text);
                ntwStream.Write(bytSend, 0, bytSend.Length);
            }
            else
            {
                MessageBox.Show("无法写入数据流");

                ntwStream.Close();
                tcpClient.Close();

                return;
            }

            ntwStream.Close();
            tcpClient.Close();

        }

        private void Listen()
        {
            Socket listener = new Socket(AddressFamily.InterNetwork,
                SocketType.Stream, ProtocolType.Tcp);
            listener.Bind(new IPEndPoint(IPAddress.Any, 9300));

            //不断监听端口
            while (true)
            {
                listener.Listen(0);
                Socket socket = listener.Accept();
                NetworkStream ntwStream = new NetworkStream(socket);
                StreamReader strmReader = new StreamReader(ntwStream);
                Invoke(new PrintRecvMssgDelegate(PrintRecvMssg),
                    new object[] { strmReader.ReadToEnd() });
                socket.Close();
            }

            //程序的listener一直不关闭
            //listener.Close();
        }

        //线程内向文本框txtRecvMssg中添加字符串及委托
        private delegate void PrintRecvMssgDelegate(string s);
        private void PrintRecvMssg(string info)
        {
            txtRecvMssg.Text += string.Format("[{0}]:{1}\r\n",
                DateTime.Now.ToLongTimeString(), info);
        }

        private void button1_Click(object sender, EventArgs e)
        {

        }

    }
}

服务器端功能:

1)文件服务器不要求支持并发,同时只支持一个连接,即一个客户端发起的一次命令。一次命令执行完毕后关闭连接并继续等待下一次连接请求。
2)文件服务器启动后一直执行,除非被人为强制关闭。
3)文件服务器端需要设定一个目录用于存放所有的文件,该目录路径不支持可配置,且该目录不要求再包含子目录。称其为”文件存放目录”。
4)对list服务,服务器端从”文件存放目录”下列举出所有的文件名称并发送给客户端。
5)对get服务,服务器根据用户指定的文件名缺省从”文件存放目录”搜索该文件并推送文件到客户端,推送不会删除原服务器上的文件。
6)对put服务,服务器将用户推送的文件存储到”文件存放目录”,如果存在同名的文件则拒绝。
7)若执行命令和传输过程中发生错误,关闭当前连接并进入等待下一个连接。

=================================================
提示:

请在编码前仔细设计一个简单的应用层协议规定客户端和服务器端之间命令传输的请求和应答流程和格式。

图片 27

 

二。参考代码:

图片 28

1.服务器端参考代码

[cpp] view
plain copy

 

  1. #include<stdio.h>  
  2. #include<stdlib.h>  
  3. #include<string.h>  
  4. #include<unistd.h>  
  5. #include<sys/types.h>  
  6. #include<sys/socket.h>  
  7. #include<netinet/in.h>  
  8. #include<arpa/inet.h>  
  9. #include<fcntl.h>  
  10. #include<dirent.h>  
  11. #include<errno.h>  
  12.   
  13. #define N 128   
  14. #define PORT_NUM 8888  
  15.   
  16. typedef struct sockaddr SA;  
  17.   
  18. void ProcessList(int connfd)  
  19. {  
  20.     char buf[N];  
  21.     DIR *mydir;  
  22.     struct dirent *myitem;  
  23.   
  24.     mydir = opendir(“.”);  
  25.     while((myitem = readdir(mydir)) != NULL){  
  26.         if((strcmp(myitem->d_name, “.”) == 0) || (strcmp(myitem->d_name, “..”) == 0))  
  27.             continue;  
  28.         strcpy(buf, myitem->d_name);  
  29.         send( connfd, buf, N, 0);  
  30.     }  
  31.     close(connfd);  
  32.   
  33.     return;  
  34. }  
  35.   
  36. void ProcessGet(int connfd, char buf[])  
  37. {  
  38.     int fd,nbyte;  
  39.   
  40.     if(( fd = open(buf+1, O_RDONLY)) < 0){  
  41.         fprintf(stderr, “fail to open %s: %s\n”,buf+1,strerror(errno));  
  42.         buf[0] = ‘N’;  
  43.         send(connfd, buf, N, 0);  
  44.         return;  
  45.     }  
  46.     buf[0] = ‘Y’;  
  47.     send(connfd, buf, N, 0);  
  48.     while(( nbyte = read( fd, buf, N)) > 0){  
  49.         send(connfd, buf, nbyte, 0);      
  50.     }  
  51.   
  52.     close(connfd);  
  53.   
  54.     return;  
  55. }  
  56.   
  57. void ProcessPut(int connfd, char buf[])  
  58. {  
  59.     int fd, nbyte;  
  60.   
  61.     if(( fd = open(buf+1, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0){  
  62.         printf(“fail to create %s on server\n”,buf+1);  
  63.         return;  
  64.     }  
  65.   
  66.     while(( nbyte = recv( connfd, buf, N, 0)) > 0){  
  67.         write(fd, buf, nbyte);    
  68.     }  
  69.     close(fd);  
  70.   
  71.     return;  
  72. }  
  73.   
  74. int main(int argc, char *argv[])  
  75. {  
  76.     int listenfd, connfd;  
  77.     int optval = 1;  
  78.     char buf[N];  
  79.     struct sockaddr_in server_addr;  
  80.   
  81.     if(( listenfd = socket( AF_INET, SOCK_STREAM, 0)) < 0 ){  
  82.         fprintf(stderr, “fail to socket: %s\n”,strerror(errno));  
  83.         exit(-1);  
  84.     }  
  85.   
  86. #ifdef _DEBUG_  
  87.     printf(“socket is %d\n”, listenfd);  
  88. #endif  
  89.   
  90.     memset(&server_addr, 0, sizeof(server_addr));  
  91.     server_addr.sin_family = AF_INET;  
  92.     server_addr.sin_port = htons(PORT_NUM);  
  93.     server_addr.sin_addr.s_addr = htonl(INADDR_ANY);  
  94.       
  95.     setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));  
  96.   
  97.     if( bind( listenfd, (SA *)(&server_addr), sizeof(server_addr)) < 0){  
  98.         perror(“fail to bind”);  
  99.         exit(-1);  
  100.     }  
  101.     listen( listenfd, 5);  
  102.   
  103.     while(1){  
  104.         if(( connfd = accept(listenfd, NULL, NULL)) < 0){  
  105.             perror(“fail to accept”);  
  106.             break;  
  107.         }     
  108.         recv( connfd, buf, N, 0);  
  109.   
  110.         switch(buf[0]){  
  111.             case ‘L’: ProcessList(connfd);  
  112.                 break;  
  113.             case ‘G’: ProcessGet(connfd, buf);  
  114.                 break;  
  115.             case ‘P’: ProcessPut(connfd, buf);  
  116.                 break;  
  117.             default: printf(“Input “);  
  118.         }  
  119.         close(connfd);  
  120.     }  
  121.   
  122.     return 0;  
  123. }  
 1 #include<sys/types.h>
 2 #include<sys/socket.h>
 3 #include<strings.h>
 4 #include<arpa/inet.h>
 5 #include<unistd.h>
 6 #include<stdlib.h>
 7 #include<stdio.h>
 8 #include<string.h>
 9 #include<errno.h>
10 #include<signal.h>
11 #include<sys/wait.h>
12 
13 #define LISTEN_PORT 84
14 void str_echo(int sockfd)
15 {
16     ssize_t n;
17     char line[512];
18 
19     printf("ready to read\n");
20     while( (n=read(sockfd,line,512))>0 )
21     {
22             line[n]='\0';
23             printf("Client: %s\n",line);
24             bzero(&line,sizeof(line));
25     }
26     printf("end read\n");
27 }
28 
29 int main(int argc, char **argv)
30 {
31     int listenfd, connfd;
32     pid_t childpid;
33     socklen_t chilen;
34 
35     struct sockaddr_in chiaddr,servaddr;
36 
37     listenfd=socket(AF_INET,SOCK_STREAM,0);
38     if(listenfd==-1)
39     {
40         printf("socket established error: %s\n",(char*)strerror(errno)); return -1;
41     }
42 
43     bzero(&servaddr,sizeof(servaddr));
44     servaddr.sin_family=AF_INET;
45     servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
46     servaddr.sin_port=htons(LISTEN_PORT);
47 
48     int bindc=bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
49     if(bindc==-1)
50     {
51         printf("bind error: %s\n",strerror(errno)); return -1;
52     }
53 
54     listen(listenfd,5);
55     for(;;)
56     {
57         chilen=sizeof(chiaddr);
58 
59         connfd=accept(listenfd,(struct sockaddr*)&chiaddr,&chilen);
60         if(connfd==-1)
61         {    printf("accept client error: %s\n",strerror(errno)); return -1; }
62         else        
63             printf("client connected\n");
64 
65         if((childpid=fork())==0)
66         {
67             close(listenfd);
68             printf("client from %s\n",inet_ntoa(chiaddr.sin_addr));
69             str_echo(connfd);
70             exit(0);    
71         }
72         else if (childpid<0)
73             printf("fork error: %s\n",strerror(errno));
74         close(connfd);
75     }
76 }

2.客户端参考代码:

[cpp] view
plain copy

 

  1. #include<stdio.h>  
  2. #include<stdlib.h>  
  3. #include<unistd.h>  
  4. #include<string.h>  
  5. #include<fcntl.h>  
  6. #include<errno.h>  
  7. #include<sys/types.h>  
  8. #include<sys/socket.h>  
  9. #include<netinet/in.h>  
  10. #include<arpa/inet.h>  
  11.   
  12. #define N 128  
  13. #define PORT_NUM 8888  
  14.   
  15. typedef struct sockaddr SA;   
  16.   
  17. void PrintHelp()  
  18. {  
  19.     printf(“help: display help info\n”);  
  20.     printf(“list: get file list of server\n”);  
  21.     printf(“get : get <file>\n”);  
  22.     printf(“put : put <file>\n”);  
  23.     printf(“quit: quit the client\n”);  
  24.   
  25.     return;  
  26. }  
  27.   
  28. void ProcessList(struct sockaddr_in server_addr)  
  29. {  
  30.     int sockfd, nbyte;  
  31.     char buf[N];  
  32.   
  33.     if(( sockfd = socket( AF_INET, SOCK_STREAM, 0)) < 0){  
  34.         printf(“fail to list\n”);  
  35.         return;  
  36.     }  
  37.   
  38.     if( connect(sockfd, (SA *)(&server_addr), sizeof(server_addr)) < 0){  
  39.         printf(“fail to connect server\n”);  
  40.         goto ERROR_1;  
  41.     }  
  42.   
  43.     strcpy(buf, “L”);  
  44.     send(sockfd, buf, N, 0);  
  45.   
  46.     while(( nbyte = recv( sockfd, buf, N, 0)) != 0){  
  47.         printf(“%s\n”,buf);   
  48.     }  
  49.   
  50. ERROR_1:  
  51.     close(sockfd);  
  52.   
  53.     return;  
  54. }  
  55.   
  56. void ProcessGet(struct sockaddr_in server_addr, char command[])  
  57. {  
  58.     int sockfd, nbyte, fd;  
  59.     char buf[N];  
  60.   
  61.     if(( sockfd = socket( AF_INET, SOCK_STREAM, 0)) < 0){  
  62.         printf(“fail to get\n”);  
  63.         return;  
  64.     }  
  65.   
  66.     if( connect( sockfd, (SA *)(&server_addr), sizeof(server_addr)) < 0){  
  67.         printf(“fail to connect server\n”);  
  68.         goto ERROR_2;  
  69.     }  
  70.   
  71.     sprintf(buf, “G%s”, command+4);  
  72.     send(sockfd, buf, N, 0);  
  73.     recv(sockfd, buf, N, 0);  
  74.   
  75.     if(buf[0] == ‘N’){  
  76.         printf(“No such file on server\n”);  
  77.         goto ERROR_2;  
  78.     }  
  79.   
  80.     if(( fd = open(command+4, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0){  
  81.         printf(“fail to create local file %s\n”,command+4);  
  82.         goto ERROR_2;  
  83.     }  
  84.   
  85.     while(( nbyte = recv(sockfd, buf, N, 0)) > 0){  
  86.         write(fd, buf, nbyte);    
  87.     }  
  88.     close(fd);  
  89.   
  90. ERROR_2:  
  91.     close(sockfd);  
  92.   
  93.     return;  
  94. }  
  95.   
  96. void ProcessPut(struct sockaddr_in server_addr, char command[])  
  97. {  
  98.     int sockfd, fd, nbyte;  
  99.     char buf[N];  
  100.   
  101.     if(( sockfd = socket( AF_INET, SOCK_STREAM, 0)) < 0){  
  102.         printf(“fail to get\n”);  
  103.         return;  
  104.     }  
  105.       
  106.     if( connect(sockfd,(SA *)(&server_addr),sizeof(server_addr)) < 0){  
  107.         printf(“fail to connect server\n”);  
  108.         goto ERROR_3;  
  109.     }  
  110.   
  111.     if((fd = open(command+4, O_RDONLY)) < 0){  
  112.         printf(“fail to open %s\n”,command+4);  
  113.         goto ERROR_3;  
  114.     }  
  115.     sprintf(buf, “P%s”, command+4);  
  116.     send(sockfd, buf, N, 0);  
  117.   
  118.     while(( nbyte = read(fd, buf, N)) > 0){  
  119.         send(sockfd, buf, nbyte, 0);      
  120.     }  
  121.     close(fd);  
  122.   
  123. ERROR_3:  
  124.     close(sockfd);  
  125.   
  126.     return;  
  127. }  
  128.   
  129. int main(int argc, char *argv[])  
  130. {  
  131.     int sockfd, fd, nbyte;  
  132.     char command[32];  
  133.     struct sockaddr_in server_addr;  
  134.   
  135.     if(argc < 3){  
  136.         printf(“Usage: %s <server_ip> <port>\n”,argv[0]);  
  137.         exit(-1);  
  138.     }  
  139.   
  140.     if(( sockfd = socket( AF_INET, SOCK_STREAM, 0)) < 0){  
  141.         fprintf(stderr, “fail to socket: %s\n”, strerror(errno));  
  142.         exit(-1);  
  143.     }  
  144.   
  145. #ifdef _DEBUG_  
  146.     printf(“socket is %d\n”,sockfd);  
  147. #endif  
  148.       
  149.     memset(&server_addr, 0, sizeof(server_addr));  
  150.     server_addr.sin_family = AF_INET;  
  151.     server_addr.sin_port = htons(atoi(argv[2]));  
  152.     server_addr.sin_addr.s_addr = inet_addr(argv[1]);  
  153.     //server_addr.sin_addr.s_addr = htonl(INADDR_ANY);  
  154.   
  155.     while(1){  
  156.         printf(“<client>”);  
  157.         fgets(command, 32, stdin);  
  158.         command[strlen(command)-1] = ‘\0’;  
  159.         if(strcmp( command, “help”) == 0){  
  160.             PrintHelp();      
  161.         }  
  162.         else if(strcmp( command, “list”) == 0){  
  163.             ProcessList(server_addr);     
  164.         }  
  165.         else if(strncmp( command, “get”, 3) == 0){  
  166.             ProcessGet(server_addr, command);     
  167.         }  
  168.         else if(strncmp( command, “put”, 3) == 0){  
  169.             ProcessPut(server_addr, command);  
  170.         }  
  171.         else if(strcmp( command, “quit”) == 0){  
  172.             printf(“Bye\n”);  
  173.             break;  
  174.         }  
  175.         else{  
  176.             printf(“Wrong command, ‘help’ for command list.\n”);      
  177.         }  
  178.     }  
  179.   
  180.     return 0;  
  181. }  

图片 29

图片 30

3.验证结果(ubuntu14.04)

图片 31

图片 32

 1 #include<sys/types.h>
 2 #include<stdlib.h>
 3 #include<stdio.h>
 4 #include<unistd.h>
 5 #include<sys/socket.h>
 6 #include<strings.h>
 7 #include<string.h>
 8 #include<arpa/inet.h>
 9 #include<errno.h>
10 
11 #define SERVER_PORT 84
12 void str_cli(char *data,int sockfd)
13 {
14     char recv[512];
15 
16     int wc=write(sockfd,data,strlen(data));
17 
18     exit(0);
19 }
20 int main(int argc, char **argv)
21 {
22     int sockfd;
23     struct sockaddr_in servaddr;
24 
25     if(argc!=3)
26         return -1;
27 
28     sockfd=socket(AF_INET,SOCK_STREAM,0);
29     if(sockfd==-1)
30     {
31         printf("socket established error: %s\n",(char*)strerror(errno)); return -1;
32     }
33 
34     bzero(&servaddr,sizeof(servaddr));
35     servaddr.sin_family=AF_INET;
36     servaddr.sin_port=htons(SERVER_PORT);
37     inet_pton(AF_INET,argv[1],&servaddr.sin_addr);
38 
39     printf("client try to connect\n");
40     int conRes=connect(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
41     if(conRes==-1)
42     {    
43         printf("connect error: %s\n",strerror(errno)); return -1;
44     }
45 
46     str_cli(argv[2],sockfd);
47 
48     exit(0);
49 }

图片 33

 

分析:
  
最简单的服务器仅能完成基本的通信,并没有考虑其他边界或者异常情况,同时,采用子进程处理客户端连接,一旦子进程数量增多,并且子进程需要长时间的运行,那么服务器性能将严重下降。

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图