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

网络编程概念全面接触(四).C++与Java的实现

2008-02-16 18:02 1031 查看
网络编程概念全面接触(四).C++与Java的实现

草木瓜 20080201

前一阵子遇到网络通信方面的东西,感觉总结还是十分必要的。这里给出一些新的补充。
本示例采用C++方式。

一、Socket.h 头文件

#ifndef SOCKET_H
#define SOCKET_H

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <time.h>

class Socket
{
  public:

    // 服务器侦听(服务端使用)
    int Listen( int iPortID );
   
    // 服务器获取连接 bForkFlag 控制是否采用fork方式
    int Accept(bool bForkFlag = false);

    // 连接socket(客户端使用)
    int Connect(
        // 远程服务器IP
        char *sRemoteHostIP,
        // 远程服务器端口
        int iRemotePortID);

    Socket();
    virtual ~Socket();

    // socket 普通读
    int Read(
        // 存放读取socket内容的缓冲
        unsigned char *sBuffer,
        // 指定读取长度
        int iLen);

    // socket 普通写
    int Write(
        // 存放准备写入socket的内容地址
        unsigned char *sBuffer,
        // 写入socket长度
        int iLen);

    // Socket recv 方法
    int Recv(
        // 读取内容存放位置
        unsigned char *sBuffer,
        // 指定读取长度
        int iLen);
       
    // Socket send 方法
    int Send(
        // 发送内容
        unsigned char *sBuffer,
        // 指定发送长度
        int iLen);
       
   struct sockaddr_in  m_ServAddress;
   struct sockaddr_in  m_ClientAddress;
   
  private:

 // 监听ID
    int m_iListenID;
   
    // 服务端接收到的客户段连接
    int m_iAcceptID;
   
    // 客户段连接
    int m_iSocketID;

};

 

#endif

二、Socket.cpp 文件内容 (略)

// Socket::Connect 客户段连接未实现,可自行填写

#include "Socket.h"
using namespace std;

int Socket::Listen(int iPortID)
{

 

}

int Socket::Accept(bool bForkFlag)
{
 

}

int Socket::Connect(char *sRemoteHostIP, int iRemotePortID)
{

 

}

Socket::Socket()
{
 this->m_iListenID = -1;
 this->m_iAcceptID = -1;
 this->m_iSocketID = -1;
}

Socket::~Socket()
{
 close(this->m_iListenID);
 close(this->m_iAcceptID);
 close(this->m_iSocketID);
}

int Socket::Read(unsigned char *sBuffer, int iLen)
{

}

 

int Socket::Write(unsigned char *sBuffer, int iLen)
{

}

int Socket::Recv(unsigned char *sBuffer, int iLen)
{

}

 

int Socket::Send(unsigned char *sBuffer, int iLen)
{
 
}

三、服务端应用示例代码

{
 
 unsigned char sRecvString[COMMAND_MAX_LEN];
 unsigned char sSendString[COMMAND_MAX_LEN];
 
 char sCommand[COMMAND_MAX_LEN];
 char sIP[20];
 
 int iRecvLen;

 Socket srv;
 srv.Listen(16666);
 while(1) {
  
  sCommand[0]=0;
  sRecvString[0]=0;
  
  srv.Accept(0);
  
  strcpy ( sIP, inet_ntoa(srv.m_ClientAddress.sin_addr));
  printf ("[GET] 客户端连接 %s/n",sIP);

  iRecvLen = srv.Recv(sRecvString, 200);
  sRecvString[iRecvLen]=0;
  printf ("[GET] 客户端发送字符: %s/n", sRecvString);
   
  if ( strlen((char *)sRecvString) >=160 )  {
   printf ("[ERROR] 客户端发送错误命令: %s", sRecvString);
   strcpy((char *)sSendString, "ERROR");
   srv.Send(sSendString, 6);   
   continue;
  }

  if ( strncmp((char *)sRecvString, "shutdownserv", 12) == 0) {
   strcpy((char *)sSendString, "EXIT");
   srv.Send(sSendString, 5);   
   return 0;
  } else  
  if ( strncmp((char *)sRecvString, "startserv ", 9) != 0 )  {
   printf ("[ERROR] 客户端发送错误命令: %s", sRecvString);
   strcpy((char *)sSendString, "ERROR");
   srv.Send(sSendString, 6);   
   continue;
  }

  printf ("[EXE] 执行命令: %s", sCommand);
  strcpy((char *)sSendString, "OK");
  srv.Send(sSendString, 3);
  system(sCommand);
  
 }
 

}

四、客户端

 客户端连接可以用任意代码书写,所必须注意的要遵循通用规范,不同语言的具体实现是有
差别的。
 如阻塞方式的recv,send是不需要用循环体,有时用了循环体在C/C++不会出错,但Java客户
端就会有问题。
 即以下服务端代码是不允许的:
 
 int num=0;
 int ret;

 if (this->m_iAcceptID <= 0) {
  printf ("socket not connected/n");
  return -1;
 }
 while( num < iLen )
 {
  ret = ::recv (this->m_iAcceptID, sBuffer, iLen, 0); 
  if( ret<0 )
  { printf("write error/n");
   return -1;
  }
  num+=ret;
 }
 return num;
 
 以下是java客户端代码,java服务端代码可自行填写:
 
 import java.net.*;
 import java.io.*;
 
 public class TestClient {
  public static void main(String args[]) {
   try {
    Socket socket = new Socket("192.168.0.99", 16666);
    OutputStream out = socket.getOutputStream();
    InputStream in = socket.getInputStream();
    

    byte[] btsend = new byte[80];
    String test=new String("abcdefg");
    btsend=test.getBytes();
    
    out.write(btsend);
    out.flush();
    

    byte[] btrecv=new byte[10];
    int i=0;
    i=in.read(btrecv);
    btrecv[i]=0;
    String strRecv = new String(btrecv,"US-ASCII");
    System.out.println(i);
    System.out.println(strRecv);
    
    out.close();
    in.close();
    socket.close();
 
   } catch (ConnectException connExc) {
    connExc.printStackTrace();
    System.err.println("服务器连接失败!");
   } catch (IOException e) {
    e.printStackTrace();
   }
  }
 }
 

五、补充说明

 本文还是照旧罗列代码,因为大部分方法在前面的系列文章已有详细的说明。这里补充说下
setsockopt这个函数。setsockopt(this->m_iListenID,SOL_SOCKET,SO_REUSEADDR,(char
*)&optval,sizeof(optval))!=0 设置可重用本地址。具体可参照man手册,有十分详细的说明。
 本示例代码在 HP UNIX  gcc version 4.1.1 Linux Eclipse 3.1 调试通过。 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息