UNIX编程(一)实现最基本的tcp server

1: 先看下客户端和服务端基本的交互流程
在这里插入图片描述
两个基础结构体:
这两个结构体其实是等价的,用于保存socket绑定信息,包括协议族,ip地址,端口。都占用了32个字节(算内存对齐),但 sockaddr 将端口和ip地址写在了一起,保存在 sa_data 中,不便于操作。但操作系统因为遗留问题,所有网络编程接口接收参数都是 sockaddr,因此在定义的时候,会使用 sockaddr_in ,使用的时候强转为 sockaddr

struct sockaddr_in {
	__uint8_t       sin_len;
	sa_family_t     sin_family;
	in_port_t       sin_port;
	struct  in_addr sin_addr;
	char            sin_zero[8];
};
struct sockaddr {
	__uint8_t       sa_len;         /* total length */
	sa_family_t     sa_family;      /* [XSI] address family */
	char            sa_data[14];    /* [XSI] addr value (actually larger) */
};

主要涉及的系统调用有:

套接字生成
//1:协议族,一般选 AF_INET 代表ipv4
//2:连接类型,SOCK_STREAM 代表稳定连接,即tcpip,SOCK_DGRAM 代表不稳定连接 UDP
//3:默认为0
//返回值为socket的标识号
int socket(int, int, int);

绑定
//1:要绑定套接字的标识号
//2:绑定信息(这里一般将sockaddr_in强转为sockaddr)
//3:绑定结构体长度
//返回值-1代表失败
int bind(int, const struct sockaddr *, socklen_t)

监听
//1:监听的socket标识
//2:可以排队的最大连接数
//返回值-1代表失败
int listen(int, int)

建立连接
//1:套接字标识
//后面几个参数就不说了 
//返回连接标识
int connect(int, const struct sockaddr *, socklen_t)

接收客户端连接
//1:套接字标识
//2:发送方协议地址
//3:长度
//返回一个新的描述字,用于表示这次连接
int accept(int, struct sockaddr * __restrict, socklen_t * __restrict)

读取接收信息的api有两组
read / write 和 recv / send,区别在于后者支持的模式更多些。

服务端代码:

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

//using namespace std;
#define MAXLINE 4096

int main(int argc, const char * argv[]) {
    int listenfd, connfd;
    struct sockaddr_in servaddr;
    char buff[4096];
    size_t n;
    
    //1: 建立套接字
    if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        printf("create socket error: %s %d\n",strerror(errno), errno);
        return 0;
    }
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
//    inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr); //两种都可以
    servaddr.sin_port = htons(6666);    //字节序转化,网络字节序是大端字节序
    
    //2: 绑定端口
    //sockaddr_in中区分了端口和服务端地址,由于两个结构体大小相同,因此可以相互转化
    if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1) {
        printf("bind error: %s %d\n",strerror(errno), errno);
        return 0;
    }
    
    //3: 监听客户端请求,在没有收到请求之前,代码阻塞在这儿
    if (listen(listenfd, 10) == -1) {
        printf("listen error: %s %d\n",strerror(errno), errno);
        return 0;
    }
    
    while (1) {
        //4: 接收客户端请求,返回值是一个描述字,用于代表和客户端的连接
        if ((connfd = accept(listenfd, nullptr, nullptr)) == -1) {
            printf("accept error: %s %d\ n",strerror(errno), errno);
        }
        n = recv(connfd, buff, MAXLINE, 0);
        buff[n] = '\0';
        printf("recv msg from client: %s\n", buff);
        close(connfd);
    }
    close(listenfd);
    return 0;
}

客户端代码:

#import <Foundation/Foundation.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <iostream>

int main() {
    int sockfd;
    char sendline[4096];
    
    struct sockaddr_in servaddr;
    
    //1: 生成套接字
    if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) { //SOCK_STREAM 稳定连接->tcp,AF_INET->ipv4 地址
        printf("create socket error: %s %d",strerror(errno),errno);
    }
    
    //2: 初始化连接信息
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;  //AF_INET ipv4 地址
    servaddr.sin_port = htons(6666);
    if (inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr) <= 0) { //点分十进制转二进制整数,127.0.0.1 -> 0001000000001110
        printf("inet_pton error: %s %d",strerror(errno),errno);
        return 0;
    }
    
    //3: 建立连接
    if (connect(sockfd, (sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
        printf("connet error: %s %d",strerror(errno),errno);
        return 0;
    }
    
    //4: 发送数据
    printf("send msg to server:\n");
    while (1) {
        fgets(sendline, 4096, stdin);
        if (send(sockfd, sendline, strlen(sendline), 0) < 0) {
            printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
        }
    }
    close(sockfd);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值