Linux网络编程:TCP Socket通信的基本用法

从 TCP/IP 协议说起

通信必然要求参与通信的双方约定好规则,这就是协议。当今世界的互联网基本就是跑在 TCP/IP 协议族上。两台主机通信最底层就是网卡对外发送的以太网帧。下面从报文结构的角度从底向上说说协议:
协议栈

  1. 以太帧头, 以太头有本对端MAC地址,主要 用来二层交换, MAC地址是固化在网卡中的,一个主机可以插多个网卡。
  2. IP头, 有本对端IP地址,主要用来路由,一个网卡可以配多个IP。并且指定传输层协议类型,TCP协议号是6,UPD协议号是17。
    这里是:IP协议号列表
    IP头:
    IP头
  3. TCP头UDP头, IP层会解出传输层的协议类型,然后加上端口号,就可以确定到应用程序进程。不同传输层协议当然可以有相同端口号。
    TCP头:
    TCP头
  4. 应用层和进程功能紧密相关,如FTP, DNS等,知名服务往往有知名端口与之相对应,如的HTTP的80端口,当然也可以自己制定端口。

一些不错TCP/IP的资料

TCP协议要提供可靠连接,涉及了复杂的状态机,流控、拥塞控制、连接保活等方面,并且站在全网的角度,可以说是最复杂的协议。正式由于TCP在传输层提供了可靠的有连接通信,使其成为了使用最为广泛的协议。相比较之下,UDP就比较简单,至于连接的可靠性必然需要应用层来考虑了。
下面是一些不错的资料:

Linux Socket TCP编程举例

server.c

/* server.c */

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define SERV_PORT 1500
#define LISTEN_BACKLOG 10
#define BUFF_SIZE 32

int main()
{
    struct sockaddr_in servsockaddr;
    int listensockfd;
    int connectsockfd;
    char buff[BUFF_SIZE] = {0};
    char *msg = "Welcome, client!\n";
    size_t msglen = strlen(msg) + 1;

    /* server向操作系统申请监听socket, 贯穿整个server生命周期,指定协议族,服务类型,传输协议号*/
    listensockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (-1 == listensockfd) {
        perror("Create socket failed");
        exit(EXIT_FAILURE);
    }

    /* server 的本端IP与端口 */
    servsockaddr.sin_family = AF_INET;
    servsockaddr.sin_port = htons(SERV_PORT);
    servsockaddr.sin_addr.s_addr = INADDR_ANY;  //本机任意一个IP

    /* server 绑定 监听socket 的本端地址: IP + 端口 */
    if (-1 == bind(listensockfd,(const struct sockaddr *)&servsockaddr, 
				   sizeof(servsockaddr))) {
        perror("Server bind socket failed!");
        close(listensockfd);
        exit(EXIT_FAILURE);
    }

    /* server 的 监听socket 由主动置为被动的监听状态 */
    if (-1 == listen(listensockfd, LISTEN_BACKLOG)) {
        perror("Listen failed!");
        close(listensockfd);
        exit(EXIT_FAILURE);
    }

    /* 迭代处理每个client的连接, 未考虑并发能力,实际一般可以fork等进行并发处理*/
    while (1) {
        /* 接受连接, 返回连接socket, 并获取client的地址及地址的大小 */
        connectsockfd = accept(listensockfd, NULL, NULL);
        if (0 > connectsockfd) {
            perror("Accetp failed!");
            close(listensockfd);
            exit(EXIT_FAILURE);
        }

        /* 收消息, 发消息, 网络IO操作,这里没判断返回值,还有其他网络IO API */
        recv(connectsockfd, buff, sizeof(buff), 0);
        printf("recv msg: %s", buff);
        send(connectsockfd, msg, msglen, 0);
        printf("send msg: %s", msg);

        shutdown(connectsockfd, SHUT_RDWR);
        close(connectsockfd);
    }

    close(listensockfd);
    return 0;
}

client.c

/* client.c */

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define SERV_IP "127.0.0.1"
#define SERV_PORT 1500
#define BUFF_SIZE 32

int main()
{
    struct sockaddr_in servsockaddr;
    int clientsockfd;
    char buff[BUFF_SIZE] = {0};
    char *msg = "Hi, Server!\n";
    size_t msglen = strlen(msg) + 1;

    /* 向操作系统申请 socket,指定协议族,服务类型,传输层协议号 */
    clientsockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (-1 == clientsockfd) {
        perror("Create socket failed!");
        exit(EXIT_FAILURE);
    }

    /* 填写服务器地址: IP + 端口 */
    servsockaddr.sin_family = AF_INET;
    servsockaddr.sin_port = htons(SERV_PORT);
    inet_pton(AF_INET, SERV_IP, &servsockaddr.sin_addr); /* 可通过返回值判断IP地址合法性 */

    /* client建链, TCP三次握手, 这里clientsockfd可以不显示绑定地址,内核会选client的本端IP并分配一个临时端口 */
    if (-1 == connect(clientsockfd, (const struct sockaddr *)&servsockaddr, sizeof(servsockaddr))) {
        perror("Connect failed!");
        close(clientsockfd);
        exit(EXIT_FAILURE);
    }

    /* 发消息, 收消息, 网络IO操作,这里没判断返回值,还有其他网络IO API */
    send(clientsockfd, msg, msglen, 0);
    printf("send msg: %s", msg);
    recv(clientsockfd, buff, sizeof(buff), 0);
    printf("recv msg: %s", buff);

    /* 关闭TCP连接, 四次握手 */
    shutdown(clientsockfd, SHUT_RDWR);
    close(clientsockfd);
    return 0;
}

Makefile

# Makefile
CFLAGS = -Werror -Wall -m64
COMPILE = $(CC) $^ $(CFLAGS) -o $@

.PHONY : all
all : server.bin client.bin

# 注意缩进都是 TAB
server.bin : server.c
    $(COMPILE)

client.bin : client.c
    $(COMPILE)

.PHONY : clean
clean :
    rm *.bin

可以用 netstat -apn | grep "127.0.0.1" 查看上面例子中的连接状态。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值