socket实现的一个基本点对点聊天程序

多个TCP连接或多个应用程序进程可能需要通过同一个 TCP协议端口传输数据。为了区别不同的应用程序进程和连接,许多计算机操作系统为应用程序与TCP/IP协议交互提供了称为套接字(Socket)的接口。

服务器监听是指服务端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态。

客户端请求是由客户端的套接字提出连接请求,要连接的目标是服务器端套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器套接字的地址和端口号,然后再向服务器端套接字提出连接请求。

连接确认是当服务器端套接字监听到或者说接收到客户端套接字的连接请求时,它就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的信息发送给客户端,一旦客户端确认了此连接,连接即可建立。而服务器端继续处于监听状态,继续接收其他客户端的连接请求。

套接字(socket)是套接口描述字的简称。和文件句柄相似,SOCKET提供了一咱通讯机制,是WINDOWS的一种通

讯方式。应用程序创建了一个套接字后,就能够获得这种机制提供的网络服务功能。对于服务器来说,它提供

了监听网络的连接请求;对于客户机来说,它可以连接到一个给定的主计算机和特定的端口上。客户端和服务

器端可以通过套接字对象来发送和接收数据。套接字提供了分别基于连接的协议(TCP)等和无连接的协议

(UDP)等,以满足网络连接的可靠性、稳定性以及高速性的要求。

WINSOCK是网络编程接口,它构成了WINDOWS平台下网络编程的基础。

开放系统互连七层模型(OSI)

应用层——表示层——会话层——传输层——网络层——数据链路层——物理层

应用层:用户的应用程序与网络之间的接口

表示层:协商数据交换格式

会话层:允许用户使用简单易记的名称建立连接

传输层:提供终端到终端的可靠连接

网络层:使数据路由经过大型互联网络

数据链路层:决定访问网络介质的方式

物理层:将数据转换为可通过物理介质传送的位

TCP、UDP协议是位传输层的协议,而IP协议则是位于网络层的协议。

TCP是传输控制协议,它是一种面向连接的协议,向用户提供可靠的全双工的字节流。

TCP关心数据传输的准确性。

应用程序利用TCP进行通讯时,发送方和接收方之间会建立一个虚拟连接,通过这一连接,双方可以把数据当作

一个双向的字节流来进行交流。它就像打电话。我们从摘机拨号开始,到拨通后建立连接、进行通话,再到挂

机断开连接这一过程,正是抽象的面向连接的具体表现。首先,在开始通话前,拨号,双方响应,从而建立一

条虚拟的“链路”。只有在双方都处于活动状态,这条“链路”才会保持存在。其次,我们可以通过这条“链

路”进行双向的会话,并且在一般情况下,我们可以通过对方的回答来确定对方是否已经正确听到了我们所说

的话,这相当于面向连接协议为保证传输正确而进行的额外校验。

UDP是用户数据报协议,这是一种无连接的协议。UDP是一种不可靠的数据报协议,它不能保证每一个UDP数据报

可以到达目的地。但是,正是由于它的不可靠性,减少了数据确认的过程,所以UDP传输数据的效率比较高。

就像是邮信。我们只需封好信封,然后将其投到邮筒中即可,但是我们不能保证邮局在把信件发送出去和信件

在发送过程中没有受到伤害。

总体看来,面向连接的服务可以保证双方传递数据的正确性,但却要为此进行额外的校验,通信双方建立通信

信道也需要许多系统开销。而无连接的服务最大的优点就是速度快,因为它不需要去验证数据的完整性,也不

会数据是否已接收而操心。

IP是网际协议,自20世纪80年代以来它一直都是网际协议的主力协议,它使用32位地址,为TCP、UDP、ICMP等

协议提供传送的分组服务。

在WINDOWS网络编程中,套接字接口主要有三种类型:流式套接字、数据报套接字以及原始套接字。

流式套接字定义了一种可靠的面向连接的服务,实现了无差错无重复的顺序数据传输。对于建立在这种流式套

接字类型上的套接字来说,数据可以是双向传输的字节流,无长度限制。

数据报套字接口定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且不保证可靠。

原始套接字允许对低层协议如IP或ICMP直接访问,主要用于网络协议的测试,例如WINDOWS带的PING程序,就是

通过ICMP协议来实现的。

客户/服务器模式

在现在的网络应用中,通信双方最常见的交互模式便是客户/服务器模式。在这种模式下,客户向服务器发出服

务请求,服务器收到请求后为客户提供相应的服务。

客户/服务器模式通常采用监听/连接的方式实现。服务器端应用程序在一个端口监听对服务的请求,也就是说

,服务进程一直处于休眠状态,直到一个客户对这个服务提出了连接请求,此时服务线程被“唤醒”并且为客

户提供服务,即对客户的请求作出适当的反应。

面向连接的协议套接字的调用

面向连接的服务器端首先调用SOCKET()建立一个套接字S,用来监听客户端的连接请求。接着,调用bind()将此

套接字与本机地址、端口绑定起来。然后,调用listen()告诉套字S,对进来的连接进行监听并确认连接请求,

于是S被置于被动的监听模式。一个正在进行监听的套接字将给每个请求发送一个确认信息,告诉发送者主机已

经收到连接请求。当时监听套接字S实际上并不接受连接请求,在客户端请求被接受后,调用

accept()将返回一个与S具有相同属性,但不能被用来进行监听,只用来进行数据收发的数据套接字NS,作为与

客户端套接字相对应的连接的另一个端点。对于该客户端套接字后续的所有操作,都应该通过NS来完成。监听

套接字S仍然用于接收其他客户的连接请求。

面向连接的服务器一般是迸发服务器。在WINDOWS平台上,我们往往在调用accept()返回NS后,会创建一个请求

/应答执行线程,将NS作为参数之一传递给该线程,由该线程来完成客户端与服务器端复杂的请求应答工作,而

主线程会再次调用accept(),以接收新的客户端连接请求。

面向连接的客户端也会调用socket()建立一个套接字C,但使用像TCP这样的面向连接的协议时,客户端不必关

心协议使用什么样的本机地址,所以不用调用bind()。客户端调用connect()向服务器端发出连接请求,在与服

务器建立连接之后,客户端和服务器端就存在了一条虚拟的“管道”,客户端套字C和服务器端套接字NS构成了

“管道”的两个端点。客户端和服务器端通过这个“管道”进行数据交换,多次调用send()/recv()来进行请求

/应答,最终完成服务后关闭用于传输的套接字C和NS,并断开连接,结束此次会话。

面向无连接协议的套接字的调用

采用无连接协议(UDP)时,服务器一般都是面向事务的。一个请求和一个应答就完成了客户程序与服务器程序

之间的相互作用。

无边的服务器使用socket()和bind()来建立和绑定套接字S。与面向连接的服务器端不同,我们不必调用

listen()和accept(),只需要调用recvFrom()在套接字S上等待接收数据就可以了。因为是无连接的,因此网络

上任何一台机器发送的套接字S的数据都可以收到。从这一点上你可以想象,它们是无序的。

无连接的服务器一般都是迭代服务器。它们接收到一个数据报后,马上进行相应处理,直到处理完成后,才开

始下一个数据报的接收、处理。所以采用无连接协议时,客户端和服务器端的交互往往是很简单的,一问一答

或只问不答的方式很常见。

无连接的服务器端只有在停止服务时,才会关闭套接字。

无连接的客户端则更简单,只需要调用socket()建立一个套接字C,就可以利用sendto()和recvfrom()与服务器

的数据进行交换。在完成会话后调用closeSocket()关闭套接字C。

服务器与客户端通过已连接套接字进行接收与发送消息!

p2pcli.c

#include <unistd.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <signal.h>

#include <stdlib.h>

#include <stdio.h>

#include <errno.h>

#include <string.h>

#define ERR_EXIT(m) \

do \

{ \

perror(m); \

exit(EXIT_FAILURE); \

} while(0)

void handler(int sig)

{

printf("recv a sig=%d\n", sig);

exit(EXIT_SUCCESS);

}

int main(void)

{

int sock;

if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)

ERR_EXIT("socket");

struct sockaddr_in servaddr;

memset(&servaddr, 0, sizeof(servaddr));

servaddr.sin_family = AF_INET;

servaddr.sin_port = htons(5188);

servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");

if (connect(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)

ERR_EXIT("connect");

pid_t pid;

pid = fork();

if (pid == -1)

ERR_EXIT("fork");

if (pid == 0)

{

char recvbuf[1024];

while (1)

{

memset(recvbuf, 0, sizeof(recvbuf));

int ret = read(sock, recvbuf, sizeof(recvbuf));

if (ret == -1)

ERR_EXIT("read");

else if (ret == 0)

{

printf("peer close\n");

break;

}

fputs(recvbuf, stdout);

}

close(sock);

kill(getppid(), SIGUSR1);

}

else

{

signal(SIGUSR1, handler);

char sendbuf[1024] = {0};

while (fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)

{

write(sock, sendbuf, strlen(sendbuf));

memset(sendbuf, 0, sizeof(sendbuf));

}

close(sock);

}

return 0;

}

p2psrv.c

#include <unistd.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <signal.h>

#include <stdlib.h>

#include <stdio.h>

#include <errno.h>

#include <string.h>

#define ERR_EXIT(m) \

do \

{ \

perror(m); \

exit(EXIT_FAILURE); \

} while(0)

void handler(int sig)

{

printf("recv a sig=%d\n", sig);

exit(EXIT_SUCCESS);

}

int main(void)

{

int listenfd;

if ((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)

/*    if ((listenfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)*/

ERR_EXIT("socket");

struct sockaddr_in servaddr;

memset(&servaddr, 0, sizeof(servaddr));

servaddr.sin_family = AF_INET;

servaddr.sin_port = htons(5188);

servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

/*servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");*/

/*inet_aton("127.0.0.1", &servaddr.sin_addr);*/

int on = 1;

if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)

ERR_EXIT("setsockopt");

if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)

ERR_EXIT("bind");

if (listen(listenfd, SOMAXCONN) < 0)

ERR_EXIT("listen");

struct sockaddr_in peeraddr;

socklen_t peerlen = sizeof(peeraddr);

int conn;

if ((conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen)) < 0)

ERR_EXIT("accept");

printf("ip=%s port=%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));

pid_t pid;

pid = fork();

if (pid == -1)

ERR_EXIT("fork");

if (pid == 0)

{

signal(SIGUSR1, handler);

char sendbuf[1024] = {0};

while (fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)

{

write(conn, sendbuf, strlen(sendbuf));

memset(sendbuf, 0, sizeof(sendbuf));

}

printf("child close\n");

exit(EXIT_SUCCESS);

}

else

{

char recvbuf[1024];

while (1)

{

memset(recvbuf, 0, sizeof(recvbuf));

int ret = read(conn, recvbuf, sizeof(recvbuf));

if (ret == -1)

ERR_EXIT("read");

else if (ret == 0)

{

printf("peer close\n");

break;

}

fputs(recvbuf, stdout);

}

printf("parent close\n");

kill(pid, SIGUSR1);

exit(EXIT_SUCCESS);

}

return 0;

}

makefile:

.PHONY:clean all

CC=gcc

CFLAGS=-Wall -g

BIN=echosrv echocli echosrv2 p2psrv p2pcli

all:$(BIN)

%.o:%.c

$(CC) $(CFLAGS) -c $< -o [email protected]

clean:

rm -f *.o $(BIN)

socket实现的一个基本点对点聊天程序,布布扣,bubuko.com

时间: 2024-08-25 09:06:23

socket实现的一个基本点对点聊天程序的相关文章

从一个简单的聊天程序SimpleChat看VPN技术

SimpleVPN写好了以后,感觉比较简单,我觉得只有简单的东西才经得起折腾,才能全民折腾,所以说SimpleVPN还不够简单,本文来一个更加简单的,展示一个超级简单的点对点聊天程序,而且还带简单加密哦.顺便,我们再来看下,到底什么是VPN以及怎样实现它.       QQ如今才刚刚行过成年之礼,典型的90后00前,却早已到了后浪把前浪拍到岸边的砍儿,果不其然,被10后的微信给逆袭了...好在都是腾讯的,这就把竞争收敛到了公司内部,不然这将意味着一个巨人的倒下,太可怕了.多年前,很多人逆向过QQ

一个简易的聊天程序(Socket)

效果图: 服务端Server代码: using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.IO; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; using System.Th

netty编写一个简单的聊天程序(六)

1.编写服务端 server启动类:" 1 public class MyChatServer { 2 3 public static void main(String[] args) throws InterruptedException { 4 5 6 EventLoopGroup bossGroup = new NioEventLoopGroup(); 7 EventLoopGroup workerGroup = new NioEventLoopGroup(); 8 9 try{ 10 S

Node.js + Web Socket 打造即时聊天程序嗨聊

前端一直是一块充满惊喜的土地,不仅是那些富有创造性的页面,还有那些惊赞的效果及不断推出的新技术.像node.js这样的后端开拓者直接将前端人员的能力扩大到了后端.瞬间就有了一统天下的感觉,来往穿梭于前后端之间代码敲得飞起,从此由前端晋升为'前后端'. 图片来自G+ 本文将使用Node.js加web socket协议打造一个网页即时聊天程序,取名为HiChat,中文翻过来就是'嗨聊',听中文名有点像是专为寂寞单身男女打造的~ 其中将会使用到express和socket.io两个包模块,下面会有介绍

Android IPC机制(五)用Socket实现跨进程聊天程序

相关文章: Android IPC机制(一)开启多进程 Android IPC机制(二)用Messenger进行进程间通信 Android IPC机制(三)在Android Studio中使用AIDL实现跨进程方法调用 Android IPC机制(四)用ContentProvider进行进程间通信 1.Socket简介 Socket也称作"套接字",是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信.它分为流式套接字和

你也可以写个聊天程序 - C# Socket学习1

阅读目录 简述 一些基础类 利用Socket编写聊天程序 利用TCP编写聊天程序 结束 简述 我们做软件工作的虽然每天都离不开网络,可网络协议细节却不是每个人都会接触和深入了解.我今天就来和大家一起学习下Socket,并写一个简单的聊天程序. 一些基础类 首先我们每天打开浏览器访问网页信息都是使用的HTTP/HTTPS协议,而HTTP是通过的TCP建立的连接.TCP底层又是通过的Socket套接字进行的通信.所以他们之间的抽象关系是: 我们在学习Socket编程的时候可能会需要用到IPEndPo

初试WebSocket构建聊天程序

上一篇文章中使用了Ajax long polling实现了一个简单的聊天程序,对于web实时通信,今天就来试用一下基于WebSocket的长连接方式. WebSocket简介 为了增强web通信的功能,在HTML5中,提供了WebSocket,它不仅仅是一种web通信方式,也是一种应用层协议. WebSocket提供了客户端和服务端之间的双全工跨域通信,通过客户端和服务端之间建立WebSocket连接(实际上是TCP连接,后面会看到),在同一时刻能够实现客户端到服务器和服务器到客户端的数据发送.

《Java项目实践》:简单聊天程序

<Java项目实践>:简单聊天程序 由于这个简单程序,还是涉及到很多的知识点,下面我们就一点一点的来完成. 我们熟悉的QQ聊天,就有一个界面,是吧,我们自己做一个简单的聊天程序,因此我们也就需要为Client写一个界面.因此,这就是我们第一步需要完成的任务. 第一步:为Client端写一个界面 完成一个界面有两种方法,一种是使用Frame对象来完成,另外一种是继承JFrame类来完成.本项目使用第二种. 第二种继承JFrame类完成的界面的程序如下: public class ChatClie

基于C# Winform的简易聊天程序[第一篇-两端通信]

程序简介 本聊天程序支持局域网内部客户端与服务端之间的互相通信. 原理 启动服务端后,服务端通过持续监听客户端发来的请求,一旦监听到客户端传来的信息后,两端便可以互发信息了.服务端需要绑定一个IP,用于客户端在网络中寻找并建立连接.信息发送原理:将手动输入字符串信息转换成机器可以识别的字节数组,然后调用套接字的Send()方法将字节数组发送出去.信息接收原理:调用套接字的Receive()方法,获取对端传来的字节数组,然后将其转换成人可以读懂的字符串信息. 界面设计 - 服务端 IP文本框 na