前言

研一的时候写过socket网络编程,研二这一年已经在用php写api都快把之前的基础知识忘干净了,这里回顾一下,主要也是项目里用到了,最近博客好杂乱啊,不过确实是到了关键时刻,各种复习加巩固准备9月份校招,顺便优美的完成手里的项目

概述

socket这个词可以有很多概念:
  • 在TCP/IP协议中,“IP地址+TCP或UDP端口号”唯一标识网络通迅中的一个进程,“IP地址+端口号”就称为socket
  • 在TCP协议中,建立连接的两个进程各自有一个socket来标识,那么这两个socket组成的socket pair就是唯一标识一个连接。socket本身有“插座”的意思,用来描述网络连接中一对一关系
  • TCP/IP协议最早是BSD UNIX上实现,为TCP/IP协议设计的应用层编程接口称为socket API

预备知识

发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出,接收主机把从网络上接到的字节依次保存在接收缓冲区中,也是按照内存地址从低到高的顺序保存,因此,网络数据流的地址应这样规定:
先发出去的数据是低地址,后发出去的数据是高地址

TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节。例如UDP段格式,地址0-1是16位的源端口号,如果这个端口号是1000(0x3e8),则地址0是0x03,地址1是0xe8,也就是先发0x03,再发0xe8,这16位在发送主机的缓冲区中也应该是低地址存0x03,高地址存0xe8

为了网络程序具有可移植性,使同样的c代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换

#include <arpa/inet.h>

/*h表示host,n表示network, htonl表示将32位长整数从主机字节序转换为网络字节序*/
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);

socket地址的数据类型以及相关函数

socket API是一层抽象的网络编程接口,适用于各种底层网络协议,如IPV4,IPV6,UNIX Domain Socket

ipv4地址的数据结构 struct sockaddr_in:





基于TCP协议的网络程序


TCP协议通迅流程:




服务器调用socket()、bind()、listen()完成初始化后,调用accept()阻塞等待,处于监听端口的状态,客户端调用socket()初始化后,调用connect()发出SYN段并阻塞等到服务器应答,服务器应答一个SYN-ACK段,客户端收到后从connect()返回,同时应答一个ACK段,服务器收到后从accept()返回

数据传输过程

建立连接后,TCP提供全双工的通信服务,但是一般的客户端/服务器程序的流程是由客户端主动发起请求,服务器被动处理,一问一答的方式。因此,服务器从accept()返回后立刻调用read(),读socket就像读管道一样,如果没有数据到达就阻塞等待,这时客户端调用write()发送请求给服务器,服务器收到后从read()返回,对客户端的请求进行处理,在此期间客户端调用read()阻塞等待服务器的应答,服务器调用write()将处理结果发回给客户端,再次调用read()阻塞等待下一条请求,客户端收到后从read()返回,发送下一条请求,如此循环下去


如果客户端没有更多的请求了,就调用close()关闭连接,就像写端关闭管道一样,服务器的read()返回0,这样服务器知道客户端关闭了连接,也调用close()关闭连接

简单的TCP网络程序


先写一个服务器端的监听程序,作用是从客户端读取字符,拼上自定义的字符串返回给客户端:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h> #define MAXLINE 80
#define SERV_PORT 9933 int main(void)
{
struct sockaddr_in servaddr, cliaddr; // 服务器和客户端的socket数据结构
// typedef int socklen_t;
// typedef int ssize_t;
socklen_t cliaddr_len;
int listenfd, connfd;
char buf[MAXLINE];
char str[INET_ADDRSTRLEN]; // 创建socket,返回整形socket标识
// domain = AF_INT : 套接字指定IPV4网络协议集
// type = SOCK_STREAM : 可靠的面向流服务器或套接字
// protocol = 0, 选择缺省的传输协议
listenfd = socket(AF_INET, SOCK_STREAM, 0); // 为服务器的sockaddr_in数据结构分配内存地址
memset(&servaddr, 0, sizeof(servaddr));
// 为服务器套接字指定协议族
servaddr.sin_family = AF_INET;
// 为服务器套接字指定ip,INADDR_ANY = 0.0.0.0
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
// 为服务器套接字指定port
servaddr.sin_port = htons(SERV_PORT); // 为一个套接字分配地址,使用socket()创建套接字后,
// 只赋予其使用的协议,并未分配地址,bind()用来为listenfd
// 分配地址,参数解释:
// sockfd = listenfd, bind函数的套接字描述符
// my_addr = &servaddr, 服务器端sockaddr结构的指针
// addrlen = sizeof(servaddr), sockaddr结构的长度
bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); // socket监听,准备接受连接请求
listen(listenfd, 20); printf("王正一的server服务器开始等待客户端连接 ...\n"); while (1) {
cliaddr_len = sizeof(cliaddr);
// 三次握手完成后,服务器调用accept()接受客户端连接
// accept()参数:
// sockfd = listenfd, 服务器端监听的套接字描述符
// cliaddr = &cliaddr, 指向sockaddr结构体指针,客户端
// 的地址信息
// addrlen = &cliaddr_len,确定客户端地址结构体的大小
connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);
read(connfd, buf, MAXLINE);
printf("从客户端%s的%d端口收到数据\n",
inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)), ntohs(cliaddr.sin_port)); strcat(buf, "从王正一机器返回的信息"); write(connfd, buf, MAXLINE);
close(connfd);
}
}

运行状态:






在写一个客户端通过socket通信向服务器发送数据的代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h> #define MAXLINE 80
#define SERV_PORT 9933 int main(int argc, char *argv[])
{
// 需要连接的服务器端socket套接字
// 客户端的socket套接字由系统自动分配
struct sockaddr_in servaddr;
char buf[MAXLINE];
int servfd;
char *str; if (argc != 2) {
printf("使用方法:./client 发送的字符串\n");
return 1;
} str = argv[1]; servfd = socket(AF_INET, SOCK_STREAM, 0); memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = inet_addr("222.31.79.131"); // 传媒ip
servaddr.sin_port = htons(SERV_PORT); // 客户端调用connect连接服务器端指定socket套接字
connect(servfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); write(servfd, str, strlen(str)); read(servfd, buf, MAXLINE);
printf("从服务器返回的信息为:%s", buf); close(servfd); return 0;
}

运行状态:



ok,基本完成了一个简单的TCP通信过程!!



使用fork并发处理多个client的请求

网络服务器通常用fork来同时服务多个客户端,父进程专门负责监听端口,每次accept一个新的客户端连接就fork出一个子进程专门服务这个客户端。但是子进程退出时会产生僵尸进程,父进程要注意处理SIGCHLD信号和调用wait清理僵尸进程









Linux C编程一站式学习读书笔记——socket编程的更多相关文章

  1. gdb笔记 ---《Linux.C编程一站式学习》

    gdb笔记 ---<Linux.C编程一站式学习> 单步执行和跟踪函数调用 函数调试实例 #include <stdio.h> int add_range(int low, i ...

  2. 《Linux/Unix系统编程手册》读书笔记8 (文件I/O缓冲)

    <Linux/Unix系统编程手册>读书笔记 目录 第13章 这章主要将了关于文件I/O的缓冲. 系统I/O调用(即内核)和C语言标准库I/O函数(即stdio函数)在对磁盘进行操作的时候 ...

  3. 《Linux/Unix系统编程手册》读书笔记7 (/proc文件的简介和运用)

    <Linux/Unix系统编程手册>读书笔记 目录 第11章 这章主要讲了关于Linux和UNIX的系统资源的限制. 关于限制都存在一个最小值,这些最小值为<limits.h> ...

  4. 《Linux/Unix系统编程手册》读书笔记1

    <Linux/Unix系统编程手册>读书笔记 目录 最近这一个月在看<Linux/Unix系统编程手册>,在学习关于Linux的系统编程.之前学习Linux的时候就打算写关于L ...

  5. Linux网络编程一站式学习

    提要 学过非常多遍计算机网络,依旧不会网络编程. 看完这篇文章之后就不会是这样了. 环境:Ubuntu14.04 64bit 何为Socket 是基于TCP/IP的网络应用编程中使用的有关数据通信的概 ...

  6. [Linux] Linux C编程一站式学习 Part.3

    Linux系统编程 文件与I/O C标准I/O库函数与Unbuffered I/O函数 C标准I/O库函数printf().putchar().fputs(),会在用户空间开辟I/O缓冲区 系统函数o ...

  7. 《Linux/Unix系统编程手册》读书笔记 目录

    <Linux/Unix系统编程手册>读书笔记1  (创建于4月3日,最后更新4月7日) <Linux/Unix系统编程手册>读书笔记2  (创建于4月9日,最后更新4月10日) ...

  8. 《Linux/Unix系统编程手册》读书笔记9(文件属性)

    <Linux/Unix系统编程手册>读书笔记 目录 在Linux里,万物皆文件.所以文件系统在Linux系统占有重要的地位.本文主要介绍的是文件的属性,只是稍微提及一下文件系统,日后如果有 ...

  9. 《Linux/Unix系统编程手册》读书笔记6

    <Linux/Unix系统编程手册>读书笔记 目录 第9章 这章主要讲了一堆关于进程的ID.实际用户(组)ID.有效用户(组)ID.保存设置用户(组)ID.文件系统用户(组)ID.和辅助组 ...

随机推荐

  1. 打印Ibatis最后,SQL声明

    做项目时,满足这一需求.我们希望最终打印出在数据库运行SQL声明,这些都普遍遇到了一些一般性问题.我会去Appfuse,结果这次没有成功.它是有相关的配置,可是好像没实用.我也就没有深查下去.我想这种 ...

  2. atitit.ajax bp dwr 3.该票据安排使用的流量汇总 VO9o.....

    atitit.ajax bp dwr 3.该票据安排使用的流量汇总 VO9o..... 1. 安装配置 1 1.1. 下载  dwr.jar 1M 1 1.2. 配置注解方式..web.xml 1 2 ...

  3. 使用sqlldr向Oracle导入大的文本(txt)文件

    我们有多种方法可以向Oracle数据库里导入文本文件,但如果导入的文本文件过大,例如5G,10G的文本文件,有些方法就不尽如意了,例如PLSQL Developer中的导入文本功能,如果文本文件过大, ...

  4. Mysql 使用CMD 登陆

    1. 将mysql 的bin 目录加到 系统环境变量中: 这样的目的是为了可以直接在cmd中使用mysql命令,而不用先CD到mysql的bin目录中. 3. 连接目标数据库 . Syntax:   ...

  5. 网易ios 面试

    1 说说 你对  MRC和 ARC 的理解 2  对OC内存分析 有什么好的方法, 3 corePlot 4 pop 动画 5 cocoapods 6 GCD 7 瀑布流 及 uicollection ...

  6. xheditor 进阶

    xhEditor提供两种方式初始化编辑器: 方法1:利用class属性来初始化和传递各种初始化参数,例:  class="xheditor {skin:'default'}" 方法 ...

  7. Strongly connected(hdu4635(强连通分量))

    /* http://acm.hdu.edu.cn/showproblem.php?pid=4635 Strongly connected Time Limit: 2000/1000 MS (Java/ ...

  8. QTP特点有哪些?

    QTP特点有哪些? 浏览:77 | 更新:2013-06-19 12:35 QTP是一个侧重于功能的回归自动化测试工具:提供了很多插件,如:.NET的,Java的,SAP的,Terminal Emul ...

  9. 一般处理程序装配数据到html页的原理

    相应html页面并保存状态输出原理:(有状态请求)请求页面提交给后台,获取值进行处理之后再根据name标记读取原html文件文字将值替换再一并返回给页面:(在response时替换)比如原模板< ...

  10. cygwin的安装使用

    Cygwin 是一个用于 Windows 的类 UNIX shell 环境. 它由两个组件组成:一个 UNIX API 库,它模拟 UNIX 操作系统提供的许多特性:以及 Bash shell 的改写 ...