网络编程概念全面接触(四).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 调试通过。