操作系统学习笔记(四)---socket实现简单的聊天程序

本文详细介绍了如何在Linux环境下使用C/C++通过socket实现简单的网络聊天程序,包括使用父子进程和pthread线程。内容涵盖了socket的基本概念、TCP与UDP的区别、socket函数的使用、字节序处理、常用网络编程头文件、I/O读写函数,以及多线程在聊天程序中的应用。此外,还提供了服务器和客户端的代码示例及其运行效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

一、socket相关资料、流程

附:Linux头文件整理

附:I/O读写操作函数

二、用父子进程实现简单的网络聊天程序(C/C++  运行环境Ubuntu)

client.c

server.c

运行效果

三、pthread实现简单的网络聊天程序(C/C++ 运行环境Ubuntu)

pthread创建进程/线程及锁的相关代码

思路及流程

server.c

server.c

运行效果


PS:现在以前端的角度写了个简易聊天室,github地址:https://github.com/ChenMingK/chat-room

一、socket相关资料、流程

Socket?

网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。

建立网络通信连接至少要一对端口号(socket)。socket本质是编程接口(API),对TCP/IP的封装,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口;HTTP是轿车,提供了封装或者显示数据的具体形式;Socket是发动机,提供了网络通信的能力。

Socket的英文原义是“孔”或“插座”。作为BSD UNIX的进程通信机制,取后一种意思。通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。在Internet上的主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务。Socket正如其英文原义那样,像一个多孔插座。一台主机犹如布满各种插座的房间,每个插座有一个编号,有的插座提供220伏交流电, 有的提供110伏交流电,有的则提供有线电视节目。 客户软件将插头插到不同编号的插座,就可以得到不同的服务。

 

TCP与UDP

TCP---传输控制协议,提供的是面向连接、可靠的字节流服务。当客户和服务器彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据。TCP提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另一端。

UDP---用户数据报协议,是一个简单的面向数据报的运输层协议。UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快

TCP和UDP都是在传输层上的。简单来说,UDP发送数据的时候是不管数据有没有真正达到目的地的,所以传输起来速度就比较快了。但是同时也容易造成数据丢失。而TCP我们知道有三次握手建立,四次握手释放,所以传输更准确,但是速度可能会相对慢一些。

为确保正确地接收数据,TCP要求在目标计算机成功收到数据时发回一个确认(即ACK)。如果在某个时限内未收到相应的ACK,将重新传送数据包。如果网络拥塞,这种重新传送将导致发送的数据包重复。但是,接收计算机可使用数据包的序号来确定它是否为重复数据包,并在必要时丢弃它。

 

socket函数创建套接字

#include <sys/types.h>         

#include <sys/socket.h>

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

socket函数对应于普通文件的打开操作。普通文件的打开操作返回一个文件描述字,而socket()用于创建一个socket描述符(socket descriptor),它唯一标识一个socket。这个socket描述字跟文件描述字一样,后续的操作都有用到它,把它作为参数,通过它来进行一些读写操作。

正如可以给fopen的传入不同参数值,以打开不同的文件。创建socket的时候,也可以指定不同的参数创建不同的socket描述符,socket函数的三个参数分别为

domain:即协议域,又称为协议族(family)。常用的协议族有,AF_INET、AF_INET6、AF_LOCAL(或称AF_UNIX,Unix域socket)、AF_ROUTE等等。协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址。

type:指定socket类型。常用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等。

protocol:故名思意,就是指定协议。常用的协议有,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。

注意:并不是上面的type和protocol可以随意组合的,如SOCK_STREAM不可以跟IPPROTO_UDP组合。当protocol为0时,会自动选择type类型对应的默认协议。

当我们调用socket创建一个socket时,返回的socket描述字它存在于协议族(address family,AF_XXX)空间中,但没有一个具体的地址。如果想要给它赋值一个地址,就必须调用bind()函数,否则就当调用connect()、listen()时系统会自动随机分配一个端口。

 

bind函数

bind函数将一个地址族中的特定地址赋给socket,例如对应AF_INET、AF_INET6就是把一个ipv4或ipv6地址和端口号组合赋给socket。

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

函数的三个参数分别为:

 

sockfd:即socket描述字,它是通过socket()函数创建的,唯一标识一个socket。bind()函数就是将给这个描述字绑定一个名字。

addr:一个const struct sockaddr *指针,指向要绑定给sockfd的协议地址。这个地址结构根据地址创建socket时的地址协议族的不同而不同,如ipv4对应的是:

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 */

};

ipv6对应的是:

struct sockaddr_in6 {

    sa_family_t     sin6_family;   /* AF_INET6 */

    in_port_t       sin6_port;     /* port number */

    uint32_t        sin6_flowinfo; /* IPv6 flow information */

    struct in6_addr sin6_addr;     /* IPv6 address */

    uint32_t        sin6_scope_id; /* Scope ID (new in 2.4) */

};

 

struct in6_addr {

    unsigned char   s6_addr[16];   /* IPv6 address */

};

Unix域对应的是:

#define UNIX_PATH_MAX    108

 

struct sockaddr_un {

    sa_family_t sun_family;               /* AF_UNIX */

    char        sun_path[UNIX_PATH_MAX];  /* pathname

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值