C语言 TCP socket

TPC协议详解:TCP 详解
 

步骤

本文主要利用socket来创建TCP链接,基本的步骤如下。
Server端步骤如下
1、创建套接字
2、将本地协议地址与sockfd绑定
3、监听本地端口
4、与客服端端通讯

Client端步骤如下
1、创建套接字
2、设置服务器的地址
3、建立与socket的连接
4、与服务器端通讯
 

使用的函数

Socket

#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol); 
/*  功能:创建套接字
	domain:协议族
	type:套接字类型
	protocol:协议类型
	返回值:成功返回套接字描述符号,出错的时候返回-1
*/

domain现在能使用的类型如下,现在常用的类型是AF_INET。

AF_UNIX, AF_LOCAL  /* Local communication              unix(7) */
AF_INET            /* IPv4 Internet protocols          ip(7) */
AF_INET6           /* IPv6 Internet protocols          ipv6(7) */
AF_IPX             /* IPX - Novell protocols */
AF_NETLINK         /* Kernel user interface device     netlink(7) */
AF_X25             /* ITU-T X.25 / ISO-8208 protocol   x25(7) */
AF_AX25            /* Amateur radio AX.25 protocol */
AF_ATMPVC          /* Access to raw ATM PVCs */
AF_APPLETALK       /* AppleTalk                        ddp(7) */
AF_PACKET          /* Low level packet interface       packet(7) */
AF_ALG             /* Interface to kernel crypto API */

type套接字类型有定义如下:

SOCK_STREAM     /* Provides sequenced, reliable, two-way, connection-based byte streams.  An out-of-band data transmission    mechanism may be supported. */
SOCK_DGRAM      /* Supports datagrams (connectionless, unreliable messages of a fixed maximum length). */
SOCK_SEQPACKET  /* Provides a sequenced, reliable, two-way connection-based data transmission path for datagrams of fixed maximum length; a consumer is required to read an entire  packet  with  each input system call. */
SOCK_RAW        /* Provides raw network protocol access. */
SOCK_RDM        /* Provides a reliable datagram layer that does not guarantee ordering. */
SOCK_PACKET     /* Obsolete and should not be used in new programs; see packet(7). */

protocol协议类型有定义如下:

IPPROTO_IP /* IP传输协议 */
IPPROTO_TCP /* TCP传输协议 */
IPPROTO_UDP /* UDP传输协议 */
IPPROTO_STCP /* STCP传输协议 */
IPPROTO_TIPC /* TIPC传输协议 */

bind

#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
/*  功能:将本地协议地址与sockfd绑定
	sockfd:套接字描述符
	addr:一个指向sockaddr结构体类型的指针
	addrlen:sockaddr结构的长度
	返回值:成功返回0,失败返回-1
*/

通用的网络地址标识为sockaddr,为了使不同格式的地址能够传入到套接字函数,地址需要被强制转换成sockaddr类型。struct sockaddr的定义如下:

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

Internet的地址定义在<netinet/in.h>,在IPV4因特网中(AF_INET)中,套接字的地址结构如下:

struct sockaddr_in {
	sa_family_t sin_family;   /*address family*/
	in_port_t sin_port;       /*port*/
	struct in_addr sin_addr;  /*address*/
};

struct in_addr {
	in_addr_t s_addr;
}

listen

#include <sys/types.h>
#include <sys/socket.h>
int listen(int sockfd, int backlog);
/*  功能:监听本地端口
	sockfd:套接字描述符
	backlog:连接请求队列的最大长度(一般由2到4)
	返回值:成功返回0,失败返回-1
*/

accept

#include <sys/socket.h> 
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
/*  功能:建立连接
	sockfd:套接字描述符
	addr:一个指向sockaddr结构体类型的指针
	addrlen:sockaddr结构的长度
	返回值:成功返回0,失败返回-1
*/

connect

#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
/*  功能:建立与指定socket的连接
	sockfd:套接字描述符
	addr:一个指向sockaddr结构体类型的指针
	addrlen:sockaddr结构的长度
	返回值:成功返回0,失败返回-1
*/

recvmsg/sendmsg
socket发送和接收的函数有如下几对:recv/send、readv/writev、recvfrom/sendto、recvmsg/sendmsg。

#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);

ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);

其他
计算机处理器的结构不同,字节序可能不同,分为大端可小端,大端模式下高地址存储低字节,小端模式相反。在不同的计算机之间通信,那么需要考虑字节序。网络协议指定了字节序称之为网络字节序,在利用socket进行通信时,应用程序可能需要在网络字节序与主机字节序之间进行转换,以确保交换格式化数据时字节序不会出现问题。TCP/IP使用大端字节序,在<arpa/inet.h>中定义了4个网络字节序转换函数如下:

#include <arpa/inet.h>
uint32_t htonl(uint32_t hostint32);    /*将32位的数据从主机字节序转换为网络字节序*/
uint16_t htons(uint16_t hostint16);   /*将16位的数据从主机字节序转换为网络字节序*/
uint32_t ntohl(uint32_t netint32);     /*将32位的数据从网络字节序转换为主机字节序*/
uint16_t ntohs(uint16_t netint16);    /*将16位的数据从网络字节序转换为主机字节序*/

网络IP地址的转换函数如下:

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int inet_aton(const char *cp, struct in_addr *inp);
in_addr_t inet_addr(const char *cp); /* 将字符串形式的IP地址转换为网络字节顺序的整型值 */
in_addr_t inet_network(const char *cp);
char *inet_ntoa(struct in_addr in); /* 网络字节顺序的整型值转换为字符串形式的IP地址 */
struct in_addr inet_makeaddr(in_addr_t net, in_addr_t host);
in_addr_t inet_lnaof(struct in_addr in);
in_addr_t inet_netof(struct in_addr in);

实例

信息数据结构定义common_def.h

#ifndef __COMMON_DEF_H__
#define __COMMON_DEF_H__
#ifdef __cplusplus
extern "C" {
#endif

#define PORT 6666
#define IP_ADDR "127.0.0.1"

enum MSG_TYPE {
    MSG_TYPE_SEND,
    MSG_TYPE_RECV,
};

typedef struct msg_data {
    int type;
    char msg[1024];
} msg_data_t;

#ifdef __cplusplus
}
#endif
#endif	// __COMMON_DEF_H__

server.c

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

int main(int argc, char const *argv[])
{
    msg_data_t s_msg;
    int msg_size = sizeof(msg_data_t);
    int recv_size;
    int result;
    int socketfd, accsocfd;
    int len = sizeof(struct sockaddr);
    struct sockaddr_in s_addr, r_addr;

    socketfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (socketfd == -1) {
        printf("socket created failed!\n");
        return -1;
    }

    memset(&s_addr, 0x00, sizeof(s_addr));
    s_addr.sin_family = AF_INET;
    s_addr.sin_port = htons(PORT);
    s_addr.sin_addr.s_addr = inet_addr(IP_ADDR);

    result = bind(socketfd, (struct sockaddr *)&s_addr, sizeof(s_addr));
    if (result == -1) {
        printf("bind failed, error:%s\n", strerror(errno));
        goto failed;
    }

    result = listen(socketfd, 10);
    if (result == -1) {
        printf("listen failed\n");
        goto failed;
    }

    while (1) {
        accsocfd = accept(socketfd, (struct sockaddr *)&r_addr, &len);
        if (accsocfd == -1) {
            printf("accept failed\n");
            goto failed;
        }

        memset(&s_msg, 0x00, sizeof(msg_data_t));

        recv_size = recv(accsocfd, &s_msg, msg_size, 0);
        printf("revc_size: %d, s_msg.type: %d\n", recv_size, s_msg.type);
        if (s_msg.type == MSG_TYPE_RECV) {
            memcpy(s_msg.msg, "server", strlen("server"));
            send(accsocfd, &s_msg, sizeof(msg_data_t), 0);
        } else if (s_msg.type == MSG_TYPE_SEND) {
            printf("server msg: %s\n", s_msg.msg);
        }

        close(accsocfd);
    }

failed:
    if (socketfd != -1) {
        close(socketfd);
        socketfd = -1;
    }

    return 0;
}

client.c

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

int main(int argc, char const *argv[])
{
    msg_data_t c_msg;
    int msg_size = sizeof(msg_data_t);
    int result;
    int socketfd;
    int cmd;
    struct sockaddr_in c_addr;

    socketfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (socketfd == -1) {
        printf("socket created failed!\n");
        return -1;
    }

    memset(&c_addr, 0x00, sizeof(c_addr));
    c_addr.sin_family = AF_INET;
    c_addr.sin_port = htons(PORT);
    c_addr.sin_addr.s_addr = inet_addr(IP_ADDR);

    result = connect(socketfd, (struct sockaddr *)&c_addr, sizeof(c_addr));
    if (result == -1) {
        printf("connect failed, error:%s\n", strerror(errno));
        goto failed;
    }

    memset(&c_msg, 0x00, sizeof(msg_data_t));

    printf("client :> ");
    scanf("%d", &cmd);
    getchar();

    if (cmd == 1) {
        c_msg.type = MSG_TYPE_SEND;
        memcpy(c_msg.msg, "client", strlen("client"));
        send(socketfd, &c_msg, sizeof(c_msg), 0);
    } else if (cmd == 2) {
        c_msg.type = MSG_TYPE_RECV;
        send(socketfd, &c_msg, sizeof(c_msg), 0);

        recv(socketfd, &c_msg, msg_size, 0);
        printf("client msg: %s\n", c_msg.msg);
    }

failed:
    if (socketfd != -1) {
        close(socketfd);
        socketfd = -1;
    }

    return 0;
}
发布了108 篇原创文章 · 获赞 8 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_33242956/article/details/99412823
今日推荐