1.什么是TCP/IPUDP

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

 Linux C++/Java/Web/OC Socket网络编程

2.Socket在哪里呢?

Linux C++/Java/Web/OC Socket网络编程

3.Socket是什么呢?

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

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

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

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

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

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

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

Linux C++/Java/Web/OC 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给出了文件名的具体长度。

在使用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

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);
    }

 

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

 

第三步:远程连接: 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()来实现数据传输。

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);
    }

 

结束: close() and shutdown() 

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

 

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

服务端代码:

#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;
}
View Code

相关文章:

  • 2022-12-23
  • 2022-02-24
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-05-16
  • 2021-06-13
  • 2022-02-09
猜你喜欢
  • 2021-12-29
  • 2021-06-25
  • 2021-08-06
  • 2021-08-10
  • 2021-10-02
  • 2021-09-21
  • 2021-12-15
相关资源
相似解决方案