当前位置: 首页 > 科技观察

Socket技术,实现Http协议和游戏服务器协议(Java代码)

时间:2023-03-12 13:34:58 科技观察

网络技术中,前后端通信是一个难点,也是服务器程序员必须克服的技术瓶颈。要实现两台电脑之间的信息交换,需要一种技术,而两台电脑网卡无非就是UDP和TCP两种技术。其中,两种技术因具有不同的特点而被用在不同的地方。除了在一些对可达性要求不高的传输领域(如:视频)使用UDP外,由于其可靠的安全可达性,90%的通信都是通过TCP协议传输的。在程序员看来,网卡是如何通过5层协议(或3层协议)实现tcp通信的,无需过多思考。在编程语言中,socket是tcp的代名词(可能有点措手不及,但我个人是这么认为的)。先用一个简单的http协议中的get案例,让大家更好的理解游戏前后端的通信协议指定。下面是java代码:publicclasstestHttpClient{publicstaticvoidmain(String[]arg){Socketsocket=newSocket();try{socket.connect(newInetSocketAddress("www.baidu.com",80),300);OutputStreamo=socket.getOutputStream();//http协议字符串包括header和body。HTTP使用“\r\n”作为分区。除了第一行,header使用key:value来存储信息。服务器解锁后,提取StringrequestStr="GET/HTTP/1.1\r\n"//第一行三个信息1.请求方式(一个会用到get和post两种)2.请求路径3.采用http1.1协议+"Host:www.baidu.com\r\n"//DeclareHost//如果有其他hearder字段信息,可以在这里阻止+"\r\n"//这里表示结束ofthepacketheader+"\r\n";//这里表示包体的结束(由于这个get没有任何结构,如果需要像post一样传递参数key1=value1&key2=value2,一定要在header中添加"Content-Length:***"声明包体长度)o.write(requestStr.getBytes());o.flush();BufferedReaderi=newBufferedReader(newInputStreamReader(socket.getInputStream()));Stringresponse="";StringreadOneStr=null;while((readOneStr=i.readLine())!=null){response+=readOneStr+"\r\n";}System.out.println(response);}catch(IOExceptionioe){ioe.printStackTrace();}}重点解释整个socket发送的内容:http协议使用“\r\n”作为解码器分段符号。该协议的二进制流包括两部分:header和body。请求协议的具体内容:header:GET/HTTP/1.1\r\n空间分为三个信息1.请求方式(一个会用到get和post两种)2.请求路径3.采用http1.1协议Host:www.baidu.com\r\nContent-Length:***\r\nContent-type:application/json\r\nCookie:************\r\nUsingkey:value向服务器提交信息Content-Length对服务器读取包体的长度很重要。\r\n再次使用“\r\n”表示HTTP头信息结束,后面的字节流就是header的内容\r\n这个表示请求包body的结束这里基本就是一个HTTP协议request协议结束,然后我们再来查看服务器的http响应信息,也包括header和body。身体有些变化。第一行“HTTP/1.1200OK\r\n”也是三部分信息,返回协议、状态码和状态(在http中200是响应成功的意思),也是一个key:value价值。它还使用两个连续的“\r\n”来划分包头和包体字节流。包体没什么好说的,就是一些html、css、js代码,也就是我们实际用户能看到的网页代码。附图如下:看了上面的http协议,我们就可以理解了,下面我来说明一下我为游戏项目的服务端设计的协议。由于我们游戏项目的功能需求,我们都是使用tcp长连接。发送不同的命令,响应前端请求,所以需要一个严谨高效的通信协议:包头包头起始标识码(int)4+命令号(shot)2+消息唯一编号(int)4+内容长度(int))4packagebody内容bytes[]意在说明:包头包含四个重要信息,第一个是包头开始标识码,用于服务器读取socket管道时寻找新包的开始循环中的信息。第二个是命令号,是请求服务器的动作命令(如:“1”为用户登录验证,“2”为kill命令),服务器会自动调用响应模块代码。第三部分是这个包的唯一编码,用于防止前端重复提交这个包,导致服务端业务逻辑错误。第四部分是包体字节流长度。这样每个包头固定长度为14字节,包体可有可无,看调用命令是否需要前端传参。附上packetizer的代码(java代码):publicclassEncodedMessage{publicstaticintcode=44434533;/*codeUnid命令messageUnid消息唯一码体封装字节流*/publicstaticbyte[]encoded(intcodeUnid,intmessageUnid,byte[]body){//44434533/包头(int)4+命令编号(shot)2+消息唯一编号(int)4+内容长度(int)4+内容字节/ByteBufferbuffer=ByteBuffer.allocate(14+body.length);buffer.putInt(code);shortcode=(short)codeUnid;buffer.putShort(code);buffer.putInt(messageUnid);buffer.putInt(body.length);buffer.put(body);returnbuffer.array();}publicstaticintbyteArrayToInt(byte[]b){returnb[3]&0xFF|(b[2]&0xFF)<<8|(b[1]&0xFF)<<16|(b[0]&0xFF)<<24;}publicstaticshortbytesToShort(byte[]b){return(short)(b[1]&0xff|(b[0]&0xff)<<8);}}这套通信协议是游戏服务器采用的,服务器可以很好的处理分包,会有没有连包的情况。