套接字是网络编程中的一种通信机制,是支持TCP/IP的网络通信的基本操作单元,可以看做是不同主机之间的进程进行双向通信的端点,简单的说就是通信的两方的一种约定,用套接字中的相关函数来完成通信过程。

端口号:

端口号(port)是传输层协议的内容。

端口号是一个2字节16位的整数;

端口号用来标识一个进程,告诉操作系统,当前这个数据交给哪一个程序进行解析;

IP地址 + 端口号能标识网络上的某一台主机的某一个进程;

一个端口号只能被一个进程占用。

端口号 & 进程:

概念

进程有唯一的pid标识,端口号也能标识进程;

一个进程可以绑定多个端口号,一个端口号不能被多个进程绑定。

源端口号 & 目的端口号

传输层协议(TCP/IP)的数据段中有两个端口号,分别叫做源端口号和目的端口号,就是在描述“数据是谁的?发给谁?”

TCP:

(TCP)传输控制协议,面向连接。是一种提供可靠数据传输的通用协议。

传输层协议

有连接

可靠传输

面向字节流

socket API:

1.创建socket文件描述符  (TCP/UDP,客户端+服务器)

int socket(int domain, int type, int protocol);

参数1(domain): 选择创建的套接字所用的协议族;

AF_INET : IPv4协议;

AF_INET6: IPv6协议;

AF_LOCAL: Unix域协议;

AF_ROUTE:路由套接口;

AF_KEY :密钥套接口。

参数2(type):指定套接口类型,所选类型有:

SOCK_STREAM:字节流套接字;

SOCK_DGRAM : 数据报套接字;

SOCK_RAW : 原始套接口。

procotol: 使用的特定协议,一般使用默认协议(NULL)。

2.绑定端口号  (TCP/IP,服务器)

int bind(int socket, const struct sockaddr *address, socklen_t address_len);

参数1(socket) : 是由socket()调用返回的并且未作连接的套接字描述符(套接字号)。

参数2(address):指向特定协议的地址指针。

参数3(address_len):上面地址结构的长度。

返回值:没有错误,bind()返回0,否则SOCKET_ERROR。

3.开始监听socket  (TCP,服务器)

int listen(int socket, int backlog);

参数1(sockfd):是由socket()调用返回的并且未作连接的套接字描述符(套接字号)。

参数2(backlog):所监听的端口队列大小。

4.接受请求  (TCP,服务器)

int accept(int socket, struct sockaddr* address, socklen_t* address_len);

参数1(socket) : 是由socket()调用返回的并且未作连接的套接字描述符(套接字号)。

参数2(address):指向特定协议的地址指针。

参数3(addrlen):上面地址结构的长度。

返回值:没有错误,bind()返回0,否则SOCKET_ERROR。

5.建立连接  (TCP,客户端)

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

6.关闭套接字

int close(int fd);

参数(fd):是由socket()调用返回的并且未作连接的套接字描述符(套接字号)。

服务器端程序:

1 、加载套接字库

2 、创建套接字(socket )。

3 、将套接字绑定到一个本地地址和端口上(bind )。

4 、将套接字设为监听模式,准备接收客户请求(listen )。

5 、等待客户请求到来;当请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept )。

6 、用返回的套接字和客户端进行通信(send/recv )。

7 、返回,等待另一客户请求。

8 、关闭套接字。

#include <stdio.h>

#include <string.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <unistd.h>

int main(int argc, char *argv[])

{

       int server_sockfd;//服务器端套接字

       int client_sockfd;//客户端套接字

       int len;

       struct sockaddr_in my_addr;   //服务器网络地址结构体

       struct sockaddr_in remote_addr; //客户端网络地址结构体

       int sin_size;

       char buf[BUFSIZ];  //数据传送的缓冲区

       memset(&my_addr,0,sizeof(my_addr)); //数据初始化--清零

       my_addr.sin_family=AF_INET; //设置为IP通信

       my_addr.sin_addr.s_addr=INADDR_ANY;//服务器IP地址--允许连接到所有本地地址上

       my_addr.sin_port=htons(8000); //服务器端口号

       /*创建服务器端套接字--IPv4协议,面向连接通信,TCP协议*/

       if((server_sockfd=socket(PF_INET,SOCK_STREAM,0))<0)

       { 

              perror("socket");

              return 1;

       }

        /*将套接字绑定到服务器的网络地址上*/

       if (bind(server_sockfd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr))<0)

       {

              perror("bind");

              return 1;

       }

       /*监听连接请求--监听队列长度为5*/

       listen(server_sockfd,5);

       sin_size=sizeof(struct sockaddr_in);

       /*等待客户端连接请求到达*/

       if((client_sockfd=accept(server_sockfd,(struct sockaddr *)&remote_addr,&sin_size))<0)

       {

              perror("accept");

              return 1;

       }

       printf("accept client %s\n",inet_ntoa(remote_addr.sin_addr));

       len=send(client_sockfd,"Welcome to my server\n",21,0);//发送欢迎信息

       /*接收客户端的数据并将其发送给客户端--recv返回接收到的字节数,send返回发送的字节数*/

       while((len=recv(client_sockfd,buf,BUFSIZ,0))>0)

       {

              buf[len]='\0';

              printf("%s\n",buf);

              if(send(client_sockfd,buf,len,0)<0)

              {

                     perror("write");

                     return 1;

              }

       }

       close(client_sockfd);

       close(server_sockfd);

       return 0;

}

  

客户端程序:

1 、加载套接字库

2 、创建套接字(socket )。

3 、向服务器发出连接请求(connect )。

4 、和服务器端进行通信(send/recv )。

5 、关闭套接字。

#include <stdio.h>

#include <string.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <unistd.h>

int main(int argc, char *argv[])

{

       int client_sockfd;

       int len;

       struct sockaddr_in remote_addr; //服务器端网络地址结构体

       char buf[BUFSIZ];  //数据传送的缓冲区

       memset(&remote_addr,0,sizeof(remote_addr)); //数据初始化--清零

       remote_addr.sin_family=AF_INET; //设置为IP通信

       remote_addr.sin_addr.s_addr=inet_addr("127.0.0.1");//服务器IP地址

       remote_addr.sin_port=htons(8000); //服务器端口号

       /*创建客户端套接字--IPv4协议,面向连接通信,TCP协议*/

       if((client_sockfd=socket(PF_INET,SOCK_STREAM,0))<0)

       {

              perror("socket");

              return 1;

       }

       /*将套接字绑定到服务器的网络地址上*/

       if(connect(client_sockfd,(struct sockaddr *)&remote_addr,sizeof(struct sockaddr))<0)

       {

              perror("connect");

              return 1;

       }

       printf("connected to server\n");

       len=recv(client_sockfd,buf,BUFSIZ,0);//接收服务器端信息

         buf[len]='\0';

       printf("%s",buf); //打印服务器端信息

       /*循环的发送接收信息并打印接收信息--recv返回接收到的字节数,send返回发送的字节数*/

       while(1)

       {

              printf("Enter string to send:");

              scanf("%s",buf);

              if(!strcmp(buf,"quit"))

                     break;

              len=send(client_sockfd,buf,strlen(buf),0);

              len=recv(client_sockfd,buf,BUFSIZ,0);

              buf[len]='\0';

              printf("received:%s\n",buf);

       }

       close(client_sockfd);//关闭套接字

    return 0;

}

  

这里总结GDB调试过程以及命令:

gcc server.c -o server_gdb -g

gdb server_gdb

 

命令

命令缩写

命令说明

list

l

显示多行源代码

break

b

设置断点,程序运行到断点的位置会停下来

info

i

描述程序的状态

run

r

开始运行程序

display

disp

跟踪查看某个变量,每次停下来都显示它的值

step

s

执行下一条语句,如果该语句为函数调用,则进入函数执行其中的第一条语句

next

n

执行下一条语句,如果该语句为函数调用,不会进入函数内部执行(即不会一步步地调试函数内部语句)

print

p

打印内部变量值

continue

c

继续程序的运行,直到遇到下一个断点

set var name=v

设置变量的值

start

st

开始执行程序,在main函数的第一条语句前面停下来

file

装入需要调试的程序

kill

k

终止正在调试的程序

watch

监视变量值的变化

backtrace

bt

产看函数调用信息(堆栈)

frame

f

查看栈帧

quit

q

退出GDB环境

下面是socket在linux下系统调用的分析

1.在include/linux/syscalls.h中定义了sys_socket函数的函数原型

asmlinkage long sys_socket(int, int, int);

系统调用函数必须满足:

asmlinkage long sys_##function-name(##args){ ,return ret}

2.在arch/arm/include/asm,unistd.h中,将sys_socket系统调用和系统调用好关联起来

#define __NR_socket 97

__SYSCALL(__NR_socket, sys_socket)//系统调用号为97

在unistd.h中,同时给出注释表明,给函数的实现在socket.c中

3.进入socket.c(net/中),发现有这样一个函数实现(或者定义)

SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol){ ….}

肯定这就是asmlinkage long sys_socket(int,int,int)的实现了。为了表明这一切,需要进一步查看宏SYSCALL_DEFINE3的定义

SYSCALL_DEINFE3的定义也在syscalls.h中

#define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__)

#define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__)

#define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)

#define SYSCALL_DEFINE4(name, ...) SYSCALL_DEFINEx(4, _##name, __VA_ARGS__)

#define SYSCALL_DEFINE5(name, ...) SYSCALL_DEFINEx(5, _##name, __VA_ARGS__)

#define SYSCALL_DEFINE6(name, ...) SYSCALL_DEFINEx(6, _##name, __VA_ARGS__)

那么:SYSCALL_DEFINE3(socket,int,family,int,type,int,protocal)===SYSCALL_DEFINEX(3,socket,__VA_ARGS__)

由此,我们得到

SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol){ ….}

SYSCALL_DEFINEX(3,_socket,__VA_ARGS__)

_SYSCALL_DEFINE(3,_socket,__VA_ARGS__)

asmlinkage long sys_socket(int family,int type,int protocol)

而他的原型正是:asmlinkage long sys_socketintintint

4.

分析了系统是如何定义和实现sys_socket的系统调用。接下来,仔细分析sys_socket是如何实现创socket的。在前面,我们知道inet_family_ops中的create函数为inet_create,也就是说,如果要创建inet型的socket,将由函数inet_create来创建。

先来看看inet_family_ops

static const struct net_proto_family inet_family_ops = {

.family = PF_INET,

.create = inet_create,

.owner  = THIS_MODULE,

};

下面看看sys_socket中的函数调用关系:

sys_socket

|

+--------- sock_create

|                    |

|                    +------- __sock_create

|                                         |

|                                         +------- security_socket_create

|                                         +-------- sock_alloc()

|                                         +--------- rcu_dereference(net_families[family])

|                                         +--------- pf->create(net, sock, protocol, kern)

|                                         +--------- module_put(pf->owner)

|                                         +--------- security_socket_post_create

+---------- sock_map_fd

sys_socket 调用sock_create函数,最终调用rcu_dereference函数来得到相应的net_family_ops,在这里是inet_family_ops,然后调用inet_family_ops结构中的create函数,这里是inet_create函数,来创建socket。sock_map_fd是得到一个文件号。

当使用socket(int,int,int)创建一个socket时,socket会调用sys_socket来完成socket的创建。

以C语言为例完成简单的网络聊天程序以及关于socket在Linux下系统调用的分析的更多相关文章

  1. 以您熟悉的编程语言为例完成一个hello/hi的简单的网络聊天程序

    Socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信,应用程序通常通过"套接字"向网络发出 ...

  2. hello/hi的简单的网络聊天程序

    hello/hi的简单的网络聊天程序 0 Linux Socket API Berkeley套接字接口,一个应用程序接口(API),使用一个Internet套接字的概念,使主机间或者一台计算机上的进程 ...

  3. python实现一个简单的网络聊天程序

    一.Linux Socket 1.Linux Socke基本上就是BSD Socket(伯克利套接字) 伯克利套接字的应用编程接口(API)是采用C语言的进程间通信的库,经常用在计算机网络间的通信.B ...

  4. 用Java实现简单的网络聊天程序

    Socket套接字定义: 套接字(socket)是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开.读写和关闭等操作.套接字允许应用程序将I/O插入到网络中,并与网络中的其他 ...

  5. telnet指令研究—以网络聊天程序为例

    一.telnet指令 Telnet取名自Telecommunications和Networks的联合缩写,是早期个人计算机上连接到服务器主机的一个网络指令,由于存在安全问题,现在已经很少被使用.在wi ...

  6. 简单的网络爬虫程序(Web Crawlers)

    程序比较简单,但是能体现基本原理. package com.wxisme.webcrawlers; import java.io.*; import java.net.*; /** * Web Cra ...

  7. 使用Java实现hello/hi的简单网络聊天程序

    Socket又称套接字,是基于应用服务与TCP/IP通信之间的一个抽象,它是计算机之间进行通信的一种约定或一种方式.通过socket这种约定,一台计算机可以接收其他计算机的数据,也可以向其他计算机发送 ...

  8. 一个简单的IM聊天程序Pie IM(以后会更新)

    这个程序用多线程,实现设备之间的聊天,支持win10通知,欢迎下载 依赖的第三方库 win10toast 代码 将以下代码写入任意.py文件 1 print('Welcome to use Pie I ...

  9. C#编写简单的聊天程序

    这是一篇基于Socket进行网络编程的入门文章,我对于网络编程的学习并不够深入,这篇文章是对于自己知识的一个巩固,同时希望能为初学的朋友提供一点参考.文章大体分为四个部分:程序的分析与设计.C#网络编 ...

随机推荐

  1. [HDU4609] 3-idiots - 多项式乘法,FFT

    题意:有\(n\)个正整数,求随机选取一个3组合,能构成三角形的概率. Solution: 很容易想到构造权值序列,对其卷积得到任取两条边(可重复)总长度为某数时的方案数序列,我们希望将它转化为两条边 ...

  2. IntelliJ IDEA,酷炫插件系列,提高你的工作效率【plugins】

                             今天介绍一下IDEA的一些炫酷的插件,IDEA强大的插件库.                          不仅能给我们带来一些开发的便捷,还能体 ...

  3. jvm 结构分析

    jvm区域总体分两类,heap区和非heap区.heap区又分: Eden Space(伊甸园).Survivor Space(幸存者区).Tenured Gen(老年代-养老区). 非heap区又分 ...

  4. 集成Log4Net到自己的Unity工程

    需要使用的插件库说明: Loxodon Framework Log4NetVersion: 1.0.0© 2016, Clark Yang=============================== ...

  5. JS点击显示隐藏内容

    JS点击显示隐藏密码 思路:获取元素,判断点击,如果DIV显示就隐藏,如果DIV隐藏就显示出来. 1 if(DIV是显示的){ 2 div.style.display='none'; 3 } 4 el ...

  6. jdk8-》日期时间及其格式处理类特性

    一.JDK8之时间⽇期处理类 核⼼类: LocalDate:不包含具体时间的⽇期. LocalTime:不含⽇期的时间. LocalDateTime:包含了⽇期及时间. LocalDate 常⽤API ...

  7. selenium的鼠标事件操作

    自动化测试过程中,经常会用到鼠标事件,在selenium的action_chains模块的ActionChains定义了鼠标操作的一些事件,要使用ActionChains类中的方法,首先需要对Acti ...

  8. 励志写一篇有味道的博文------json

    之前有更古老的数据交互中间键xml,但是由于比较复杂后来出现了json json :轻量级数据交换格式 json与python数据对比 json        python object      d ...

  9. Oracle的表空间、用户和表的区别和联系

    Oracle的表空间.用户和表的区别和联系 Oracle数据库是通过表空间来存储实际存在的那些表.索引.视图的, 表空间分类: 临时表空间:   用于存储数据库中单持久性模型对象,如表.索引.视图等, ...

  10. Linux服务器上实现数据库和图片文件的定时备份

    一. 1.首先创建一个目录,用于存放备份的数据   2.在该目录下创建两个子目录一个用于存放数据库的信息,一个用于存放图片资源       3.#数据库的备份 执行下面的命令    mysqldump ...