阅读目录
- 基本的局域网聊天
- 客户端服务端双向异步聊天源码
- 局域网内服务端和有限个客户端聊天源码
- 完美异步聊天服务端和客户端源码
- C++定时器
- select异步代码
- pthead多线程
服务端: 服务器端先初始化socket,然后与端口绑定,对端口进行监听,调用accept阻塞,等待客户端连接。 socket() -> bind() -> listen() -> accept() 客户端: 客户端先初始化socket,然后与服务端连接,服务端监听成功则连接建立完成 socket() -> connect()
socket的大概过程是这样的:
服务端先创建一个套接字,端口绑定,对端口进行监听,调用accpet阻塞,等待客户端连接。客户端创建一个套接字,然后通过三次握手完成tcp连接后服务端accpet返回重新建立一个套接字代表返回客户端的tcp连接,(在accpet成功返回前有一个要注意的是server会有两个队列,一个存放完成三次握手的一个是未完成三次握手的,每次accpet会从完成三次握手的队列中取出一个并一直建立TCP连接,此时才能算是真正的连接成功),完成上面的步骤后即可以开始数据的传输了,数据传输结束后再调用close关闭连接
此外再说一下select函数在server和client双向通信中的重要作用:网络编程的过程中,经常会遇到许多阻塞的函数,网络编程时使用的recv, recvfrom、connect函数都是阻塞的函数,当函数不能成功执行的时候,程序就会一直阻塞在这里,无法执行下面的代码。selcet函数是一个轮循函数,即当循环询问文件节点,可设置超时时间,超时时间到了就跳过代码继续往下执行,就像我们下面的第一个程序一样,如果不注释掉server的send那么如果server不想client发送消息则进程就会停顿在此处等待server发送无法执行下面的代码,无法接受client发送过来的消息,第二个程序就对此进行的改进,在程序中引入了select当超时后就会跳过当前代码,执行下一步不会一直阻塞。(poll和epoll是对select的改进)
TCP编程的服务器端一般步骤是: | UDP编程的服务器端一般步骤是: |
---|---|
1、创建一个socket,用函数socket(); 2、设置socket属性,用函数setsockopt(); * 可选 3、绑定IP地址、端口等信息到socket上,用函数bind(); 4、开启监听,用函数listen(); 5、接收客户端上来的连接,用函数accept();6、收发数据,用函数send()和recv(),或者read()和write(); 7、关闭网络连接; 8、关闭监听; | 1、创建一个socket,用函数socket(); 2、设置socket属性,用函数setsockopt();* 可选 3、绑定IP地址、端口等信息到socket上,用函数bind();4、循环接收数据,用函数recvfrom(); 5、关闭网络连接; |
TCP编程的客户端一般步骤是: | UDP编程的客户端一般步骤是: |
1、创建一个socket,用函数socket(); 2、设置socket属性,用函数setsockopt();* 可选 3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选 4、设置要连接的对方的IP地址和端口等属性; 5、连接服务器,用函数connect(); 6、收发数据,用函数send()和recv(),或者read()和write(); 7、关闭网络连接; | 1、创建一个socket,用函数socket(); 2、设置socket属性,用函数setsockopt();* 可选 3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选 4、设置对方的IP地址和端口等属性; 5、发送数据,用函数sendto(); 6、关闭网络连接; |
基本的局域网聊天
局域网TCP服务端:
实现的功能是client到server的半双工通信,server只能接受接收client发送过来的消息,但是不能向client发送消息。
#include <sys/types.h>``#include <sys/socket.h>``#include <stdio.h>``#include <netinet/in.h>``#include <arpa/inet.h>``#include <unistd.h>``#include <string.h>``#include <stdlib.h>``#include <fcntl.h>``#include <sys/shm.h>``#include <thread>``#include <iostream>``#define PORT 7000``#define QUEUE 20//连接请求队列``int` `conn;``void` `thread_task()``{``}` `int` `main()``{`` ``//printf("%d\n",AF_INET);//IPv4协议`` ``printf``(``"%d\n"``,SOCK_STREAM);``//字节流套接字`` ``int` `ss = socket(AF_INET, SOCK_STREAM, 0);``//若成功则返回一个sockfd(套接字描述符)`` ``//printf("%d\n",ss);`` ``struct` `sockaddr_in server_sockaddr;``//一般是储存地址和端口的。用于信息的显示及存储使用`` ``/*设置 sockaddr_in 结构体中相关参数*/`` ``server_sockaddr.sin_family = AF_INET;`` ``server_sockaddr.sin_port = htons(PORT);``//将一个无符号短整型数值转换为网络字节序,即大端模式(big-endian) `` ``//printf("%d\n",INADDR_ANY);`` ``//INADDR_ANY就是指定地址为0.0.0.0的地址,这个地址事实上表示不确定地址,或“所有地址”、“任意地址”。`` ``//一般来说,在各个系统中均定义成为0值。`` ``server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);``//将主机的无符号长整形数转换成网络字节顺序。 `` ``if``(bind(ss, (``struct` `sockaddr* ) &server_sockaddr, ``sizeof``(server_sockaddr))==-1)`` ``{`` ``perror``(``"bind"``);`` ``exit``(1);`` ``}`` ``if``(listen(ss, QUEUE) == -1)`` ``{`` ``perror``(``"listen"``);`` ``exit``(1);`` ``}` ` ``struct` `sockaddr_in client_addr;`` ``socklen_t length = ``sizeof``(client_addr);`` ``///成功返回非负描述字,出错返回-1`` ``conn = accept(ss, (``struct` `sockaddr*)&client_addr, &length);`` ``//如果accpet成功,那么其返回值是由内核自动生成的一个全新描述符,代表与所返回客户的TCP连接。`` ``//accpet之后就会用新的套接字conn`` ``if``( conn < 0 )`` ``{`` ``perror``(``"connect"``);`` ``exit``(1);`` ``}` ` ``char` `buffer[1024];`` ``//创建另外一个线程`` ``//std::thread t(thread_task);`` ``//t.join();`` ``//char buf[1024];`` ``//主线程`` ``while``(1)`` ``{`` ``//这里把send注释掉了,所以这个程序中server只能是接收client端的数据并能给client发送数据,即使不注释掉也没用,因为没有对是否有数据传入和传入`` ``//进行判断所以按照下面的代码这样写,每次都要先让server输入后才能输出client传过来的数据,若是server不输入则程序无法向下走就没有client发送过来的输出,`` ``//而且每次显示也只能是一行,这样显示就全是错的了,所以就需要select和FD_ISSET的判断了`` ``// memset(buf, 0 ,sizeof(buf));`` ``// if(fgets(buf, sizeof(buf),stdin) != NULL) {`` ``// send(conn, buf, sizeof(buf), 0); `` ``// }` ` ``memset``(buffer, 0 ,``sizeof``(buffer));`` ``int` `len = recv(conn, buffer, ``sizeof``(buffer), 0);``//从TCP连接的另一端接收数据。`` ``/*该函数的第一个参数指定接收端套接字描述符;`` ``第二个参数指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据;`` ``第三个参数指明buf的长度;`` ``第四个参数一般置0*/`` ``if``(``strcmp``(buffer, ``"exit\n"``) == 0)``//如果没有收到TCP另一端发来的数据则跳出循环不输出`` ``{`` ``break``;`` ``}`` ``printf``(``"%s"``, buffer);``//如果有收到数据则输出数据`` ``//必须要有返回数据, 这样才算一个完整的请求`` ``send(conn, buffer, len , 0);``//向TCP连接的另一端发送数据。`` ``}`` ``close(conn);``//因为accpet函数连接成功后还会生成一个新的套接字描述符,结束后也需要关闭`` ``close(ss);``//关闭socket套接字描述符`` ``return` `0;``}
局域网TCP客户端:
/*局域网TCP客户端*/``#include <sys/types.h>``#include <sys/socket.h>``#include <stdio.h>``#include <netinet/in.h>``#include <arpa/inet.h>``#include <unistd.h>``#include <string.h>``#include <stdlib.h>``#include <fcntl.h>``#include <sys/shm.h>` `#define MYPORT 7000``#define BUFFER_SIZE 1024` `int` `main()``{`` ``//