Socket编程实践(3) --Socket API

socket函数

#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);

创建一个套接字用于通信

參数:

domain:指定通信协议族(protocol family),经常使用取值AF_INET(IPv4)

type:指定socket类型, 流式套接字SOCK_STREAM。数据报套接字SOCK_DGRAM,原始套接字SOCK_RAW

protocol:协议类型,经常使用取值0, 使用默认协议

返回值:

成功: 返回非负整数,套接字;

失败: 返回-1

bind函数

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

绑定一个本地地址到套接字

參数:

sockfd:socket函数返回的套接字

addr:要绑定的地址

//sockaddr_in结构, bind时须要强制转换成为struct sockaddr*类型
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 */
};
/* Internet address. */
struct in_addr
{
    uint32_t       s_addr;     /* address in network byte order */
};
/**演示样例:INADDR_ANY的使用, 绑定本机随意地址**/
int main()
{
    int listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if (listenfd == -1)
        err_exit("socket error");

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8001);
    //绑定本机的随意一个IP地址, 作用同以下两行语句
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    //inet_aton("127.0.0.1", &addr.sin_addr);
    //addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    if (bind(listenfd, (const struct sockaddr *)&addr, sizeof(addr)) == -1)
        err_exit("bind error");
    else
        cout << "bind success" << endl;
}

listen函数

int listen(int sockfd, int backlog);

listen函数应该用在调用socket和bind函数之后, 而且用在调用accept之前, 用于将一个套接字从一个主动套接字转变成为被动套接字。

backlog说明:

对于给定的监听套接口,内核要维护两个队列:

1、已由客户发出并到达服务器,服务器正在等待完毕对应的TCP三路握手过程(SYN_RCVD状态)

2、已完毕连接的队列(ESTABLISHED状态)

可是两个队列长度之和不能超过backlog

backlog推荐使用SOMAXCONN(3.13.0-44-generic中该值为128), 使用等待队列的最大值;

Man-Page中的listen说明:

listen() marks the socket referred to by sockfd as a passive socket, that is, as a socket that

will be used to accept incoming connection requests using accept(2).

The sockfd argument is a file descriptor that refers to a socket of type  SOCK_STREAM  or

SOCK_SEQPACKET.

The backlog argument defines the maximum length to which the queue of pending connections for

sockfd may grow.  If a connection request arrives when the queue is full, the  client may  receive

an  error with an indication of ECONNREFUSED or, if the underlying protocol supports retransmission,

the request may be ignored so that a later reattempt at  connection succeeds.

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvempmMjgwNDQxNTg5/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" />

If the backlog argument is greater than the value in  /proc/sys/net/core/somaxconn(Ubuntu 14.04 该值为128),  then it  is  silently truncated to that value; the default value in this file is 128.  In kernels

before 2.4.25, this limit was a hard coded value, SOMAXCONN, with the value 128.

accept函数

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

从已完毕连接队列返回第一个连接(the first connection request on the queue of  pending  connections  for the listening

socket, sockfd, creates a new connected socket, and returns a new file descriptor referring to that socket.

The newly created socket  is  not  in  the listening state),假设已完毕连接队列为空。则堵塞。The original

socket sockfd is unaffected by this call.

參数:

sockfd:服务器套接字

addr:将返回对等方的套接字地址, 不关心的话, 能够设置为NULL

addrlen:返回对等方的套接字地址长度, 不关心的话能够设置成为NULL, 否则一定要初始化

返回值:

On  success, these system calls return a non-negative integer that is a descriptor for the accepted

socket.  On error, -1 is returned, and errno is set appropriately.

connect函数

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

建立一个连接至addr所指定的套接字

參数:

sockfd:未连接套接字

addr:要连接的套接字地址

addrlen:第二个參数addr长度

演示样例:echo server/client实现

//server端代码
int main()
{
    int listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if (listenfd == -1)
        err_exit("socket error");

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8001);
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    if (bind(listenfd, (const struct sockaddr *)&addr, sizeof(addr)) == -1)
        err_exit("bind error");
    if (listen(listenfd, SOMAXCONN) == -1)
        err_exit("listen error");

    char buf[512];
    int readBytes;
    struct sockaddr_in clientAddr;
    //谨记: 此处一定要初始化
socklen_t addrLen = sizeof(clientAddr);
    while (true)
    {
        int clientfd = accept(listenfd, (struct sockaddr *)&clientAddr, &addrLen);
        if (clientfd == -1)
            err_exit("accept error");
        //打印客户IP地址与端口号
        cout << "Client information: " << inet_ntoa(clientAddr.sin_addr)
             << ", " << ntohs(clientAddr.sin_port) << endl;

        memset(buf, 0, sizeof(buf));
        while ((readBytes = read(clientfd, buf, sizeof(buf))) > 0)
        {
            cout << buf;
            if (write(clientfd, buf, readBytes) == -1)
                err_exit("write socket error");
            memset(buf, 0, sizeof(buf));
        }
        if (readBytes == 0)
        {
            cerr << "client connect closed..." << endl;
            close(clientfd);
        }
        else if (readBytes == -1)
            err_exit("read socket error");
    }
    close(listenfd);
}
//client端代码
int main()
{
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1)
        err_exit("socket error");

    //填写服务器端口号与IP地址
    struct sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(8001);
    serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    if (connect(sockfd, (const struct sockaddr *)&serverAddr, sizeof(serverAddr)) == -1)
        err_exit("connect error");

    char buf[512];
    while (fgets(buf, sizeof(buf), stdin) != NULL)
    {
        if (write(sockfd, buf, strlen(buf)) == -1)
            err_exit("write socket error");
        memset(buf, 0, sizeof(buf));
        int readBytes = read(sockfd, buf, sizeof(buf));
        if (readBytes == 0)
        {
            cerr << "server connect closed... \nexiting..." << endl;
            break;
        }
        else if (readBytes == -1)
            err_exit("read socket error");
        cout << buf;
        memset(buf, 0, sizeof(buf));
    }
    close(sockfd);
}

附-Makefile

.PHONY: clean all
CC = g++
CPPFLAGS = -Wall -g -pthread -std=c++11
BIN = server client
SOURCES = $(BIN.=.cpp)

all: $(BIN)
$(BIN): $(SOURCES) 

clean:
    -rm -rf $(BIN) bin/ obj/ core
时间: 2024-12-20 04:11:19

Socket编程实践(3) --Socket API的相关文章

Socket编程实践(19) --Socket API封装(2)

注:这一片博客与下一篇博客<Socket编程实践(20)>合为一篇,由于代码较多,所以分为两篇,本篇为上篇,主要讲解前一篇讲解的Socket类的增强,下一篇主要讲解怎样使用这个增强版的Socket类(ServerSocket/ClientSocket类的实现与使用)! 思想来源: 1)http://www.cnblogs.com/-Lei/archive/2012/09/04/2670964.html 2)http://blog.csdn.net/column/details/linux66.

Socket编程实践(20) --Socket API封装(3)

注:这一片博客与上一篇博客<Socket编程实践(19)>合为一篇,由于代码较多,所以分为两篇,本篇为下篇,这一篇主要讲解怎样使用上一篇开发的增强版的Socket类(ServerSocket/ClientSocket类的实现与使用)! 思想来源: 1)http://www.cnblogs.com/-Lei/archive/2012/09/04/2670964.html 2)http://blog.csdn.net/column/details/linux66.html 3)http://blo

Socket编程实践(18) --Socket API 封装(1)

序言: 定义一套用于TCP通信比较实用/好用Socket类库(运用C++封装的思想,将socket API尽量封装的好用,实用); 思想来源:http://www.cnblogs.com/-Lei/archive/2012/09/04/2670942.html Socket.h #ifndef SOCKET_H_INCLUDED #define SOCKET_H_INCLUDED #include <sys/socket.h> #include <sys/types.h> #inc

Socket编程实践(2) --Socket编程导引

什么是Socket? Socket能够看成是用户进程与内核网络协议栈的接口(编程接口, 例如以下图所看到的), 其不仅能够用于本机进程间通信.能够用于网络上不同主机的进程间通信, 甚至还能够用于异构系统之间的通信. IPv4套接口地址结构 IPv4套接口地址结构通常也称为"网际套接字地址结构".它以"sockaddr_in"命名,定义在头文件<netinet/in.h>中 //TCP/IP地址结构 struct sockaddr_in { uint8_t

Socket编程实践(6) --TCPNotes服务器

僵尸进程过程 1)通过忽略SIGCHLD信号,避免僵尸进程 在server端代码中加入 signal(SIGCHLD, SIG_IGN); 2)通过wait/waitpid方法.解决僵尸进程 signal(SIGCHLD,onSignalCatch); void onSignalCatch(int signalNumber) { wait(NULL); } 3) 假设多个客户端同一时候关闭, 问题描写叙述如以下两幅图所看到的: watermark/2/text/aHR0cDovL2Jsb2cuY

Socket编程实践(6) --TCP服务端注意事项

僵尸进程处理 1)通过忽略SIGCHLD信号,避免僵尸进程 在server端代码中添加 signal(SIGCHLD, SIG_IGN); 2)通过wait/waitpid方法,解决僵尸进程 signal(SIGCHLD,onSignalCatch); void onSignalCatch(int signalNumber) { wait(NULL); } 3) 如果多个客户端同时关闭, 问题描述如下面两幅图所示: /** client端实现的测试代码**/ int main() { int s

Socket编程实践(10) --select的限制与poll的使用

select的限制 用select实现的并发服务器,能达到的并发数一般受两方面限制: 1)一个进程能打开的最大文件描述符限制.这可以通过调整内核参数.可以通过ulimit -n(number)来调整或者使用setrlimit函数设置,但一个系统所能打开的最大数也是有限的,跟内存大小有关,可以通过cat /proc/sys/fs/file-max 查看 /**示例: getrlimit/setrlimit获取/设置进程打开文件数目**/ int main() { struct rlimit rl;

socket编程之三:socket网络编程中的常用函数

这节本来打算先给出常用函数介绍,再给两个代码实例,写着写着发现越来越长,决定把代码放在下一节. 本节内容持续更新...... 1 socket()函数 原型: int socket(int domain, int type, int protocol); 描述: 类似打开一个文件,返回一个socket描述符,唯一标识一个socket,后面相应的操作都是这用这个socket描述符. 参数: domain:协议族,常用的协议族有AF_INET.AF_INET6.AF_LOCAL.AF_ROUTE等:

Socket编程实践(3)

Socket 基础API实践(2)--实现回射服务器 accept函数 功能:从已完成连接队列返回第一个连接,如果已完成连接队列为空,则阻塞. 原型 int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); 参数 sockfd:服务器套接字 addr:将返回对等方的套接字地址 addrlen:返回对等方的套接字地址长度 返回值 On  success, these system calls return a nonneg