TCP套接字编程 学习笔记 1

0. 套接字函数

1.socket函数

为了执行网络I/O,一个进程必须做的第一件事情就是调用socket函数,指定期望的通信协议类型(IPv4的TCP,IPv6的UDP,Unix域字节流协议等)

#include<sys/socket.h>
int socket ( int family , int type , int protocol);
返回 : 非负的描述符 ------ 成功,-1 ----- 失败-

family : 指明协议族,    (AF_INET,IPv4协议)  ,   (AF_INET,IPv6协议),......

type:指明套接口类型,  (SOCK_STREAM,字节流套接口),(SOCK_DGRAM,数据报套接口),......

protocol:指明协议类型常值,(IPPROTO_TCP,TCP传输协议),(IPPROTO_UDP,UDP传输协议),(IPPROTO_SCTP,SCTP传输协议),(0,family和type组合的系统缺少值).

成功则返回一个套接口描述字

2.connect函数

TCP客户用connect函数来建立与TCP服务器的连接

#include<sys/socket.h>
int connect(int sockfd,const struct sockaddr *servaddr,socklen_t addrlen)
返回:0------成功 -1 ------失败

sockfd : socket函数返回的套接字描述符

servaddr:套接口地址结构(必须含有服务器的地址和端口号)的指针

addrlen:该结构的大小

如果客户端没有绑定端口号,则内核会确定源IP地址,并选择一个临时端口作为源端口

如果是TCP套接口,调用connect函数将激发TCP的三次握手,而且仅在连接建立成功或出错时才返回,其中出错可能有以下几种情况:

  1. 若TCP客户没有收到SYN分节的响应,则返回ETIMEOUT错误.会重复发送3次,如果3次都没有响应则返回该错误
  2. SYN响应为RST(表示复位),表示服务器在该端口上没有进程在等待连接,返回ECONNREFUSED错误.RST产生的3个条件:1.目的地为某端口的SYN到达,然而该商品上没有正在监听的服务器.
                                   2.TCP想取消一个已有的连接
                                   3.TCP接收到一个根本不存在的连接上的分节(TCPv1第246-250页有更详细的信息)
  3. SYN在中间的某个路由器上引发一个目的地不可达的ICMP错误,主机内核保存错误并继续发送SYN,若在某个规定的时间后仍未收到响应,则把保存的消息作为
     EHOSTUNREACH或ENETUNREACH错误返回给进程.

3.bind函数

bind函数把一个本地协议地址赋给一个套接口.

本地协议地址(32位IPv4或128位的IPv6,16位UDP或TCP端口号)

#include<sys/socket.h>
int bind(int sockfd,const struct sockaddr* myaddr,socklen_t addrlen);
返回:0------成功,-1------失败

myaddr:套接口地址结构的指针

addrlen:该结构的大小

调用bind可以指定IP地址和端口,可以两者都指定,也可以都不指定

IP地址 端口 结果
通配地址 0     内核选择IP地址和端口
通配地址 非0 内核选择IP地址,进程选择端口
本地IP地址 0 进程选择IP地址,内核选择端口 
本地IP地址 非0 进程指定IP地址和端口

通过getsockname()来返回实际的协议地址

4.listen函数

listen函数仅由TCP服务器调用,它做两件事情:

1.当socket函数创建一个套接口时,它被假设为一个主动的套接口,也就是说,它是一个即将调用connect发起连接的客户端套接口.listen函数把一个未连接的套接口转换成为一个被动的套接口,指示内核应当接收指向该套接口的连接请求.调用listen寻到套接口由CLOSE状态转换为LISTEN状态

2.第二个参数指定内核应该为相应套接口排队的最大连接个数

#include<sys/socket.h>
int listen(int sockfd,int backlog);
返回:0------成功,-1------失败

内核为任何一个给定的监听套接字维护两个队列

  1. 未完成连接队列:某个客户发出SY并到达服务器,而服务器正在等待完成相应的TCP三路握手过程.
  2. 已完成连接队列:每个已完成TCP三路握手过程的客户对应其中的一项.

5.accept函数

accept函数由TCP服务器调用,用于从已完成连接队列队头取出下一个已完成连接.如果已完成连接队伍为空,那么进程被投入睡眠(假设为阻塞方式)

#include<sys/socket.h>
int accept(int sockfd,struct sockaddr* cliaddr,socklen_t* addrlen);
返回:非负描述符------成功,-1------失败

cliaddr:接收对端客户的协议地址.

addrlen:调用前整数值置为cliaddr地址结构的大小,调用后返回实际接口地址结构内确切的字节数

若返回成功,则内核为每个已连接的客户创建一个已连接的套接口.

简单的服务器时间回显程序

#include<time.h>
#include<iostream>
#include<sys/socket.h>
#include<netinet/in.h>
#include<stdlib.h>
#include<stdio.h>
#include<errno.h>
#include<string.h>
#include<cassert>
#include<unistd.h>
#include<arpa/inet.h>
using std::cout;
class TCP
{
public:
   bool Socket()
   {
      listenfd = socket(AF_INET,SOCK_STREAM,0);
      return listenfd >= 0;
   }
   bool Bind(int port)
   {
      memset(&servaddr,0,sizeof(servaddr));  //清空结构体
      servaddr.sin_family = AF_INET;      //指定协议
      servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //通配地址
      servaddr.sin_port = htons(port);    //指定端口号
      int bind_ret = -1;
      if(listenfd >= 0)
          bind_ret = bind(listenfd,(sockaddr *)&servaddr,sizeof(servaddr));//绑定指定端口
      else
      {
          cout << "listenfd < 0 !!!\n";
          return false;
      }

      return bind_ret == 0;
   }

   bool Listen(int len){
       return listen(listenfd,len) == 0;
   }   

   int Accept()
   {
       socklen_t len = sizeof(cliaddr);
       int connfd = accept(listenfd,(sockaddr *)&cliaddr,&len);   //从已完成连接队列取出首个,若没有则阻塞
       cout << "connect from " << inet_ntop(AF_INET,&cliaddr.sin_addr,buf,sizeof(buf))
            << ", port " << ntohs(cliaddr.sin_port) << "\n";
       return connfd;
   }

   bool Close(int connfd)
   {
       close(connfd);
       return true;
   }

private:
   int listenfd;
   struct sockaddr_in servaddr,cliaddr;
   char buf[1024];
};

int main()
{
   TCP tcp;
   bool r = tcp.Socket();
   assert(r == true);
   r = tcp.Bind(1027);
   assert(r == true);
   r = tcp.Listen(30);
   while(true){
      int connfd = tcp.Accept();
      time_t ticks = time(0);
      char buf[128];
      snprintf(buf,sizeof(buf),"%.24s\r\n",ctime(&ticks));
      write(connfd,buf,strlen(buf));
      tcp.Close(connfd);
   }
   return 1;
}
时间: 2024-11-05 22:02:44

TCP套接字编程 学习笔记 1的相关文章

UNIX网络编程笔记(3)—基本TCP套接字编程

基本TCP套接字编程 主要介绍一个完整的TCP客户/服务器程序需要的基本套接字函数. 1.概述 在整个TCP客户/服务程序中,用到的函数就那么几个,其整体框图如下: 2.socket函数 为了执行网络I/O,一个进程必须要做的事情就是调用socket函数.其函数声明如下: #include <sys/socket.h> int socket(int family ,int type, int protocol); 其中: family:指定协议族 type:指定套接字类型 protocol:指

Unix网络编程之基本TCP套接字编程(上)

TCP客户/服务器实例 服务器程序 #include "unp.h" int main(int argc, char **argv) { int listenfd, connfd; pid_t childpid; socklen_t clilen; struct sockaddr_in cliaddr, servaddr; listenfd = Socket(AF_INET, SOCK_STREAM, 0); //1 bzero(&servaddr, sizeof(servad

《网络编程》基于 TCP 套接字编程的分析

本节围绕着基于 TCP 套接字编程实现的客户端和服务器进行分析,首先给出一个简单的客户端和服务器模式的基于 TCP 套接字的编程实现,然后针对实现过程中所出现的问题逐步解决.有关基于 TCP 套接字的编程过程可参考文章<基本 TCP 套接字编程>.该编程实现的功能如下: (1)客户端从标准输入读取文本,并发送给服务器: (2)服务器从网络输入读取该文本,并回射给客户端: (3)客户端从网络读取由服务器回射的文本,并通过标准输出回显到终端: 简单实现流图如下:注:画图过程通信双方是单独的箭头,只

【UNIX网络编程(二)】基本TCP套接字编程函数

基于TCP客户/服务器程序的套接字函数图如下: 执行网络I/O,一个进程必须做的第一件事就是调用socket函数,指定期望的通信协议类型. #include <sys/socket.h> int socket(int family, int type, int protocol);/*返回值:若成功则为非负描述符,若出错则为-1*/ socket函数成功时返回一个小的非负整数值,它与文件描述符类似,把它称为套接字描述符,简称sockfd.family参数指明协议族,被称为协议域.type参数指

【UNIX网络编程(四)】TCP套接字编程详细分析

引言: 套接字编程其实跟进程间通信有一定的相似性,可能也正因为此,stevens这位大神才会将套接字编程与进程间的通信都归为"网络编程",并分别写成了两本书<UNP1><UNP2>.TCP套接字编程是套接字编程中非常重要的一种,仔细分析,其实它的原理并不复杂.现在就以一个例子来详细分析TCP套接字编程. 一.示例要求: 本节中试着编写一个完成的TCP客户/服务器程序示例,并对它进行深入的探讨.该示例会用到绝大多数的基本函数,未用到但比较重要的函数会在后面的补充上

《网络编程》基本 TCP 套接字编程

在进行套接字编程之前必须熟悉其地址结构,有关套接字的地址结构可参考文章<套接字编程简介>.基于 TCP 的套接字编程的所有客户端和服务器端都是从调用socket 开始,它返回一个套接字描述符.客户端随后调用connect 函数,服务器端则调用 bind.listen 和accept 函数.套接字通常使用标准的close 函数关闭,但是也可以使用 shutdown 函数关闭套接字.下面针对套接字编程实现过程中所调用的函数进程分析.以下是基于 TCP 套接字编程的流程图: socket 函数 套接

Unix网络编程学习笔记之第4章 基于TCP套接字编程

1. socket函数 int socket(int family, int type,int protocol) 成返回一个套接字描述符.错误返回-1 其中family指定协议族,一般IPv4为AF_INET, IPv6为AF_INET6. 其中type指定套接字类型,字节流:SOCK_STREAM.   数据报:SOCK_DGRAM. 一般情况下通过family和type的组合都可以唯一确定一个套接字类型.所以一般我们就把protocol设为0就可以了. 有时在某些特殊情况下,family和

unix网络编程第四章----基于TCP套接字编程

为了执行网络I/O操作.进程必须做的第一件事情就是调用Socket函数.指定期待的通信协议 #include<sys/socket.h> int socket(int family,int type,int protocol); family表示协议族,比如AF_INET,type表示套接字类型, protocol一般设置为0 family: AF_INET ipv4协议 type: SOCK_STREAM 字节流套接字 SOCK_DGRAM 数据报套接字 SOCK_RAW 原始套接字 pro

套接字编程相关函数(2:TCP套接字编程相关函数)

1. 基本TCP客户/服务器程序的套接字函数 2. socket函数 为了执行网络I/O,一个进程必须做的第一件事就是调用socket函数,指定期望的通信协议类型.其定义如下: #include <sys/socket.h> int socket(int family, int type, int protocol); // 返回:若成功则返回非负描述符,若失败则返回-1 其中:family参数指明协议族,它是图4-2中所示的某个常值.该参数也往往被称为协议域. type指明套接字类型,它是图