网络编程学习笔记:linux下的socket编程

socket是进程通信的一种方式,通过调用一些API可以实现进程间通信,建立连接以及收发信息的过程如下图所示:

这些函数的用法如下:

1、int socket(int protocolFamily, int type, int protocol); 返回描述符sockfd

  • l  protocolFamily:协议族,AF_INET(IPV4)、AF_INET6(IPV6)、AF_LOCAL(或称AF_UNIX,unix域socket)、AF_ROUTE等。协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了用IPV4地址(32位)与端口号(16位),AF_UNIX决定了要用一个绝对路径名作为地址
  • l  type:指定socket类型。常用的类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等
  • l  protocol:协议名

调用socket创建之后,返回的描述符存在于协议族空间中,但没有一个具体的地址,必须要通过bind才行

2、int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

  • l  sockfd:socket描述字,socket的返回值
  • l  addr:一个const struct *addr指针,指向要绑定的sockfd的协议地址。这个地址结构根据地址创建socket时的地址协议族的不同而不同

如, IPV4:

struct sockaddr_in {
sa_family_t    sin_family; /* address family: AF_INET */
in_port_t      sin_port;   /* port in network byte order */
struct in_addr sin_addr;   /* internet address */
};

struct in_addr{
  uint32_t s_addr; // address in network byte order
};
  • l  addrlen:对应的地址的长度

3、int listen(int sockfd, int backlog);服务器监听函数

  • l  sockfd:socket描述字,socket的返回值
  • l  backlog: socket 可以排队的最大连接个数

  listen函数将socket变为被动类型的, 等待客户连接请求

4、int connect(int sockfd, const struct *addr, socklen_t addrlen);

  • l  sockfd:socket描述字,socket的返回值
  • l  addr:服务器的socket地址
  • l  addrlen:socket地址的长度

5、int accept(int sockfd, struct sockaddr* addr, socklen_t *addrlen);

  • l  sockfd:socket描述字,socket的返回值
  • l  addr:结果参数,接收一个返回值指向客户端的地址,如果对客户的地址不在乎,可以设置为NULL
  • l  addrlen:结果参数,接收上述addr结构大小,指明addr结构所占字节数,也可NULL

  accept()成功返回一个SOCKET描述符,表示接收到的套接字的描述符。否则返回值INVALID_SOCKET。

6、ssize_t send(int sockfd, const void *buf, ssize_t len, int flags);

  • l  sockfd: socket描述字,socket的返回值
  • l  buf:要发送的数据buffer
  • l  len:要传送的数据大小
  • l  flag:一般取值为0,影响TCP首部的可选部分

  send将自己的数据copy到内核的send buffer,返回值为[-1,size]:

    • 返回-1说明发送数据失败,系统内部出问题了
    • 返回[0,size]:由于send是从内核的send buffer中写数据,那么send buffer中剩下的长度为m,返回min(m, size),返回0就是send buffer没有空间了

7、ssize_t recv(int sockfd, void *buf, ssize_t len, int flags);

各参数意义基本与send相同,recv操作将内核中的数据拷贝到应用程序内部

返回值是[-1,size]:

    • 返回-1表明接收失败,socket失效、recv操作由于系统内部原因中断等原因
    • 返回0表明没有数据,在TCP中接收发送有一个timeout,当timeout的时候还没有数据返回0
    • 返回[1,size]:recv的操作时从内核拷贝数据,数据有多少拷贝多少,内核现在收到长度为m的数据,返回min(m, size);

8、int close(int fd);

一个echo小例子,客户端向服务器发送什么服务器就给客户端返回什么信息:

1、服务器代码:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>   // 基本系统数据类型,是系统的基本系统数据类型的头文件
#include<sys/socket.h>
#include<netinet/in.h>  // 互联网地址族
#include<arpa/inet.h>   //IP地址转换函数inet_pton
#include<unistd.h>      //close

#include<iostream>

int main(int argc, char** argv){
    int socketfd, bindfd, connectfd;
    char buffer[4096];
    struct sockaddr_in serverAddress;
    int sendSize, recvSize;

    //printf("================== create socket ======================\n");
    // create socket
    socketfd = socket(AF_INET, SOCK_STREAM, 0);
    if(socketfd == -1){
        printf("Create socket error:%s(errno:%d)\n", strerror(errno),errno);
        exit(0);
    }

    //printf("================== set address ========================\n");
    //set server address
    memset(&serverAddress, 0, sizeof(serverAddress));
    serverAddress.sin_family = AF_INET;
    serverAddress.sin_port = htons(800);
    //serverAddress.sin_addr.s_addr = htonl(127.0.0.1);
    inet_pton(AF_INET, "127.0.0.1", &serverAddress.sin_addr);

    std::cout << serverAddress.sin_addr.s_addr << ‘\n‘;

    //printf("================== bind address ========================\n");
    // bind address to the socket
    bindfd = bind(socketfd, (struct sockaddr*)&serverAddress, sizeof(serverAddress));
    if(bindfd == -1){
        printf("bind socket error:%s(errno:%d)\n", strerror(errno),errno);
        exit(0);
    }

    //printf("================== listen ========================\n");
    if(listen(socketfd, 10) == -1){
        printf("listen socket error:%s(errno:%d)\n",strerror(errno),errno);
        exit(0);
    }

    printf("================== waiting connect ========================\n");
    while(1){
        sleep(2);
        // recvive a connect and accept
        connectfd = accept(socketfd, (struct sockaddr*)NULL, NULL);
        if(connectfd == -1){
            printf("connet socket error:%s(errno:%d)\n",strerror(errno),errno);
            continue;
        }

        // receive data
        recvSize = recv(connectfd, buffer, 4096, 0);
        if(recvSize == -1){
            printf("recvive data error:%s(errno:%d)\n",strerror(errno),errno);
            continue;
        }

        printf("%s\n", buffer);

        // send data
        sendSize = send(connectfd, buffer, 4096, 0);
        if(sendSize == -1){
            printf("send data error:%s(errno:%d)\n",strerror(errno),errno);
            continue;
        }
        close(connectfd);

    }
    close(socketfd);

}

2、客户端代码:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>   // 基本系统数据类型,是系统的基本系统数据类型的头文件
#include<sys/socket.h>
#include<netinet/in.h>  // 互联网地址族
#include<arpa/inet.h>   //IP地址转换函数inet_pton
#include<unistd.h>      //close

#include<iostream>

int main(){
    int socketfd, connectfd;
    int sendSize, recvSize;

    struct sockaddr_in serverAddress;

    char sendBuf[4096];
    char recvBuf[4096];

    printf("================== create socket ========================\n");
    socketfd = socket(AF_INET, SOCK_STREAM, 0);
    if(socketfd == -1){
        printf("create socket error:%s(error%d)\n", strerror(errno),errno);
        exit(0);
    }

    memset(&serverAddress, 0, sizeof(serverAddress));
    serverAddress.sin_family = AF_INET;
    serverAddress.sin_port = htons(800);
    //serverAddress.sin_addr.s_addr = htonl("127.0.0.1");
    inet_pton(AF_INET, "127.0.0.1", &serverAddress.sin_addr);

    std::cout << serverAddress.sin_addr.s_addr << ‘\n‘;

    connectfd = connect(socketfd, (struct sockaddr*)&serverAddress, sizeof(serverAddress));
    if(connectfd == -1){
        printf("connect server error:%s(error(%d))\n", strerror(errno), errno);
        exit(0);
    }

    printf("send message to server:\n");
    //std::cin >> sendBuf;
    fgets(sendBuf, 4096, stdin);
    //printf("%s\n", sendBuf);
    sendSize = send(socketfd, sendBuf, strlen(sendBuf), 0);
    if(sendSize == -1){
        printf("send data error:%s(error%d)\n", strerror(errno), errno);
        exit(0);
    }

    printf("wait echo from server:\n");

    sleep(2);
    recvSize = recv(socketfd, recvBuf, sizeof(recvBuf), 0);
    if(recvSize < 0){
        printf("recvive echo error:%s(error%d)\n", strerror(errno), errno);
        exit(0);
    }
    printf("%s", recvBuf);

    close(socketfd);

}

在这过程中遇到的几个问题:

1、(客户端)errno 111:connection refused

  这个问题说明客户端没有找到应该连接的端口,需要做到:

  • 确保服务端在相应的端口监听;
  • 关闭防火墙(ubuntu下面的命令:sudo ufw disable);
  • 而且server端要 sudo 运行;

  由于我客户端和服务器端口号不匹配,就出现了这个问题

2、(服务器)errno 14:bad address

  accept()函数的第二个参数指的是一个接收返回结果的缓存区,表示的是本次连接的客户端的地址,如果无所谓客户端地址可以写为NULL,而一旦给这个参数赋了非NULL得值,但是这个缓存区又不可写的话,就会出现bad address报错

3、(服务器)errno 107: transport endpoint is not connected.

  这个问题,是由于我服务器端的recv和send函数的第一个参数写的本地socket描述符造成的,事实上,这应该是已连接的socket描述符

时间: 2024-10-10 16:25:40

网络编程学习笔记:linux下的socket编程的相关文章

Linux 程序设计学习笔记----Linux下文件类型和属性管理

转载请注明出处:http://blog.csdn.net/suool/article/details/38318225 部分内容整理自网络,在此感谢各位大神. Linux文件类型和权限 数据表示 文件属性存储结构体Inode的成员变量i_mode存储着该文件的文件类型和权限信息.该变量为short int类型. 这个16位变量的各个位功能划分为: 第0-8位为权限位,为别对应拥有者(user),同组其他用户(group)和其他用户(other)的读R写W和执行X权限. 第9-11位是权限修饰位,

Socket编程学习之道:揭开Socket编程的面纱

对TCP/IP.UDP.Socket编程这些词你不会很陌生吧?随着网络技术的发展,这些词充斥着我们的耳朵.那么我想问: 1.         什么是TCP/IP.UDP? 2.         Socket在哪里呢? 3.         Socket是什么呢? 4.         你会使用它们吗? 什么是TCP/IP.UDP? TCP/IP(Transmission Control Protocol/Internet Protocol)即传输控制协议/网间协议,是一个工业标准的协议集,它是为

Linux下TCP网络编程与基于Windows下C#socket编程间通信

一.linux下TCP网络编程基础,需要了解相关函数 Socket():用于套接字初始化. Bind():将 socket 与本机上的一个端口绑定,就可以在该端口监听服务请求. Listen():使socket处于被动的监听模式,并为该  socket  建立一个输入数据队列,将到达的服务器, 请求保存在此队列中,直到程序处理他们. Accept():让服务器接收客户的连接请求. Connect():客户端使用connect函数来配置 socket并与远端服务器建立一个 TCP 连接. Clos

Linux下TCP网络编程与基于Windows下C#socket编程之间通信

一.linux下TCP网络编程基础,需要了解相关函数 Socket():用于套接字初始化. Bind():将 socket 与本机上的一个端口绑定,就可以在该端口监听服务请求. Listen():使socket处于被动的监听模式,并为该  socket  建立一个输入 数据队列,将到达的服务器, 请求保存在此队列中,直到程序处理他们. Accept():让服务器接收客户的连接请求. Connect():客户端使用connect函数来配置 socket并与远端服务器建立一个 TCP 连接. Clo

Linux下的socket编程

网络通信编程即编写通过计算机与其他程序之间进行通讯的程序,相互通信的程序中一方可以称为客户端程序,另一方称为服务程序,应用系统提供Socket编程接口可以编写自己的网络程序. 一  通过TCP/IP协议进行传输 TCP:为应用程序提供可靠的通信连接.适合一次传输大批的数据情况.并使用于要求得到的响应程序. UDP:提供无线连接通信,且对传送包进行可靠性保证.适合一次传输少量的数据,可靠性则由应用层来负责. 二  Socket套接字 网络通信编程通过socket接口来进行的.socket接口是TC

Linux下的socket编程实践(一) 网络基本知识以及 TCP/IP简述

ISO/OSI七层参考模型 1.物理层:主要定义物理设备标准,如网线的接口类型.光纤的接口类型.各种传输介质的传输速率等.它的主要作用是传输比特流(就是由1.0转化为电流强弱来进行传输,到达目的地后再转化为1.0,也就是我们常说的数模转换与模数转换).这一层的数据叫做比特.(标志:RJ-45) 2.数据链路层:定义了如何让格式化数据以进行传输,以及如何让控制对物理介质的访问.这一层通常还提供错误检测和纠正,以确保数据的可靠传输,交换机属于本层. 3.网络层:在位于不同地理位置的网络中的两个主机系

LINUX 下 ipv6 socket 编程

大家都知道,随着互联网上主机数量的增多,现有的32位IP地址已经不够用了,所以推出了下一代IP地址IPv6,写网络程序的要稍微改变一下现有的网络程序适应IPv6网络是相当容易的事.对于我们来说就是IP地址变化了,所以程序里在用到IP地址的地方做相应的改变就可以了. 记住:主要是改变程序里设置IP地址和端口等部分的代码. 服务器端源代码如下:/***********************/#include <stdio.h>#include <stdlib.h>#include &

Linux下的socket编程实践(十) 基本UDP编程细节

在我的这两篇博客中,简单介绍并实现了基于UDP(TCP)的windows(UNIX下流程基本一致)下的服务端和客户端的程序,本文继续探讨关于UDP编程的一些细节. http://blog.csdn.net/nk_test/article/details/47733307 http://blog.csdn.net/nk_test/article/details/47756381 下图是一个简单的UDP客户/服务器模型: 我在这里也实现了一个简单的UDP回射服务器/客户端: /**实践: 实现一个基

Linux下的socket编程实践(四)TCP的粘包问题和常用解决方案

TCP粘包问题的产生 由于TCP协议是基于字节流并且无边界的传输协议, 因此很有可能产生粘包问题.此外,发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一个TCP段.若连续几次需要send的数据都很少,通常TCP会根据优化算法把这些数据合成一个TCP段后一次发送出去,但是接收方并不知道要一次接收多少字节的数据,这样接收方就收到了粘包数据.具体可以见下图: 假设主机A send了两条消息M1和M2 各10k 给主机B,由于主机B一次提取的字节数