Linux C++/Java Socket网络编程

一,Linux C++ Socket网络编程

1.什么是TCP/IPUDP

  TCP/IPTransmission Control Protocol/Internet Protocol)即传输控制协议/网间协议,是一个工业标准的协议集,它是为广域网(WANs)设计的。
  UDPUser Data Protocol,用户数据报协议)是与TCP相对应的协议。它是属于TCP/IP协议族中的一种。
  下面的图表明了这些协议的关系。

 Linux C++/Java Socket网络编程

2.Socket在哪里呢?

Linux C++/Java Socket网络编程

3.Socket是什么呢?

  Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

  门面模式,用自己的话说,就是系统对外界提供单一的接口,外部不需要了解内部的实现。

4.有很多的框架,为什么还在从Socket开始?

  现在的跨平台网络编程框架很多,如Java的SSH,C/C++的Boost等。

  现在的分布式框架很多,如Hadoop等。

  我的任务是把一个C/C++程序做成分布式,要求的不配环境,基本属于纯计算的,结果很小。所以选择了Socket。

  重要的是Socket是分布式、云计算、网络编程的基础,对Socket的学习有利于对其他框架的理解。

  下图是Socket编程的基本流程:

Linux C++/Java Socket网络编程

 

 

第一步:建立一个socket

int socket(int af, int type, int protocol) 

A. ‘int af‘代表地址族或者称为socket所代表的域,通常有两个选项: 
    1. AF_UNIX - 只在单机上使用。 
    2. AF_INET - 可以在单机或其他使用DARPA协议(UDP/TCP/IP)的异种机通信。 
B. ‘int type‘代表你所使用的连接类型,通常也有两种情况: 
    1. SOCK_STREAM - 用来建立面向连接的sockets,可以进行可靠无误的的数据传输 
    2. SOCK_DGRAM - 用来建立没有连接的sockets,不能保证数据传输的可靠性。
C. ‘int protocol‘通常设定为0。使系统选择默认的由协议族和连接类型所确定的协议。
D. 返回值是一个文件描述句柄,如果在此期间发生错误则返回-1并且设定了相应的errno。 

int sockfd;
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        perror("socket 创建出错!");
        exit(1);
    }

 

第二步:绑定名字socket: bind() 

int bind(int sockfd, struct sockaddr *name, int namelen) 

A. sockfd是从socket()调用得到的文件描述句柄。

B. name是一个指向sockaddr类型结构的一个指针。

C. namelen给出了文件名的具体长度。

1. 如果地址族被设定为AF_UNIX,这个类型的定义如下所示: 

struct sockaddr {
    u_short sa_family;
    char sa_data[14];
};

在这个结构种,name.sa_family应当被设定为AF_UNIX。

name.sa_data应当包含最长为14个字节的文件名,这个文件名用来分配给socket。

Linux C++/Java Socket网络编程
struct sockaddr name;
    name.sa_family = AF_UNIX;
    strcpy(name.sa_data, "/tmp/whatever");

    int sockfd;
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        perror("socket 创建出错!");
        exit(1);
    }
    if (bind(sockfd, &name, strlen(name.sa_data) + sizeof(name.sa_family))
            == -1) {
        perror("bind");
        exit(1);
    }
Linux C++/Java Socket网络编程

 

2. 在使用AF_INET地址族的时候,类型定义如下:

struct sockaddr_in {
    short int sin_family;
    unsigned short int sin_port;
    struct in_addr sin_addr;
    unsigned char sin_zero[8];
};

 

在这个结构种,name.sin_family应当被设定为AF_INET

Linux C++/Java Socket网络编程
struct sockaddr_in name;

    int sockfd;
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        exit(1);
    }

    name.sin_family = AF_INET;
    name.sin_port = htons(8087);
    name.sin_addr.s_addr = htonl(INADDR_ANY);
    bzero(&(name.sin_zero), 8); /* zero out the rest of the space */

    if (bind(sockfd, (struct sockaddr *) &name, sizeof(struct sockaddr))
            == -1) {
        exit(1);
    }
Linux C++/Java Socket网络编程

 

现在,如果没有问题的话,我们建立的socket就有一个名字了!相反,如果不成功,它会设定相应的错误代码,并使程序退出。

这里需要说明的是,如果你的计算机不想和别人的计算机连接,那么完全没有必要使用bind()。

 

第三步:远程连接: connect() 

int connect(int sockfd, struct sockaddr *serv_addr, int addrlen)

A. sockfd是我们建立的文件描述句柄

B. serv_addr是一个sockaddr结构,包含目的的地址和端口号

C. addrlen 被设定为sockaddr结构的大小。 

if (connect(sockfd, (struct sockaddr *) &name, sizeof(name)) < 0) {  exit(1); }

 

第四步:监听: listen() 

int listen(int sockfd, int backlog)

B. 参数backlog是指一次可以监听多少个连接 

if (listen(sockfd, 20) == -1) { exit(1); }

 

第五步:接受连接: accept()

当有人试图从我们打开的端口登陆进来时,我们应该响应他,这个时候就要用到accept()函数了。

int accept(int sockfd, void *addr, int *addrlen)

conn = accept(sockfd, (struct sockaddr*) &name, sizeof(name));
    if (conn < 0) {
        exit(1);
    }

 

第六步:输入和输入的完成: send() and recv() 

int send(int sockfd, const void *msg, int len, int flags)
int recv(int sockfd, void *buf, int len, unsigned int flags)

send(): 
sockfd - socket file descriptor 
msg - message to send 
len - size of message to send 
flags - read ‘man send‘ for more info, set it to 0 for now 

recv(): 
sockfd - socket file descriptor 
buf - data to receive 
len - size of buf 
flags - same as flags in send() 

注意:如果使用的连接类型是SOCK_DGRAM,那么应该使用sendto()和recvfrom()来实现数据传输。

Linux C++/Java Socket网络编程
char buffer[BUFFER_SIZE];
while (1) {
        memset(buffer, 0, sizeof(buffer));
        int len = recv(conn, buffer, sizeof(buffer), 0);
        if (strcmp(buffer, "exit\n") == 0)
            break;
        fputs(buffer, stdout);
        send(conn, buffer, len, 0);
    }
Linux C++/Java Socket网络编程

 

结束: close() and shutdown() 

当传输结束时,应当关闭连接。

 

一个服务端等待, 客户端上传文件到服务端,通过输入要上传的文件名,目前只做到仅对当前执行文件的目录下的文件,应该在服务端收到文件路径之后进行处理的。

服务端代码:

Linux C++/Java Socket网络编程
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netdb.h>
#include <arpa/inet.h>//for inet_ntoa
#include <unistd.h>//for fork()

#define SERVER_PORT 6666
#define LISTEN_QUEUE  20
#define BUFFER_SIZE 1024
#define FILE_NAME_MAX_SIZE 512

int main() {
    //设置一个socket地址结构server_addr,代表服务器internet地址, 端口
    struct sockaddr_in server_addr;
    bzero(&server_addr, sizeof(server_addr)); //把一段内存区的内容全部设置为0
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htons(INADDR_ANY);
    server_addr.sin_port = htons(SERVER_PORT);

    //创建用于internet的流协议(TCP)socket,用server_socket代表服务器socket
    int server_socket = socket(PF_INET, SOCK_STREAM, 0);
    if (server_socket < 0) {
        printf("Create Socket Failed!");
        exit(1);
    }

    //把socket和socket地址结构联系起来
    if (bind(server_socket, (struct sockaddr*) &server_addr,
            sizeof(server_addr))) {
        printf("Server Bind Port: %d Failed!\n", SERVER_PORT);
        exit(1);
    }

    //server_socket用于监听
    if (listen(server_socket, LISTEN_QUEUE)) {
        printf("Server Listen Failed!");
        exit(1);
    }

    while (1) {
        //定义客户端的socket地址结构client_addr
        char buffer[BUFFER_SIZE];
        struct sockaddr_in client_addr;
        socklen_t length = sizeof(client_addr);

        int client_socket = accept(server_socket,
                (struct sockaddr*) &client_addr, &length);
        if (client_socket < 0) {
            printf("Server Accept Failed!\n");
            break;
        }
        bzero(buffer, BUFFER_SIZE);
        // 获取客户端要传输的文件名
        length = recv(client_socket, buffer, BUFFER_SIZE, 0);
        if (length < 0) {
            printf("Server Recieve Data Failed!\n");
            break;
        }
        char file_name[FILE_NAME_MAX_SIZE + 1];
        bzero(file_name, FILE_NAME_MAX_SIZE + 1);
        strncpy(file_name, buffer,
                strlen(buffer) > FILE_NAME_MAX_SIZE ?
                        FILE_NAME_MAX_SIZE : strlen(buffer));
        // 新建文件
        FILE * fp = fopen(file_name, "w");
        if (NULL == fp) {
            printf("File: %s CAN NOT WRITE!\n", file_name);
        } else {
            bzero(buffer, BUFFER_SIZE);
            int file_block_length = 0;
            while ((file_block_length = recv(client_socket, buffer, BUFFER_SIZE,
                    0)) > 0) {
                if (file_block_length < 0) {
                    printf("Recieve Data From Client Failed!\n");
                    break;
                }
                int write_length = fwrite(buffer, sizeof(char),
                        file_block_length, fp);
                if (write_length < file_block_length) {
                    printf("File: %s Write Failed\n", file_name);
                    break;
                }
                bzero(buffer, BUFFER_SIZE);
            }
            fclose(fp);
            printf("File: %s Transfer Finished\n\n", file_name);
        }
        close(client_socket);
    }
    close(server_socket);
    return 0;
}
Linux C++/Java Socket网络编程

客户端

Linux C++/Java Socket网络编程
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netdb.h>
#include <arpa/inet.h>//for inet_ntoa
#include <unistd.h>//for fork()

#define SERVER_PORT 6666
#define BUFFER_SIZE 1024
#define FILE_NAME_MAX_SIZE 512

int main() {
    //设置一个socket地址结构client_addr,代表客户机internet地址, 端口
    struct sockaddr_in client_addr;
    bzero(&client_addr, sizeof(client_addr)); //把一段内存区的内容全部设置为0
    client_addr.sin_family = AF_INET; //internet协议族
    client_addr.sin_addr.s_addr = htons(INADDR_ANY); //INADDR_ANY表示自动获取本机地址
    client_addr.sin_port = htons(0); //0表示让系统自动分配一个空闲端口
    //创建用于internet的流协议(TCP)socket,用client_socket代表客户机socket
    int client_socket = socket(AF_INET, SOCK_STREAM, 0);
    if (client_socket < 0) {
        printf("Create Socket Failed!\n");
        exit(1);
    }
    //把客户机的socket和客户机的socket地址结构联系起来
    if (bind(client_socket, (struct sockaddr*) &client_addr,
            sizeof(client_addr))) {
        printf("Client Bind Port Failed!\n");
        exit(1);
    }

    //设置一个socket地址结构server_addr,代表服务器的internet地址, 端口
    struct sockaddr_in server_addr;
    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(SERVER_PORT);
    socklen_t server_addr_length = sizeof(server_addr);
    // 向服务器发起连接,连接成功后client_socket代表了客户机和服务器的一个socket连接
    if (connect(client_socket, (struct sockaddr*) &server_addr,
            server_addr_length) < 0) {
        exit(1);
    }

    // 连接上服务器, 选择要上传的文件
    char file_name[FILE_NAME_MAX_SIZE + 1];
    bzero(file_name, FILE_NAME_MAX_SIZE + 1);
    printf("Please Input File Name Upload To Server: ");
    scanf("%s", file_name);

    char buffer[BUFFER_SIZE];
    bzero(buffer, BUFFER_SIZE);
    strncpy(buffer, file_name,
            strlen(file_name) > BUFFER_SIZE ? BUFFER_SIZE : strlen(file_name));

    FILE * fp = fopen(file_name, "r");
    if (NULL == fp) {
        printf("File: %s NOT FOUND! \n", file_name);
        exit(1);
    }

    // 发送文件名
    int nameLength = send(client_socket, buffer, BUFFER_SIZE, 0);
    if (nameLength < 0) {
        printf("File name Error! \n");
        exit(0);
    }

    bzero(buffer, BUFFER_SIZE);
    int file_block_length = 0;

    while ((file_block_length = fread(buffer, sizeof(char), BUFFER_SIZE, fp))
            > 0) {
        if (send(client_socket, buffer, file_block_length, 0) < 0) {
            printf("Send File:\t%s Failed\n", file_name);
            break;
        }
        bzero(buffer, BUFFER_SIZE);
    }
    printf("File:\t%s Transfer Finished\n", file_name);
    fclose(fp);
    close(client_socket);
    return 0;
}
Linux C++/Java Socket网络编程

 

附上Windows Socket编程 http://www.cppblog.com/bujiwu/archive/2009/01/11/71707.aspx  

 

二,JAVA ServerSocket

声明一个ServerSocket打开一个未占用的套接口,并开始ServerSocket.aceept 监听端口。

其他程序可以Socket连接IP与端口,new BufferReader(new InputStreamReader(socket.getInputStream()))接收消息。

Linux C++/Java Socket网络编程
import java.io.*;
import java.net.*;
import java.sql.*;
import java.util.Vector;

class ServerThread extends Thread {

    private Socket socket;// 定义套接口
    private BufferedReader in;// 定义输入流
    private PrintWriter out;// 定义输出流
    Connection conn;
    int no;

    public ServerThread(Socket s) throws IOException {// 线程构造函数
        socket = s;// 取得传递参数
        in = new BufferedReader(new InputStreamReader(socket.getInputStream()));// 创建输入流
        out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(
                socket.getOutputStream())), true);// 创建输出流
        start();// 启动线程run
    }

    public void run() {// 重写run方法
        System.out.println("监听Socket中...");
        try {
            Class.forName("com.mysql.jdbc.Driver");// 连接数据库
            conn = DriverManager.getConnection(
                    "jdbc:mysql://localhost:3306/javaicq", "root", "");
            while (true) {
                String OP = in.readLine();
                // 停止
                if (OP.equals("end"))
                    break;
                // 登录
                else if (OP.equals("login")) {
                    try {
                        PreparedStatement prepare = conn
                                .prepareStatement("select nickname,password from icq where icqno=?");// 设定数据库查寻条件

                        int g = Integer.parseInt(in.readLine());
                        String passwd = in.readLine().trim();

                        prepare.setInt(1, g);// 设定参数
                        ResultSet rs = prepare.executeQuery();// 执行数据库查寻
                        if (!rs.next())
                            out.println("用户不存在");
                        else {// 以下比较输入的号码于密码是否相同
                            String pass = rs.getString("password").trim();
                            if (!passwd.equals(pass)) {
                                out.println("密码错误");
                            } else {
                                // 以及注册用户的ip 地址
                                PreparedStatement online = conn
                                        .prepareStatement("update icq set ip=? where icqno=?");
                                online.setString(1, socket.getInetAddress()
                                        .getHostAddress());
                                online.setInt(2, g);
                                online.executeUpdate();
                                // set status online
                                PreparedStatement status = conn
                                        .prepareStatement("update icq set status=1 where icqno=?");
                                status.setInt(1, g);
                                status.executeUpdate();
                                out.println("YES");
                            }
                        }
                        rs.close();
                    } catch (Exception e) {
                        e.printStackTrace();
                        out.println("Error");
                    }
                }
                // 新建
                else if (OP.equals("new")) {
                    try {
                        // 准备接受用户的呢称,密码,email,个人资料,籍贯,头像等信息
                        String nickname = in.readLine().trim();
                        String password = in.readLine().trim();
                        String email = in.readLine().trim();
                        String info = in.readLine().trim();
                        String place = in.readLine().trim();
                        int picindex = Integer.parseInt(in.readLine());

                        PreparedStatement userInfo = conn
                                .prepareStatement("insert into icq(nickname,password,email,info,place,pic,icqno) values(?,?,?,?,?,?,?)");
                        userInfo.setString(1, nickname);
                        userInfo.setString(2, password);
                        userInfo.setString(3, email);
                        userInfo.setString(4, info);
                        userInfo.setString(5, place);
                        userInfo.setInt(6, picindex);
                        userInfo.setInt(7, Integer.parseInt(Double.toString(
                                Math.random()).substring(3, 8)));
                        userInfo.executeUpdate();// 执行数据库添加
                        // 查询其注册的号码
                        PreparedStatement qNo = conn
                                .prepareStatement("select icqno from icq where nickname=?");
                        qNo.setString(1, nickname);
                        ResultSet rs = qNo.executeQuery();
                        while (rs.next()) { // 找到最近注册的号码
                            no = rs.getInt(1);
                        }
                        out.println("YES");
                        out.println(no);
                        rs.close();
                    } catch (Exception e) {
                        e.printStackTrace();
                        out.println("Error");
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            try {
                conn.close();
                socket.close();
            } catch (IOException e) {
            } catch (SQLException e) {
            }
        }
    }
}

public class Server {// 主服务器类
    public static void main(String args[]) throws IOException {
        ServerSocket so = new ServerSocket(8081);// 在8081端口创建套接口
        so.setSoTimeout(100000);
        System.out.println("服务器已启动 " + so);
        Socket testServer = new Socket(InetAddress.getLocalHost(), 8081);
        PrintWriter out = new PrintWriter(new BufferedWriter(
                new OutputStreamWriter(testServer.getOutputStream())), true);
        try {
            while (true) {
                Socket socket = so.accept();// 无限监听客户的请求
                System.out.println("建立Socket连接:" + socket);
                try {
//                    out.println("login");
//                    out.println(123);
//                    out.println(123);
                    new ServerThread(socket);// 创建新线程
                } catch (IOException e) {
                    socket.close();
                }
            }
        } catch (SocketTimeoutException e) {
            System.out.println("超时断开连接");
        } finally {
            so.close();
        }
    }
}
Linux C++/Java Socket网络编程

 

 

Linux C++/Java Socket网络编程,布布扣,bubuko.com

Linux C++/Java Socket网络编程

上一篇:iphone13是双卡双待吗?


下一篇:2022/1/13