Linux下的Socket网络编程:一个简易聊天室的实现-徐慧军
- 格式:doc
- 大小:314.50 KB
- 文档页数:18
socket编程聊天室基本流程一、引言Socket编程是一种用于网络通信的编程技术。
它允许程序员创建客户端和服务器应用程序,这些应用程序可以在不同的计算机上运行并通过Internet或局域网相互通信。
在本文中,我们将介绍Socket编程聊天室的基本流程。
二、Socket编程概述Socket编程是一种基于TCP/IP协议的网络编程技术。
它使用套接字(socket)来实现网络通信。
套接字是一种抽象概念,它表示一个网络连接点,可以用来发送和接收数据。
在Socket编程中,客户端和服务器之间建立一个连接,然后通过这个连接进行数据传输。
客户端向服务器发送请求,并等待服务器响应。
服务器接收请求并处理它,并将响应发送回客户端。
三、Socket编程聊天室基本流程1. 创建服务器程序首先,我们需要创建一个服务器程序来监听客户端连接请求。
在Python中,可以使用socket模块来创建套接字对象,并使用bind()方法将其绑定到指定的IP地址和端口号上。
2. 创建客户端程序然后,我们需要创建一个客户端程序来连接到服务器。
同样地,在Python中可以使用socket模块来创建套接字对象,并使用connect()方法连接到指定的IP地址和端口号上。
3. 实现消息传输一旦客户端和服务器之间建立了连接,它们就可以开始进行消息传输。
在Socket编程中,可以使用send()方法将数据发送到对方,使用recv()方法从对方接收数据。
4. 实现聊天室功能为了实现聊天室功能,我们需要让多个客户端能够同时连接到服务器,并且能够相互通信。
为此,我们可以使用多线程或异步编程技术来实现。
在多线程模式下,每个客户端连接都会被分配一个独立的线程来处理。
这个线程负责接收客户端发送的消息,并将其转发给其他客户端。
在异步编程模式下,我们可以使用协程或回调函数来处理消息传输。
当有新的消息到达时,就会触发相应的回调函数进行处理。
5. 实现用户管理为了实现用户管理功能,我们需要让每个客户端都能够注册一个唯一的用户名,并且能够查看当前在线的用户列表。
C#基于Socket实现简单聊天室功能因为这段时间在学习Socket,所以就试着写了⼀个简单的聊天室。
主要分为服务器端和多个客户端。
利⽤服务器端作数据中转站,实现消息群发。
1、服务器端有两个类:using System.Collections.Generic;using ;using .Sockets;namespace 聊天室_Socket_TCP_服务器端{class Program{static List<Client> clients = new List<Client>();static List<Client> notClients = new List<Client>();/// <summary>/// ⼴播消息/// </summary>/// <param name="message"></param>public static void CastMessageTOAllConnetedClients(string message){foreach (var client in clients){if (client.Conneted){client.CastMessage(message);}else{notClients.Add(client);}}foreach (var temp in notClients){clients.Remove(temp);}}static void Main(string[] args){Socket tcpSever = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);tcpSever.Bind(new IPEndPoint(IPAddress.Parse("192.168.1.2"), 8899));tcpSever.Listen(100);//监听是否有客户端发起连接Console.WriteLine("Begin to listen...");while (true){Socket clientSocket = tcpSever.Accept();if (clientSocket!=null){Console.WriteLine("A client has connneted...");Client client = new Client(clientSocket);//将每个新创建的连接通信放于client类做通信clients.Add(client);}}Console.ReadKey();}}}using System;using .Sockets;using System.Text;using System.Threading;namespace 聊天室_Socket_TCP_服务器端{/// <summary>/// 利⽤该类和客户端做通信/// </summary>class Client{public Socket clientSocket;private Thread mesManageTherad;private byte[] bufffer=new byte[20];public Client(Socket soc){clientSocket = soc;//由于消息是不断发送的,需要多次进⾏处理。
基于Socket接口的Linux与Windows网络聊天室设计与实现作者:陈洁孟晓景来源:《软件导刊》2015年第06期摘要:为了实现Linux与Windows跨平台通信,及时共享信息,构建了一个适用于跨平台的网络聊天室通信程序。
先搭建跨平台通信环境,然后使用Socket套接字网络编程接口实现通信。
整个系统采用客户机/服务器(C/S)模型,Windows客户端采用MFC框架编写,Linux 服务器和客户端均采用C语言实现。
通过测试,Linux和Windows的客户端可以成功连接到服务器,从而实现跨平台网络聊天功能。
关键词:Linux;Windows;套接字;客户机/服务器DOIDOI:10.11907/rjdk.151230中图分类号:TP319文献标识码:A 文章编号:16727800(2015)006009403作者简介作者简介:陈洁(1990-),女,山东菏泽人,山东科技大学信息科学与工程学院硕士研究生,研究方向为计算机网络应用技术;孟晓景(1962-),男,浙江绍兴人,山东科技大学信息科学与工程学院教授,研究方向为计算机网络。
0 引言Linux是一个基于POSIX和UNIX的多任务、多用户、支持多线程的操作系统。
Linux在众多方面表现出强大的优势,通常用在控制领域以提高系统性能。
然而,目前使用最多的Windows操作系统图形界面友好,拥有良好的集成开发环境,操作简单,深受广大用户的喜爱。
两种操作系统各有千秋,越来越多场合同时使用,这就出现了跨平台的通信问题。
对此,本文通过Socket套接口来实现不同操作系统之间的实时聊天,运用C/S模式设计出Windows 与Linux操作系统间可以互相通信的网络聊天室,本客户端可以显示其它客户端的消息,实现了信息的及时共享。
1 Socket编程原理与过程1.1 Socket编程原理Socket[1]作为BSD UNIX的进程通信机制,通常也称作“套接字”,用于描述IP地址和端口,是网络通信的基本单元。
Linux下的网络编程与Socket通信在Linux操作系统中,网络编程是一项重要而复杂的技术,而Socket通信则是其中的核心内容。
本文将深入探讨Linux下的网络编程与Socket通信相关的知识和技巧。
一、Linux网络编程概述Linux网络编程指的是在Linux环境下开发网络应用程序的技术。
在Linux系统中,网络编程主要基于Socket套接字实现,通过Socket 套接字,应用程序可以与网络上的其他设备进行通信。
二、Socket通信基础知识1. 什么是Socket?Socket是一种抽象的概念,它提供了一种通信机制,使得不同设备之间可以进行数据传输和通信。
在Linux中,Socket通信是通过套接字(Socket)来实现的。
2. Socket通信的类型Socket通信有两个基本的类型:流式Socket(SOCK_STREAM)和数据报式Socket(SOCK_DGRAM)。
- 流式Socket是一种面向连接的Socket,使用TCP协议,数据传输是可靠和有序的,适用于需要建立可靠连接的数据传输。
- 数据报式Socket是一种无连接的Socket,使用UDP协议,数据传输是不可靠和无序的,适用于对实时性要求较高的数据传输。
三、Linux下的网络编程步骤在Linux下进行网络编程时,我们需要按照以下步骤进行:1. 创建Socket:选择合适的Socket类型,创建一个Socket套接字。
2. 绑定Socket:将Socket与本地地址绑定,以便其他设备可以通过该地址找到它。
3. 监听连接请求(仅适用于流式Socket):对于服务端,需要监听来自客户端的连接请求。
4. 接受连接请求(仅适用于流式Socket):对于服务端,接受客户端的连接请求,建立连接。
5. 连接Socket:对于客户端,连接到服务端的Socket。
6. 发送和接收数据:通过Socket进行数据的发送和接收。
7. 关闭Socket:通信结束后,关闭Socket套接字。
Linux下S ocket网络编程,文件传输,数据传输的C语言例子2010年03月11日星期四 16:15什么是Sock etSocket接口是TCP/IP网络的AP I,Socket接口定义了许多函数或例程,程序员可以用它们来开发TC P/IP网络上的应用程序。
要学Inter net上的T CP/IP网络编程,必须理解Soc ket接口。
Socket接口设计者最先是将接口放在Unix操作系统里面的。
如果了解Uni x系统的输入和输出的话,就很容易了解S ocket了。
网络的 Socket数据传输是一种特殊的I/O,Socket也是一种文件描述符。
Socket也具有一个类似于打开文件的函数调用So cket(),该函数返回一个整型的S ocket描述符,随后的连接建立、数据传输等操作都是通过该S ocket实现的。
常用的Sock et类型有两种:流式Socke t (SOCK_ST REAM)和数据报式So cket(SOCK_DG RAM)。
流式是一种面向连接的Soc ket,针对于面向连接的TCP服务应用;数据报式Socke t是一种无连接的Sock et,对应于无连接的UDP服务应用。
Socket建立为了建立Soc ket,程序可以调用S ocket函数,该函数返回一个类似于文件描述符的句柄。
socket函数原型为:int socket(int domain, int type, int protoco l);domain指明所使用的协议族,通常为PF_I NET,表示互联网协议族(TCP/IP协议族);type参数指定socke t的类型: SOCK_ST REAM 或SOCK_D GRAM,Socket接口还定义了原始Socke t(SOCK_RA W),允许程序使用低层协议;protoco l通常赋值"0"。
python实现简易聊天室(Linux终端)本⽂实例为⼤家分享了python实现简易聊天室的具体代码,供⼤家参考,具体内容如下群聊聊天室1.功能:类似qq群聊功能1.有⼈进⼊聊天室需要输⼊姓名,姓名不能重复2.有⼈进⼊聊天室,其他⼈会受到通知xxx进⼊聊天室3.⼀个⼈发消息,其他⼈会受到消息xxx:xxxxxxxx4.有⼈退出聊天室,其他⼈也会收到通知xxx退出聊天室5.扩展功能:服务端消息公告,服务端发送消息所有⼈都能收到管理员消息:xxxxxxxx2.确定技术模型1.服务端和客户端服务端处理请求,发送管理员消息客户端执⾏各种功能2.套接字选择:udp套接字3.消息发送模型:转发客户端 ~> 服务端 ~> 其他客户端4.存储⽤户信息:{name:addr}5.处理收发关系:多进程分别处理收发3.注意事项1.设计封装⽅案2.写⼀个功能模块测试⼀个模块3.注意注释的添加#coding =utf-8'''chat roomenv:python3.5exc:socket and forkname:mianmabbemail:****************服务端功能:1.搭建⽹络通信2.处理进⼊聊天室* 接收姓名* 判断是否允许进⼊* 将结果反馈给客户端* 如果不允许则结束,允许则将⽤户插⼊数据结构* 给其他⼈发送通知3.处理聊天* 接收消息,判断消息类型,分为L(输⼊姓名),C(发消息),Q(退出聊天室)* 将消息转发4.处理退出聊天室5.发送管理员消息'''from socket import *from os import *from sys import *user = {} #创建空字典⽤来存储⽤户的昵称和地址#处理登录def do_login(s,name,addr):if name in user: #判断昵称是否已经存在s.sendto('该昵称已被占⽤'.encode(),addr)returnelse: #昵称不存在,则发送约定好的'OK's.sendto(b'OK',addr)#功能:有⼈进⼊聊天室,其他⼈会收到消息msg = '\n 欢迎 %s 进⼊聊天室 '%namefor i in user: #发送该条消息给其他⽤户s.sendto(msg.encode(),user[i])user[name] = addr #将该⽤户插⼊数据结构(字典)#处理聊天def do_chat(s,name,text):msg = '%s : %s'%(name,text) #设置消息显⽰格式for i in user:s.sendto(msg.encode(),user[i])#处理退出def do_quit(s,name):msg = '%s 退出了聊天室'%namefor i in user:if i != name: #给其他⼈发送该⽤户退出的消息s.sendto(msg.encode(),user[i])else: #给该⽤户客户端发送约定好的EXIT让⽗进程退出s.sendto(b'EXIT',user[i])del user[name] #删除字典中该⽤户#处理请求def do_request(s):#循环接受所有客户请求while True:try:data,addr = s.recvfrom(1024)except KeyboardInterrupt: #捕获⽗进程直接退出错误exit('服务端退出!')# print(data.decode())msgList = data.decode().split() #按空格拆分为列表,⽅便索引 if msgList[0] == 'L': #判断消息类型do_login(s,msgList[1],addr)elif msgList[0] == 'C':text = ' '.join(msgList[2:]) #将消息中可能有的空格加回来 do_chat(s,msgList[1],text)elif msgList[0] == 'Q':do_quit(s,msgList[1])def main():s = socket(AF_INET,SOCK_DGRAM)ADDR = ('0.0.0.0',8888)s.bind(ADDR)#创建进程pid = fork()if pid < 0:print('Error')elif pid == 0: #⼦进程⽤来发送管理员消息while True:try:text = input('管理员 : ')except KeyboardInterrupt: #捕获⼦进程直接退出错误exit()msg ='C 管理员 %s'%texts.sendto(msg.encode(),ADDR)else: #⽗进程⽤来处理请求do_request(s)main()客户端功能:'''1.搭建通信2.进⼊聊天室* 输⼊姓名* 发送给服务器* 接收服务器反馈* 不允许则重新输⼊,允许则进⼊聊天室* 创建新的进程⽤于消息收发3.聊天* 循环发送消息消息类型分为L(输⼊姓名),C(发消息),Q(退出聊天室)* 循环接收消息4.退出聊天室5.接受管理员消息'''from socket import *from os import *from sys import *ADDR = ('127.0.0.1',8888) #填写服务端地址#循环发送消息def send_msg(s,name):while True:try:text = input() #客户输⼊要发送的消息except KeyboardInterrupt: #⼦进程防⽌⽤户Ctrl+C直接退出text = 'quit'if text.strip() == 'quit': #规定输⼊quit退出msg = 'Q ' + name #消息类型,姓名s.sendto(msg.encode(),ADDR)exit('您已退出聊天室')else:msg = 'C %s %s'%(name,text) #消息类型,姓名,消息s.sendto(msg.encode(),ADDR)#循环接收信息def recv_msg(s):while True:try:data,addr = s.recvfrom(1024)except KeyboardInterrupt: #⽗进程防⽌⽤户Ctrl+C直接退出exit()if data.decode() =='EXIT': #当⽤户退出,⽆需再收消息,约定EXIT让⽗进程退出 exit() #退出⽗进程print(data.decode())#创建⽹络连接def main():s = socket(AF_INET,SOCK_DGRAM)while True:name = input('请输⼊昵称:') #输⼊姓名if not name:returnmsg = 'L ' +name#发送请求s.sendto(msg.encode(),ADDR)#等待回复data,addr = s.recvfrom(1024)if data.decode() == 'OK':print('您已进⼊聊天室')breakelse: #登录失败print(data.decode()) #直接打印服务端的错误信息#创建进程pid = fork()if pid < 0:print('Error')elif pid == 0: #⼦进程发消息send_msg(s,name)else: #⽗进程收消息recv_msg(s)main()先运⾏服务端,再运⾏客户端以上就是本⽂的全部内容,希望对⼤家的学习有所帮助,也希望⼤家多多⽀持。
*****《软件综合开发案例教程》课程设计报告课程设计题目小组成员:**,**,**,**任课讲师和助教:**,**2014年 10 月 29 日摘要这个课程设计整体采用在linux平台下,在同一局域网内实现聊天功能。
整体框架主要分为服务端和客户端两个部分,设置好ip和端口号,在Linux下编译和调试两主大块程序,实现服务端和客户端的连接,然后以服务端作为中继转实现客户端之间的通信。
使用socket网络编程采用TCP/IP协议保证连接可靠,并在项目管理中采用linux流行的gcc和makefile编译,提高了编译和调试效率,加快了项目的完成速度。
本次设计的聊天工具采用字符串命令行的方式进行通信,主要实现了五个功能:用户之间一对一的聊天、编辑、换行、用户之间聊天记录保存、聊天记录调取。
经过测试,程序运行可靠,能满足在linux中实现网络聊天的要求。
关键词 linux,socket,gedit,服务端,客户端,网络编程AbstractThe course design of the overall use in the Linux platform, within the same LAN chat functions.Overall framework consists of server and client two parts, set up IP and port number, two main large program under Linux compiling and debugging, to connect server and client, and then turn to the server as a relay to realize the communication between the e the socket network programming using TCP/IP protocol to ensure reliable connect, and using Linux popular GCC in project management and compile a makefile, improve the efficiency of the compiling and debugging, to speed up the completion of the project.The design way of chat tool USES the string on the command line to communicate, has realized the five main functions: the one-on-one chat between users, editing, line feeds, chat records, chat records obtained between users.After testing, the program runs, reliable and can meet the requirements of the implementation in the Linux network chat. Keywords Linux, socket, gedit, server, client, network programming目录摘要 (II)Abstract (II)第一章绪论.......................................................................................... - 1 -1.1、背景......................................................................................... - 1 -1.2、网络聊天的发展状况 ............................................................ - 1 -1.3、Linux系统............................................................................ - 2 -1.3.1 、Linux的简介............................................................... - 2 -1.3.2 、Linux发展历程........................................................... - 3 - 第二章系统应用技术 ......................................................................... - 4 -2.1 、socket概念及功能 ............................................................... - 4 -2.2、socket类型 ........................................................................... - 5 -2.3 、socket函数 ........................................................................... - 5 -2.3.1 、API功能介绍.............................................................. - 5 -2.3.2、套接口地址结构 ........................................................... - 7 -2.3.3、基本转换函数 ............................................................... - 8 -2.3.4、socket编程流程 ............................................................ - 8 -2.4、TCP/IP 协议......................................................................... - 10 -2.4.1、协议概述 ..................................................................... - 10 -2.4.2、TCP/IP层次结构 ........................................................ - 10 - 第三章需求分析 ............................................................................... - 13 -3.1 、功能需求 ............................................................................ - 13 -3.2、性能需求 .............................................................................. - 13 -3.3 、模块划分 ............................................................................ - 13 -3.3.1、界面需求设计 ........................................................... - 14 -3.3.2 、通信需求 ................................................................... - 14 -3.3.3 、数据存储 ................................................................... - 14 -3.4、系统总流程图 ...................................................................... - 14 - 第四章系统测试 ............................................................................... - 15 -4.1、测试说明 .............................................................................. - 15 -4.2、模块测试 .............................................................................. - 15 -4.3、测试结果分析 ...................................................................... - 16 - 第五章设计和实现 ........................................................................... - 16 -5.1、窗体的设计 .......................................................................... - 16 -5.2、实现服务器与客户端的连接 .............................................. - 17 -5.3、聊天功能 .............................................................................. - 18 -5.4、编辑功能的实现 .................................................................. - 19 -5.5、换行功能的实现 .................................................................. - 20 -5.6、数据库的创建与连接 .......................................................... - 21 -5.6.1、MySQL数据库的简介 ............................................... - 21 -5.6.2、MYSQL环境搭建 ...................................................... - 21 -5.7 、显示聊天记录 .................................................................... - 23 -结论................................................................................................ - 25 - 致谢................................................................................................ - 26 -第一章绪论1.1、背景随着Internet的快速发展和普及,网络已经遍布世界各个角落。
基于Linux C的socket聊天室1 系统功能1.1 支持群聊天。
1. 2 支持向指定用户发送悄悄话功能。
1. 3 支持不同消息不同颜色显示。
1. 4 用户名为登录的唯一标示,所以不允许重名,客户端登录具有重名检查功能。
1. 5 支持上线下线通知。
1. 6 支持服务器发送系统消息功能。
1. 7 支持登录时检测服务器是否在线。
1. 8 支持服务器下线通知客户端,客户端强行下线。
2 硬件平台2.1 功能用来作为对Linux C程序所用软件以及操作系统的安装的载体,2. 2 特点计算机的整体配置还算不错,4G内存,500G硬盘,1G独立显卡等,使得运行一般的大游戏都不卡。
2. 3 组成K43T系列华硕计算机一台,外加键盘一个。
3 软件平台3.1 操作系统平台主要有Windows7操作系统和ubuntu操作系统,电脑以安装Window7为主,ubuntu操作系统以虚拟系统的方式安装在电脑上。
3. 2 系统软件平台主要用到的软件有:Linux的ubuntu操作系统,VMware Workstation软件,Window7下的记事本软件,wps软件和CodeBlocks C编程软件以及GCC编译器等等。
3. 3 系统设计3.3.1模块设计主要写了两个模块程序,一个是服务端程序,另一个是客户端程序,在服务端程序主要完成对服务的基本配置以及对客户端程序的一些初始化参数进行设计等,实现端用户聊天的功能。
在客户端程序里,完成对客户信息的封装,可以供多个客户同时登陆,并能对错误信息给出对应的提示,方便用户使用。
服务程序文件盒客户端程序文件分别为server.c和client.c,使用Gcc来调试运行成server和client文件,先启动server程序,配置服务端信息,然后启动客户端client程序,用户登陆,进行聊天。
3.3.2 服务端程序server.c主要代码如下:typedef struct {char name[10];SA_IN address;} USER;//XXX :用户链表typedef struct Hnode_list {USER data;struct Hnode_list *next;} Hlink, *plink;int memoryError(plink p);int creatUserList(plink head);int findUser(plink head, char name[10]);int delUser(plink head, char name[10]);int getAllUser(plink const head);int addUser(plink head, USER data);//XXX :用户链表void ProcessLogin(char* command, SA_IN rec_addr);void ProcessChat(char* command);void ProcessQuit(char* command);int ProcessPrivate(char* command,SA_IN rec_addr);void ProcessAd();void sig_int(int signum);//ctl+c关掉服务器static plink head;static int socket_fd;int main(void) {char buf[BUFFERSIZE];SA_IN address, rec_addr;socklen_t length;struct timeval tv;_sec=60;//插播广告时间_usec=0;head = malloc(sizeof(Hlink));//用户列表头creatUserList(head); //创建列表signal(SIGINT,sig_int);if ((socket_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {perror("Socket creation fails");exit(-1);}memset(&address, 0, sizeof(address));address.sin_family = AF_INET;address.sin_addr.s_addr = inet_addr(SERVER_IP);address.sin_port = htons(SERVER_PORT);if (bind(socket_fd, (SA *) &address, sizeof(address)) == -1) { perror("Socket is bound to fail");exit(-1);}//30秒没有收到任何消息就发送广告printf("The server to start successip:%s\n",inet_ntoa(address.sin_addr));length = sizeof(rec_addr);setsockopt(socket_fd,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv));while (1) {memset(buf,0,sizeof(buf));//服务器只有此处用来接收if (recvfrom(socket_fd, buf, sizeof(buf), 0, (SA *) &rec_addr, &length) == -1){buf[0]='A';//发送广告标志位}switch (buf[0]) {case 'L'://登录ProcessLogin(buf, rec_addr);break;case 'C'://群聊天ProcessChat(buf);break;case 'Q'://退出ProcessQuit(buf);break;case 'A'://广告ProcessAd();break;case 'P'://悄悄话ProcessPrivate(buf,rec_addr);break;default:printf("ERROR");break;}}}3.3.3 客户端client.c主要代码如下:#define BUFFERSIZE 1024typedef struct sockaddr SA;typedef struct sockaddr_in SA_IN;#define SERVER_IP "127.0.0.1"#define SERVER_PORT 8000void sig_user1(int signo);void sig_user2(int signo);void sig_alrm(int signo);char* ProcessLogin(SA_IN serv_addr);//以下为无关紧要的功能函数声明void dispDot();int msSleep(long ms);static pid_t pid;static int socket_fd;int main(void) {char buf[BUFFERSIZE];char buf_temp[BUFFERSIZE];char name[10];SA_IN serv_addr;signal(SIGUSR1, sig_user1);signal(SIGUSR2,sig_user2);signal(SIGINT,SIG_IGN);//拒绝用户ctrl+c强制退出,只能键入quit退出if ((socket_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {perror("socket build error");exit(-1);}//XXX:服务器配置信息memset(&serv_addr, 0, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = inet_addr(SERVER_IP);serv_addr.sin_port = htons(SERVER_PORT);//XXXstrcpy(name, ProcessLogin(serv_addr));//得到登录名if ((pid = fork()) == -1) {perror("child process build error");return -1;}if (pid == 0)///////////////////////////////////子进程,用来接收服务器的消息{memset(buf,'\0',BUFFERSIZE);//必须清空否则会有虚假数据memset(buf_temp,'\0',BUFFERSIZE);sprintf(buf, "C%s On-line", name);if (sendto(socket_fd, buf, sizeof(buf), 0, (SA *) &serv_addr, sizeof(serv_addr)) < 0){perror("On-line failure");}kill(getppid(), SIGUSR1);//唤醒主进程让其输入while (1) {memset(buf,'\0',BUFFERSIZE);//必须清空否则会有虚假数据memset(buf_temp,'\0',BUFFERSIZE);if (recvfrom(socket_fd, buf, sizeof(buf), 0, NULL, NULL) < 0) { perror("Receive messages failure");}if (strncmp("Q",buf,1) == 0) //接收服务器下线通知{printf("\033[22;30m%s", buf+1); //输出黑色文字系统消息kill(getppid(), SIGUSR2);kill(getpid(), SIGUSR2);}else if (strncmp("A",buf,1) == 0){printf("\033[22;96m%s", buf+1); //输出灰色文字系统消息}else if (strncmp("P",buf,1) == 0){printf("\033[22;31m%s", buf+1); //输出红色文字系统消息}else if (strncmp("R",buf,1) == 0){if (strncmp(buf+1, name, strlen(name)) == 0){sprintf(buf_temp, "me%s", buf + strlen(name)+1);printf("\033[22;32m%s", buf_temp);//输出黄色文字自己的消息}elseprintf("\033[22;33m[Messages are received @]%s", buf+1); //输出绿色文字}printf("\033[22;30m");//恢复黑色}}//////////////////////////////////////////////////////主进程////////////////////////////////////pause(); //等待SIGUSR1int quit_flag = 0;getchar();//清空输入名字时残存的回车符,因为ProcessLogin使用scanf while (1)//父进程{memset(buf,'\0',BUFFERSIZE);//必须清空否则会有虚假数据memset(buf_temp,'\0',BUFFERSIZE);fgets(buf_temp,BUFFERSIZE,stdin); //采用fgets比采用scanf好,聊天时可以键入空格buf_temp[strlen(buf_temp)-1]='\0';if (strncmp("quit", buf_temp, 4) == 0) //退出{sprintf(buf, "Q%s", name);quit_flag = 1;}//键入$开头为悄悄话else if (strncmp("$", buf_temp, 1) == 0) //注:发送悄悄话的格式:$对方名称$消息内容{char *check=buf_temp+1; //必须对其检查一定要有两个$...$否则造成服务器当机while(*check!='\0'&&*check!='$') check++;if(*check!='$'){printf("[system] \n message format: $ each other name $ message content \n < enter >\n");continue;}sprintf(buf, "P%s%s", name,buf_temp);}else //群发消息{sprintf(buf, "C%s say:%s", name, buf_temp);}//开始发送消息if (sendto(socket_fd, buf, sizeof(buf), 0, (SA *) &serv_addr, sizeof(serv_addr)) < 0){perror("Send failure");}if (quit_flag){kill(pid, SIGUSR2);break;}}kill(getpid(), SIGUSR2);}4 系统测试测试数据及截图如下所示:5 系统源码5.1 服务端源码#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <fcntl.h>#include <dirent.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <errno.h>#include <netdb.h>#include <signal.h>#define BUFFERSIZE 1024typedef struct sockaddr SA;typedef struct sockaddr_in SA_IN;#define SERVER_IP "127.0.0.1"#define SERVER_PORT 8000#include <time.h>typedef struct {char name[10];SA_IN address;} USER;//XXX :用户链表typedef struct Hnode_list {USER data;struct Hnode_list *next;} Hlink, *plink;int memoryError(plink p);int creatUserList(plink head);int findUser(plink head, char name[10]); int delUser(plink head, char name[10]); int getAllUser(plink const head);int addUser(plink head, USER data);//XXX :用户链表void ProcessLogin(char* command, SA_IN rec_addr);void ProcessChat(char* command);void ProcessQuit(char* command);int ProcessPrivate(char* command,SA_IN rec_addr);void ProcessAd();void sig_int(int signum);//ctl+c关掉服务器static plink head;static int socket_fd;int main(void) {char buf[BUFFERSIZE];SA_IN address, rec_addr;socklen_t length;struct timeval tv;_sec=60;//插播广告时间_usec=0;head = malloc(sizeof(Hlink));//用户列表头creatUserList(head); //创建列表signal(SIGINT,sig_int);if ((socket_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {perror("Socket creation fails");exit(-1);}memset(&address, 0, sizeof(address));address.sin_family = AF_INET;address.sin_addr.s_addr = inet_addr(SERVER_IP);address.sin_port = htons(SERVER_PORT);if (bind(socket_fd, (SA *) &address, sizeof(address)) == -1) { perror("Socket is bound to fail");exit(-1);}//30秒没有收到任何消息就发送广告printf("The server to start successip:%s\n",inet_ntoa(address.sin_addr));length = sizeof(rec_addr);setsockopt(socket_fd,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv));while (1) {memset(buf,0,sizeof(buf));//服务器只有此处用来接收if (recvfrom(socket_fd, buf, sizeof(buf), 0, (SA *) &rec_addr, &length) == -1){buf[0]='A';//发送广告标志位}switch (buf[0]) {case 'L'://登录ProcessLogin(buf, rec_addr);break;case 'C'://群聊天ProcessChat(buf);break;case 'Q'://退出ProcessQuit(buf);break;case 'A'://广告ProcessAd();break;case 'P'://悄悄话ProcessPrivate(buf,rec_addr);break;default:printf("ERROR");break;}}}void ProcessLogin(char* command, SA_IN rec_addr) {char name[10];USER user;char buf[BUFFERSIZE];memset(buf,'\0',BUFFERSIZE);strcpy(name, command + 1);if (findUser(head, name) == -1) {strcpy(, name);user.address = rec_addr;addUser(head, user);buf[0] = 'Y';if (sendto(socket_fd, buf, sizeof(buf), 0, (SA *) &rec_addr,sizeof(rec_addr)) == -1){perror("Login failed");}}else {buf[0] = 'N';if (sendto(socket_fd, buf, sizeof(buf), 0, (SA *) &rec_addr,sizeof(rec_addr)) == -1){perror("Login failed");}}}void ProcessChat(char* command) {char buf[BUFFERSIZE];SA_IN address;memset(buf,'\0',BUFFERSIZE);sprintf(buf, "R%s\n<Please enter the>\n", command + 1); //'\n'强行推送数据必须否则要到缓冲区满才一次性发送plink p = head->next;while (p != NULL) {address = (p->data).address;if (sendto(socket_fd, buf, sizeof(buf), 0, (SA *) &address,sizeof(address)) < 0){perror("Message sent failure");}p = p->next;}}void ProcessQuit(char* command) {char name[10];char buf[BUFFERSIZE];memset(buf,'\0',BUFFERSIZE);SA_IN address;sprintf(name, "%s", command + 1);plink p = head->next;delUser(head, name);sprintf(buf, "R%s Get offline\n", name);while (p != NULL) {address = (p->data).address;if (sendto(socket_fd, buf, sizeof(buf), 0, (SA *) &address,sizeof(address)) < 0){perror("Message sent failure");}p = p->next;}}void ProcessAd(){char buf[BUFFERSIZE];char buf_temp[BUFFERSIZE];memset(buf,'\0',BUFFERSIZE);//必须清空否则会有虚假数据memset(buf_temp,'\0',BUFFERSIZE);SA_IN address;plink p = head->next;strcat(buf,"A<<System message>>The current online users\n");while (p != NULL){sprintf(buf_temp,"[%s:%s] ", (p->data).name, inet_ntoa((p->data).address.sin_addr));strcat(buf,buf_temp);p = p->next;}strcat(buf,"\n<Please enter the>\n");p = head->next;while (p != NULL) {address = (p->data).address;if (sendto(socket_fd, buf, sizeof(buf), 0, (SA *) &address,sizeof(address)) < 0){perror("Message sent failure");}p = p->next;}}int ProcessPrivate(char* command,SA_IN rec_addr){char buf[BUFFERSIZE];char buf_temp[BUFFERSIZE];char name_from[10];char name_to[10];int non_user_flag=0;int i,j;for(i=1,j=0;command[i]!='$';i++,j++){name_from[j]=command[i];}name_from[j]='\0';i++;for(j=0;command[i]!='$';i++,j++){name_to[j]=command[i];}name_to[j]='\0';i++;memset(buf,'\0',BUFFERSIZE);//必须清空否则会有虚假数据memset(buf_temp,'\0',BUFFERSIZE);strcpy(buf_temp,command+i);strcat(buf_temp,"\n<Please enter the>\n");//双方名字及消息准备就绪sprintf(buf,"P[from%sThe whispers]%s",name_from,buf_temp);plink p = head->next;SA_IN address;while (p != NULL) {if (strcmp((p->data).name, name_to) == 0){address = (p->data).address;break;}p = p->next;}if (p == NULL)non_user_flag=1;if(!non_user_flag){if (sendto(socket_fd, buf, sizeof(buf), 0, (SA *) &address,sizeof(address)) < 0){perror("Whispers send failure");}sprintf(buf,"R%s Have received the message, the wisps sent successfully\n<Please enter the>\n",name_to);if (sendto(socket_fd, buf, sizeof(buf), 0, (SA *)&rec_addr,sizeof(address)) < 0){perror("Message sent failure");}}else{sprintf(buf,"R%s not on-line,Whispers send failure\n<Please enter the>\n",name_to);if (sendto(socket_fd, buf, sizeof(buf), 0, (SA *) &rec_addr,sizeof(address)) < 0){perror("Whispers send failure");}}return 0;}void sig_int(int signum){char buf[BUFFERSIZE];plink p = head->next;SA_IN address;sprintf(buf,"Q The server is offline\n");while (p != NULL) {address = (p->data).address;if (sendto(socket_fd, buf, sizeof(buf), 0, (SA *) &address,sizeof(address)) < 0){perror("Offline messages sent to the client");}p = p->next;}printf("Server is shut down\n");exit(0);}//XXX:使用带头结点的单向链表存放用户信息int memoryError(plink p) //判断内存是否申请成功{if (p == NULL) {printf("MEMORY ERROR!");return 1;}return 0;}int creatUserList(plink head) {if (memoryError(head))return -1;head->next = NULL;}int addUser(plink head, USER data) //始终在表头插入{plink new_create = malloc(sizeof(Hlink));if (memoryError(new_create))return -1;new_create->data = data;new_create->next = head->next;head->next = new_create;}int delUser(plink head, char name[10]) {plink q = head;plink p = head->next;while (p != NULL) {if (strcmp((p->data).name, name) == 0)break;q = p;p = p->next;}if (p == NULL) {printf("Delete user failure\n");return -1;}q->next = p->next;free(p);p = NULL;}int findUser(plink head, char name[10]) { plink p = head->next;while (p != NULL) {if (strcmp((p->data).name, name) == 0)return 0;p = p->next;}if (p == NULL)return -1;}5.2 客户端源码#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <fcntl.h>#include <dirent.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <errno.h>#include <netdb.h>#include <signal.h>#define BUFFERSIZE 1024typedef struct sockaddr SA;typedef struct sockaddr_in SA_IN;#define SERVER_IP "127.0.0.1"#define SERVER_PORT 8000void sig_user1(int signo);void sig_user2(int signo);void sig_alrm(int signo);char* ProcessLogin(SA_IN serv_addr); //以下为无关紧要的功能函数声明void dispDot();int msSleep(long ms);static pid_t pid;static int socket_fd;int main(void) {char buf[BUFFERSIZE];char buf_temp[BUFFERSIZE];char name[10];SA_IN serv_addr;signal(SIGUSR1, sig_user1);signal(SIGUSR2,sig_user2);signal(SIGINT,SIG_IGN);//拒绝用户ctrl+c强制退出,只能键入quit退出if ((socket_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {perror("socket build error");exit(-1);}//XXX:服务器配置信息memset(&serv_addr, 0, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = inet_addr(SERVER_IP);serv_addr.sin_port = htons(SERVER_PORT);//XXXstrcpy(name, ProcessLogin(serv_addr));//得到登录名if ((pid = fork()) == -1) {perror("child process build error");return -1;}if (pid == 0)///////////////////////////////////子进程,用来接收服务器的消息{memset(buf,'\0',BUFFERSIZE);//必须清空否则会有虚假数据memset(buf_temp,'\0',BUFFERSIZE);sprintf(buf, "C%s On-line", name);if (sendto(socket_fd, buf, sizeof(buf), 0, (SA *) &serv_addr, sizeof(serv_addr)) < 0){perror("On-line failure");}kill(getppid(), SIGUSR1);//唤醒主进程让其输入while (1) {memset(buf,'\0',BUFFERSIZE);//必须清空否则会有虚假数据memset(buf_temp,'\0',BUFFERSIZE);if (recvfrom(socket_fd, buf, sizeof(buf), 0, NULL, NULL) < 0) { perror("Receive messages failure");}if (strncmp("Q",buf,1) == 0) //接收服务器下线通知{printf("\033[22;30m%s", buf+1); //输出黑色文字系统消息kill(getppid(), SIGUSR2);kill(getpid(), SIGUSR2);}else if (strncmp("A",buf,1) == 0){printf("\033[22;96m%s", buf+1); //输出灰色文字系统消息}else if (strncmp("P",buf,1) == 0){printf("\033[22;31m%s", buf+1); //输出红色文字系统消息}else if (strncmp("R",buf,1) == 0){if (strncmp(buf+1, name, strlen(name)) == 0){sprintf(buf_temp, "me%s", buf + strlen(name)+1);printf("\033[22;32m%s", buf_temp);//输出黄色文字自己的消息}elseprintf("\033[22;33m[Messages are received @]%s", buf+1); //输出绿色文字}printf("\033[22;30m");//恢复黑色}}//////////////////////////////////////////////////////主进程////////////////////////////////////pause(); //等待SIGUSR1int quit_flag = 0;getchar();//清空输入名字时残存的回车符,因为ProcessLogin使用scanf while (1)//父进程{memset(buf,'\0',BUFFERSIZE);//必须清空否则会有虚假数据memset(buf_temp,'\0',BUFFERSIZE);fgets(buf_temp,BUFFERSIZE,stdin); //采用fgets比采用scanf好,聊天时可以键入空格buf_temp[strlen(buf_temp)-1]='\0';if (strncmp("quit", buf_temp, 4) == 0) //退出{sprintf(buf, "Q%s", name);quit_flag = 1;}//键入$开头为悄悄话else if (strncmp("$", buf_temp, 1) == 0) //注:发送悄悄话的格式:$对方名称$消息内容{char *check=buf_temp+1; //必须对其检查一定要有两个$...$否则造成服务器当机while(*check!='\0'&&*check!='$') check++;if(*check!='$'){printf("[system] \n message format: $ each other name $ message content \n < enter >\n");continue;}sprintf(buf, "P%s%s", name,buf_temp);}else //群发消息{sprintf(buf, "C%s say:%s", name, buf_temp);}//开始发送消息if (sendto(socket_fd, buf, sizeof(buf), 0, (SA *) &serv_addr, sizeof(serv_addr)) < 0){perror("Send failure");}if (quit_flag){kill(pid, SIGUSR2);break;}}kill(getpid(), SIGUSR2);}//////////////////////////////////////////////Siganl///////////////////// /////////////////////////void sig_user2(int signo) {printf("%dEnd of the process\n", getpid());exit(-1);}void sig_user1(int signo) {;}void sig_alrm(int signo){return ;}//////////////////////////////////////////////Siganl///////////////////// /////////////////////////char* ProcessLogin(SA_IN serv_addr) {char buf[10];static char name[10];struct sigaction act; //处理服务器未上线sigaction(SIGALRM,NULL,&act);act.sa_handler=sig_alrm;act.sa_flags&=~SA_RESTART; //有何用处??????sigaction(SIGALRM,&act,NULL);while (1) {printf("Please enter your user name:");buf[0] = 'L';scanf("%s", buf + 1);strcpy(name, buf + 1);alarm(2);//设置连接服务器超时时间为2秒printf("In the login");dispDot();if (sendto(socket_fd, buf, sizeof(buf), 0, (SA *) &serv_addr, sizeof(serv_addr)) < 0){perror("Login failed");}recvfrom(socket_fd, buf, sizeof(buf), 0, NULL, NULL);if (buf[0] == 'N') {printf("This user name has been used, please enter the username again\n");} else if (buf[0] == 'Y') {printf("%slogin success\n",name);alarm(0);//必须关闭闹钟return name;break;}else{printf("The server is not online, log out\n");exit(-1);}}}////////////////////一些无关紧要的功能函数void dispDot(){int i;for(i=0;i<10;i++){printf(". ");fflush(stdout);msSleep(90000);}printf("\n");}int msSleep(long ms){struct timeval tv;_sec = 0;_usec = ms;return select(0, NULL, NULL, NULL, &tv);}。
简单实现linux聊天室程序花了很长时间⽤来练习掌握linux上socket的⼀个聊天室程序,可以实现的哦。
具体代码如下代码⼀:#ifndef _I_H#define _I_H#include <math.h>#include <stdio.h>#include <sys/socket.h>#include <arpa/inet.h>#include <netinet/in.h>#include <unistd.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <stdlib.h>#include <errno.h>#include <string.h>#include <inttypes.h>#include <time.h>#include <sys/ioctl.h>#include <net/if.h>#include <signal.h>#include <ncurses.h>#include <math.h>#define SEVR_IP "127.0.0.1"#define SEVR_PORT 8081#define CNTNT_LEN 150#define MSG_LEN sizeof(struct msg)#define ADDR_LEN sizeof(struct sockaddr)#define USR_LEN sizeof(struct user)#define PRT_LEN 8#define HSTR_LEN sizeof(struct chat_history)/* declare Global variables */int mainfd;/* used as chat histroy file handle*/int sockfd;/* used as socket local handle */int count;struct sockaddr_in server;/* msg is used for communicating message */struct msg{int flag; /* flag meaning:1,ordinary; 2,log msg; 3,reg msg, other,file*/int id_from;int id_to;char content[CNTNT_LEN];char append[10];};/* user is used information list */struct user{int id;char name[10];char password[10];char *p_chatlog;struct sockaddr user_addr;};/* chat_history used for reading chat history */struct chat_history{char content[CNTNT_LEN];char time[25];/* i_functions below is funtions needed by both client and sever */ extern int i_saveto_chat(struct msg *pmsg);int i_clean_stdin (){while ('\n' == getchar()){continue;}return(0);}int i_print(char *pmsg, int size){int i = 1;for (i; i<= size; i++){if (*pmsg != '\n'){printf("%c", *pmsg);pmsg ++;}else{return(0);}}return(0);}int i_input(char *p_input){char c = '\0';int i;for (i = 0; i < CNTNT_LEN; i++){p_input[i] = getchar();if (p_input[i] =='\n'){return(0);}}printf("you have input long enough!\n");return(0);}int i_socket(int domain, int type, int protocol){int fd;if ((fd = socket(domain, type, protocol)) == -1){perror("creat socket error:");exit(1);}return(fd);}int i_bind(int fd, const struct sockaddr *addr, int namelen){if (-1 == bind(fd, addr, namelen)){perror("i_bind error:");exit(1);}return (0);if (-1 == recvfrom(fd, buf, len, flags, addr, size)){perror("i_recvfrom error:");exit(1);}return(0);}int i_sendto(int fd, void *buf, size_t len, int flags,struct sockaddr *addr, int size){if (-1 == sendto(fd, buf, len, flags, addr, size)){perror("i_sendto error");exit(1);}return (0);}int i_open(const char *pathname, int flags){int fd;if ((fd = open(pathname, flags)) == -1){perror("open_failed");exit(1);}return (fd);}int i_read(int fd, void *msg, int len){if(-1 == read(fd, msg, len)){perror("i_read error");exit(1);}return(0);}int i_write(int fd, void *msg, int len){if (-1 == write(fd, msg, len)){perror("i_write error");exit(0);}return(0);}/* init a socket,file and server addr */int i_init(){mainfd = i_open("./chat_log", O_RDWR|O_CREAT); sockfd = i_socket(AF_INET, SOCK_DGRAM, 0); /* initialize server address */bzero(&server, sizeof(server));server.sin_family = AF_INET;inet_pton(AF_INET, "127.0.0.1", &server.sin_addr); server.sin_port = htons(SEVR_PORT);perror("init");return (0);}char *i_get_time(){time_t time_now;int i_lseek(int fd, off_t size, int position){if (-1 == lseek(fd, size, position)){perror("seek error");exit(1);}return(0);}int i_saveto_chat(struct msg *pmsg){struct chat_history hstr;bzero(&hstr, HSTR_LEN);count = count + 1;hstr.count =count;hstr.from = pmsg->id_from;hstr.to = pmsg->id_to;strncpy(hstr.content, pmsg->content, CNTNT_LEN); strncpy(hstr.time, i_get_time(), 25);i_lseek(mainfd, 0, SEEK_END);i_write(mainfd, &hstr, HSTR_LEN);return(0);}int i_print_history(int len, int i){struct chat_history chat_reader;int j;int position;bzero(&chat_reader, HSTR_LEN);if (i != 0){position = len*i*HSTR_LEN;i_lseek(mainfd, position, SEEK_END);}else{position = len*i*HSTR_LEN;i_lseek(mainfd, HSTR_LEN, SEEK_SET);}for (j = 1; j <= len; j++){i_read(mainfd, &chat_reader, HSTR_LEN);printf("\n#item%d:id%dto id%d \n", j,chat_reader.from, chat_reader.to);i_print(chat_reader.content, CNTNT_LEN);printf("\n Time:%s\n", chat_reader.time);}return(0);}#endif代码⼆:#include "i.h"int user_list_fd;/* start:initialization */int init(){struct user usr;/* init the user list file's fist user to 0*/memset((struct user*)&usr, '\0', sizeof(struct user));i_lseek(user_list_fd, 0, SEEK_SET);i_write(user_list_fd, (char*)&usr, USR_LEN);/* bind the struct sockaddr_in server to the sockfd */i_bind(sockfd, (struct sockaddr*)&server, ADDR_LEN);struct chat_history apple;bzero(&apple, HSTR_LEN);i_lseek(mainfd, 0, SEEK_SET);i_write(mainfd, &apple, HSTR_LEN);i_lseek(mainfd, -HSTR_LEN, SEEK_END);i_read(mainfd, &apple, HSTR_LEN);count = apple.count;return(0);}/* end:initialization *//* start:message control */int send_msg(struct msg *msg_recv, struct sockaddr *addr){int i;struct user usr;/* a common message come */printf("a ordinar message come !\n");i = msg_recv->id_to;i_lseek(user_list_fd, i*USR_LEN, SEEK_SET);i_read(user_list_fd, &usr, USR_LEN);strncpy(msg_recv->append, , 10);i_sendto(sockfd, msg_recv, MSG_LEN, 0,&(er_addr), ADDR_LEN);printf("id%d send a message to id%d sucess!\n", msg_recv->id_from, msg_recv->id_to); return(0);}int check_login(struct msg *msg_recv, struct sockaddr *addr){int i = msg_recv->id_from;;struct user usr;/* a login requet */printf("a login request come!\n");/* get the id's information */i_lseek(user_list_fd, i*USR_LEN, SEEK_SET);i_read(user_list_fd, &usr, USR_LEN);int n;n = strcmp(usr.password, msg_recv->content);/* 如果验证成功,则发送成功信息 */if (n == 0){/* save user new address */i_lseek(user_list_fd, -USR_LEN, SEEK_CUR);er_addr = *addr;i_write(user_list_fd, &usr, USR_LEN);/* tell user pass */i_sendto(sockfd, (struct msg*)msg_recv, sizeof(struct msg), 0,&(er_addr), ADDR_LEN);}else{/* 出错的话的respond */msg_recv->flag = -1;i_sendto(sockfd, (struct msg*)msg_recv, sizeof(struct msg), 0,&(er_addr), ADDR_LEN);}return(1);}printf("Id %d login sucess!\n", i);return(0);}int reg_user(struct msg *msg_recv, struct sockaddr *addr){struct user usr;printf("a regit requet come:\n");/* find the last user and hava the please to add a new user */int n;i_lseek(user_list_fd, -USR_LEN, SEEK_END);i_read(user_list_fd, &usr, USR_LEN);/* 把新⽤户的信息赋值到usr然后填⼊到user list file中 */const char *name;const char *password;name = &(msg_recv->content[0]);password = &(msg_recv->content[10]);strcpy((), name);strcpy(usr.password, password);memcpy(&(er_addr),addr, ADDR_LEN);usr.id = (usr.id + 1);i_lseek(user_list_fd, 0, SEEK_END);i_write(user_list_fd, &usr, USR_LEN);msg_recv->id_from = usr.id;/* regist to the user list then tell the user reg success */i_sendto(sockfd, (struct msg*)msg_recv, sizeof(struct msg), 0,addr, ADDR_LEN);printf("Id %d regist sucess!\n", usr.id);return(0);}int msg_cntl(){struct msg msg_recv;struct sockaddr addr_recv;printf("begin listen input...\n");int size = ADDR_LEN;for (;;){bzero(&msg_recv, MSG_LEN);i_recvfrom(sockfd, &msg_recv, sizeof(struct msg), 0,&addr_recv, &size);printf("message received...\n");i_saveto_chat(&msg_recv);switch (msg_recv.flag){case 1 :send_msg(&msg_recv,(struct sockaddr*)&addr_recv);/* send ordinary chat */ break;case 2 :check_login(&msg_recv, (struct sockaddr*)&addr_recv);break;case 3 :reg_user(&msg_recv, (struct sockaddr*)&addr_recv);}return(0);}/* end:message control*//* start:exit_sys()*/int exit_sys(){close(sockfd);close(mainfd);close(user_list_fd);printf("exit system");kill(0, SIGABRT);exit(0);}/* end:exit_sys()*//* start:chat_history*/int get_page_size(){struct chat_history page_size_reader;i_lseek(mainfd, -HSTR_LEN, SEEK_END);i_read(mainfd, &page_size_reader, HSTR_LEN);return(page_size_reader.count);}int read_chat_history(){printf("****char*history***");printf("(n-nextpage; p-prepage; q-quit)\n");int page_num;/* */int remains;int berry = get_page_size();page_num = berry / 8;remains = berry % 8;if (remains != 0)page_num ++;elsepage_num = page_num;printf("there are %d page total %d items",page_num, berry);int i = -1;while (1){char flag;if ((berry + i*8) >= 0){printf("(%d~%d)\n", (berry + i*8), (berry + (i+1)*8)); i_print_history(PRT_LEN, i);printf("@@@\n");while ('\n' == (flag = getchar())){}switch (flag){case 'p' :i--;break;case 'n' :break;}if (i >= 0){printf("have at the end!\n");printf("return to menu!\n");}}else{printf("(1~%d)\n", remains);i_print_history(remains, 0);printf("#########over##############\n"); return(0);}}return(0);}/* end:chat_history*//* start:menu*/int menu(){sleep(1);printf("----------help----menu---------\n");printf("\t r--report to user\n");printf("\t c--chat history\n");printf("\t h--help menu\n");printf("\t e--exit the system\n");printf("----------help_menu---------\n");int command = 0;printf("input command>");command = getchar();switch(command){case 'c':read_chat_history();break;case 'e':exit_sys();break;case 'r'://report();//break;default :menu();break;}getchar();return(0);}/* end:menu*/int main(){init();pid_t pid;switch (pid = fork()){case -1 :perror("fork error\n");exit(1);break;case 0 :return(0);}代码三:#include "i.h"#define START_PORT 8089struct sockaddr_in my_addr;int my_id;int my_log();/* declare funtion*//* */int i_send_msg(){int id;struct msg the_msg;char end = '@';printf("input recver id:");scanf("%d", &id);getchar();printf("\ninput content:");i_input(the_msg.content);char flag = 'y';if (1){the_msg.flag = 1;the_msg.id_from = my_id;the_msg.id_to = id;i_sendto(sockfd, &the_msg, sizeof(struct msg), 0, (struct sockaddr*)&server, sizeof(struct sockaddr)); i_saveto_chat(&the_msg); /* save to history */printf("send to id:%d success.\n", my_id);return(0);}elsereturn(1);return(0);}int reply(){return(0);}int send_file(){return(0);}/**//* start:initialize */int init(){struct ifreq req;struct sockaddr_in *host;int port;i_init();/* init user addr */bzero(&my_addr, sizeof(struct sockaddr));my_addr.sin_family = AF_INET;host = (struct sockaddr_in*)&(req.ifr_addr);printf("ip: %s\n", inet_ntoa(host->sin_addr));memcpy(&my_addr, (struct sockaddr_in*)&(req.ifr_addr), sizeof(struct sockaddr_in));port = START_PORT;do{port++;my_addr.sin_port = htons(port);bind(sockfd, (struct sockaddr*)&my_addr,sizeof(struct sockaddr));}while (errno == EADDRINUSE);struct chat_history apple;memset(&apple, 'b', HSTR_LEN);i_lseek(mainfd, 0, SEEK_SET);apple.count = 0;i_write(mainfd, &apple, HSTR_LEN);i_lseek(mainfd, -HSTR_LEN, SEEK_END);i_read(mainfd, &apple, HSTR_LEN);count = apple.count;printf("port:%d\n", port);printf("init successful\n");return(0);}/* end:initialize *//* start:chat_history*/int get_page_size(){struct chat_history page_size_reader;i_lseek(mainfd, -HSTR_LEN, SEEK_END);i_read(mainfd, &page_size_reader, HSTR_LEN);return(page_size_reader.count);}int read_chat_history(){printf("****char*history***");printf("(n-nextpage; p-prepage; q-quit)\n");int page_num;/* */int remains;int berry = get_page_size();page_num = berry / 8;remains = berry % 8;if (remains != 0)page_num ++;elsepage_num = page_num;printf("there are %d page total %d items",page_num, berry);int i = -1;if ((berry + i*8) >= 0){printf("(%d~%d)\n", (berry + i*8), (berry + (i+1)*8)); i_print_history(PRT_LEN, i);printf("@@@\n");while ('\n' == (flag = getchar())){}switch (flag){case 'p' :i--;break;case 'n' :i++;break;case 'q' :return(0);default :break;}if (i >= 0){printf("have at the end!\n");printf("return to menu!\n");}}else{printf("(1~%d)\n", remains);i_print_history(remains, 0);printf("#########over##############\n");return(0);}}return(0);}/* end:chat_history*//* start:exit_sys*/void exit_sys(){close(sockfd);close(mainfd);kill(0, SIGABRT);exit(0);}/* end:exit_sys*//* start:menu*/int print_menu(){printf("\n--------------help--menu----------------\n");printf("\t h--help munu\n");printf("\t s--send message\n");printf("\t r--reply to\n");printf("\t c--chat history\n");printf("\t f--send files\n");printf("\t e--exit the system\n");printf("----------------help--menu----------------\n");}int get_input(char *command){printf(">");scanf("%c", command);return(1);}int menu()/* to avoid the output at mixed with the sub process */sleep(1);print_menu();char command;while (1 == get_input(&command)){switch(command){case 'h':print_menu();break;case 's':i_send_msg();break;case 'r':reply();break;case 'f':send_file();break;case 'c':read_chat_history();break;case 'e':exit_sys();break;default :printf(">");break;}}return(0);}/* end:menu*//* start:message contol :send_msg and recv_msg */int ordnary_msg_recv(struct msg *pmsg){char time_info[25];char end_symble;end_symble = '&';/* handle the msg */printf("Message:from %s(id%d) to U:\n", pmsg->append, pmsg->id_from); i_print(pmsg->content, MSG_LEN);printf("\n\t%s", i_get_time());return(0);}int file_msg_recv(struct msg *pmsg){}int handle_msg(struct msg *pmsg){if (pmsg->flag == 1){ordnary_msg_recv(pmsg);return(0);}else if (pmsg->flag >= 4){file_msg_recv(pmsg);return(0);}return(0);}int listen_msg(){struct msg msg_recv;struct sockaddr addr_recv;int len = ADDR_LEN;printf("begin listen...\n");for ( ; ; ){i_recvfrom(sockfd, &msg_recv, MSG_LEN, 0,&addr_recv, &len);i_saveto_chat(&msg_recv); /* save to history */ordnary_msg_recv(&msg_recv);}}/* end:message contol*//* start:log process :login and regist */int login(){/* input id:*/printf("*****login>>\n");printf("id:");scanf("%d", &my_id);/* input password*/char password[15];printf("\npassword(*less 15 char):");scanf("%s", password);getchar();/* send login information */struct msg log_msg;bzero(&log_msg, MSG_LEN);log_msg.flag = 2;log_msg.id_from = my_id;log_msg.id_to = 0;strncpy(log_msg.content, password, 15);i_saveto_chat(&log_msg); /* save to history */i_sendto(sockfd, (struct msg*)&log_msg, MSG_LEN, 0, (struct sockaddr*)&server, sizeof(struct sockaddr));//printf("log_msg : %d\n", log_msg.id_from);//printf("password: %s\n", log_msg.content);/* after input msg ,wait for server respond*/struct sockaddr in_addr;int len = ADDR_LEN;i_recvfrom(sockfd, (struct msg*)&log_msg, MSG_LEN,0, &in_addr, &len);if (2 == log_msg.flag){printf("login success\n");return(0);}else{printf("login error:%s\n", log_msg.content);printf("please relog..\n");menu();}return (0);}int regist(){printf("*****regist>>\n");/* input chat name */char name[10];bzero(name, 10);printf("input your chat name(less 8 char):");scanf("%s", name);//name[9] = ';'; /* add a ; symbol in the end of name */ /* input password */char password[15];bzero(password, 15);printf("\ninput your password(less 14 char):");scanf("%s", password);/* send regist information*/struct msg reg_msg;bzero(®_msg, MSG_LEN);reg_msg.flag = 3;reg_msg.id_from = 0;reg_msg.id_to = 0;bzero(reg_msg.content, CNTNT_LEN);strncpy(reg_msg.content, name, 10);strncpy(&(reg_msg.content[10]), password, 15);reg_msg.content[25] = '\n';i_saveto_chat(®_msg); /* save to history *//* send regist informatin to server */i_sendto(sockfd, (struct msg*)®_msg, sizeof(struct msg), 0, (struct sockaddr*)&server, ADDR_LEN);/* after input msg ,wait for server respond*/printf("wating for server reply...\n");struct sockaddr in_addr;struct msg msg_back;int len = ADDR_LEN;bzero(&in_addr, ADDR_LEN);bzero(&msg_back, MSG_LEN);i_recvfrom(sockfd,(struct msg*)&msg_back, MSG_LEN,0,&in_addr, &len);/* check whether pass */if (3 != msg_back.flag){printf("error: %s \n", msg_back.content);exit(1);}elsemy_id = msg_back.id_to;printf("congratulate! you have regist""id %s(id %d) success\n", msg_back.content, msg_back.id_to); login();return(0);}int my_log(){/* choose login or regist*/char flag;printf("are you want login or regist(l/r)\n");scanf("%c", &flag);getchar();switch (flag){case 'l' :login();break;case 'r' :regist();break;default :printf("error input\n");my_log();break;}return (0);}/* end:log */int main(){init();printf("\n************welcome!************\n");my_log();pid_t pid;switch (pid = fork()){case -1 :perror("fork error!\n");exit(1);break;case 0 :listen_msg();break;default :menu();break;}}希望本⽂所述对⼤家实现linux多⼈聊天室程序。
Linux下的Socket网络编程:一个简易聊天室的实现-徐慧军
高级程序设计与应用实践
报告
一个简易聊天室的实现
姓名:徐慧军
学号:2121134
专业:电子与通信工程
学院:信息科学与技术学院
任课教师:廖晓飞
2013年05月02日
Linux下的Socket网络编程:
——一个简易聊天室的实现一、socket介绍
socket接口是TCP/IP网络的API,socket接口定义了许多函数或例程,程序员可以用它们来开发TCP/IP网络上的应用程序。
要学Internet上的TCP/IP 网络编程,必须理解socket接口。
socket接口设计者最先是将接口放在Unix操作系统里面的。
如果了解Unix 系统的输入和输出的话,就很容易了解socket了。
网络的socket数据传输是一种特殊的I/O,socket也是一种文件描述符。
socket也具有一个类似于打开文件的函数调用socket(),该函数返回一个整型的socket描述符,随后的连接建立、数据传输等操作都是通过该socket实现的。
常用的socket类型有两种:流式socket (SOCK_STREAM)和数据报式socket(SOCK_DGRAM)。
流式是一种面向连接的socket,针对于面向连接的TCP服务应用;数据报式socket是一种无连接的socket,对应于无连接的UDP服务应用。
二、Socket创建
socket函数原型为:
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
功能:调用成功,返回socket文件描述符;失败,返回-1,并设置errno 参数说明:
domain指明所使用的协议族,通常为PF_INET,表示互联网协议族(TCP/IP 协议族;
type参数指定socket的类型:
SOCK_STREAM 提供有序、可靠、双向及基于连接的字节流
SOCK_DGRAM 支持数据报
SOCK_SEQPACKET 提供有序、可靠、双向及基于连接的数据报通信
SOCK_RAW 提供对原始网络协议的访问
SOCK_RDM 提供可靠的数据报层,但是不保证有序性
protocol通常赋值"0".
socket描述符是一个指向内部数据结构的指针,它指向描述符表入口。
调用socket函数时,socket执行体将建立一个socket,实际上"建立一个socket"意味着为一个socket数据结构分配存储空间。
socket执行体为你管理描述符表。
两个网络程序之间的一个网络连接包括五种信息:通信协议、本地协议地址、本地主机端口、远端主机地址和远端协议端口。
socket数据结构中包含这五种信息。
三、Socket邦定
bind函数原型为:
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sock_fd,struct sockaddr *my_addr, int addrlen);
功能说明:将套接字和指定的端口相连。
成功返回0,否则,返回-1,并置errno.
参数说明:sock_fd是调用socket函数返回的socket描述符,
my_addr是一个指向包含有本机IP地址及端口号等信息的sockaddr类型的指针;
addrlen常被设置为sizeof(struct sockaddr)。
struct sockaddr结构类型是用来保存socket信息的:
struct sockaddr {
unsigned short sa_family; /* 地址族, AF_xxx */
char sa_data[14]; /* 14 字节的协议地址 */
};
sa_family一般为AF_INET,代表Internet(TCP/IP)地址族;
sa_data则包含该socket的IP地址和端口号。
另外还有一种结构类型:
struct sockaddr_in {
short int sin_family; /* 地址族 */
unsigned short int sin_port; /* 端口号 */
struct in_addr sin_addr; /* IP地址 */
unsigned char sin_zero[8]; /* 填充0 以保持与struct sockaddr同样大小 */
};
这个结构更方便使用。
sin_zero用来将sockaddr_in结构填充到与struct sockaddr同样的长度,可以用bzero()或memset()函数将其置为零。
指向sockaddr_in 的指针和指向sockaddr的指针可以相互转换,这意味着如果一个函数所需参数类型是sockaddr时,你可以在函数调用的时候将一个指向sockaddr_in的指针转换为指向sockaddr的指针;或者相反。
使用bind函数时,可以用下面的赋值实现自动获得本机IP地址和随机获取一个没有被占用的端口号:
my_addr.sin_port = 0; /* 系统随机选择一个未被使用的端口号 */
my_addr.sin_addr.s_addr = INADDR_ANY; /* 填入本机IP地址 */
通过将my_addr.sin_port置为0,函数会自动为你选择一个未占用的端口来使用。
同样,通过将my_addr.sin_addr.s_addr置为INADDR_ANY,系统会自动填入本机IP地址。
注意在使用bind函数是需要将sin_port和sin_addr转换成为网络字节优先顺序。
计算机数据存储有两种字节优先顺序:高位字节优先和低位字节优先(大端和小端)。
Internet上数据以高位字节优先顺序在网络上传输,所以对于在内部是以低位字节优先方式存储数据的机器,在Internet上传输数据时就需要进行转换,否则就会出现数据不一致。