套接字基礎知識

POSIX API 中有四種型別的套接字:TCP,UDP,UNIX 和(可選)RAW。Unix 域套接字可能像流套接字或資料包套接字一樣。

一些端點型別:

  1. struct sockaddr - 通用端點型別。通常,其他具體端點型別僅在 posix 呼叫中轉換為此型別。
  2. struct sockaddr_in - IPv4 端點
struct sockaddr_in {
    sa_family_t sin_family;
    in_port_t sin_port;            /* Port number.  */
    struct in_addr sin_addr;       /* Internet address.  */
};
struct in_addr {
    in_addr_t s_addr;
};
  1. struct sockaddr_in6 - IPv6 端點
struct sockaddr_in6 {
    sa_family_t sin6_family;
    in_port_t sin6_port;    /* Transport layer port # */
    uint32_t sin6_flowinfo;    /* IPv6 flow information */
    struct in6_addr sin6_addr;    /* IPv6 address */
    uint32_t sin6_scope_id;    /* IPv6 scope-id */
};
  1. struct sockaddr_un
struct sockaddr_un {
    sa_family_t sun_family;               /* AF_UNIX */
    char        sun_path[108];            /* pathname */
};

整個計劃

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

#define DESIRED_ADDRESS "127.0.0.1"
#define DESIRED_PORT 3500
#define BUFSIZE 512

int main()
{
    // ADDRESS PART
    // MAIN PART
    close(sock);
    return EXIT_SUCCESS;
}

建立 IPv4 端點

struct sockaddr_in addr = {0};
addr.sin_family = AF_INET;
addr.sin_port = htons(DESIRED_PORT); /*converts short to
                                       short with network byte order*/
addr.sin_addr.s_addr = inet_addr(DESIRED_ADDRESS);

TCP 伺服器片段

int sock = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock == -1) {
    perror("Socket creation error");
    return EXIT_FAILURE;
}

if (bind(sock, (struct sockaddr*) &addr, sizeof(addr)) == -1) {
    perror("Bind error");
    close(sock);
    return EXIT_FAILURE;
}

if (listen(sock, 1/*length of connections queue*/) == -1) {
    perror("Listen error");
    close(sock);
    return EXIT_FAILURE;
}

socklen_t socklen = sizeof addr;
int client_sock = accept(sock, &addr, &socklen); /* 2nd and 3rd argument may be NULL. */
if (client_sock == -1) {
    perror("Accept error");
    close(sock);
    return EXIT_FAILURE;
}

printf("Client with IP %s connected\n", inet_ntoa(addr.sin_addr));

char buf[BUFSIZE];
if (send(sock, "hello", 5, 0) == -1) {
    perror("Send error");
    close(client_sock);
    close(sock);
    return EXIT_FAILURE;
}

ssize_t readden = recv(sock, buf, BUFSIZE, 0);
if (readden < 0) {
    perror("Receive error");
    close(client_sock);
    close(sock);
    return EXIT_FAILURE;
}
else if (readden == 0) {
  fprintf(stderr, "Client orderly shut down the connection.\n");
}
else {readden > 0) {
    if (readden < BUFSIZE)
    {
      fprintf(stderr, "Received less bytes (%zd) then requested (%d).\n", 
        readden, BUFSIZE);
    }

    write (STDOUT_FILENO, buf, readden);
}

TCP 客戶端程式碼段

int sock = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock == -1) {
    perror("Socket creation error");
    return EXIT_FAILURE;
}
if (connect(sock, (struct sockaddr*) &addr, sizeof(addr)) == -1) {
    perror("Connection error");
    close(sock);
    return EXIT_FAILURE;
}

char buf[BUFSIZE];
if (send(sock, "hello", 5, 0); /*write may be also used*/ == -1) {
    perror("Send error");
    close(client_sock);
    close(sock);
    return EXIT_FAILURE;
}

ssize_t readden = recv(sock, buf, BUFSIZE, 0); /*read may be also used*/
if (readden < 0) {
    perror("Receive error");
    close(client_sock);
    close(sock);
    return EXIT_FAILURE;
}
else if (readden == 0)
{
    fprintf(stderr, "Client orderly shut down the connection.\n");
}
else /* if (readden > 0) */ {
    if (readden < BUFSIZE)
    {
      fprintf(stderr, "Received less bytes (%zd) then requested (%d).\n", 
        readden, BUFSIZE);
    }

    write (STDOUT_FILENO, buf, readden);
}  

UDP 伺服器片段

int sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock == -1) {
    perror("Socket creation error");
    return EXIT_FAILURE;
}
if (bind(sock, (struct sockaddr*) &addr, sizeof(addr)) == -1) {
    perror("Bind error");
    close(sock);
    return EXIT_FAILURE;
}

char buf[BUFSIZE];
ssize_t readden = recvfrom(sock, buf, BUFSIZE, 0, &addr, sizeof(addr));
if (readden > 0) {
    printf("Client with IP %s sent datagram\n", inet_ntoa(addr.sin_addr));
    write (STDOUT_FILENO, buf, readden);
}
sendto(sock, "hello", 5, 0, &addr, sizeof(addr));