- github-httpserverftpserverchattingRoomRMI
- RMI Remote Method Invocation
- httpserver
- FTP
- 客户端连接到服务器端 c语言伪码Java版见github
- 客户端连接到 FTP 服务器接收欢迎信息
- 客户端发送用户名和密码登入 FTP 服务器
- 让服务器进入被动模式在数据端口监听
- 客户端通过被动模式下载文件
- Chatting功能
- 服务器和客户端通信协议
- 客户端发送请求数据格式
- 服务器响应数据格式
-
- 基于socket的一个简单的android聊天工具
- 遇到的问题
RMI Remote Method Invocation
调用不同JVM上的方法
httpserver
这里并不需要解析xml/html, 根据request请求,返回html让浏览器解析
遇到的问题
-
第一个问题,之前在创建文件的时候程序一直报错,说文件路径不对或者不存在,之后发现在写路径的时候’/’这些元字符是需要转义的。
-
第二个问题,是服务器运行后,无法在网页上查看图片等信息,同时服务端抛出异常,但是html网页可以正常显示。这个问题最后通过询问同学和查资料,得知我所使用的发送和接收消息的流不对,不能够使用datainputstream和dataoutputstream,并且wirteUTF方法不能用于http服务器中因为这个方法一开始会写入两个字节,即为将要写入的字节数(不是字符串的长度)。后来改成了printstream和bufferedreader就成功了。
-
第三个问题,是没有意识到浏览器解析html时,对于html内嵌的图片和视频等资源会在解析之后再次送一个get请求。所以通过浏览器访问服务端网页时,服务端不用进行标签解析。
-
第四个问题,在服务器发送响应体的时候,我发现出现一个奇怪的现象,就是程序运行的时候,总是无法下载完成,打开对应的客户端文件夹发现文件没有下载完成(即为0字节),但是在关闭客户端运行之后,那个文件夹里的文件就下载好了。最后发现,是服务器在发送响应体的时候,最后忘记关闭了文件发送流,所以导致客户端一直在读取服务器的信息,导致失败。后来添加了close,判断-1才能成功,接受所有信息并关闭文件
FTP:
Resume file upload/download after lost connection
使用 Socket 通信实现 FTP 客户端程序
客户端连接到服务器端 c语言伪码(Java版见github)
客户端连接到 FTP 服务器,接收欢迎信息
<code class="language-c hljs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">SOCKET control_sock;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">struct</span> hostent *hp;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">struct</span> sockaddr_in server;
<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">memset</span>(&server, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">sizeof</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">struct</span> sockaddr_in));<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* 初始化socket */</span>
control_sock = socket(AF_INET, SOCK_STREAM, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>);
hp = gethostbyname(server_name);
<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">memcpy</span>(&server.sin_addr, hp->h_addr, hp->h_length);
server.sin_family = AF_INET;
server.sin_port = htons(port);<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* 连接到服务器端 */</span>
connect(control_sock,(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">struct</span> sockaddr *)&server, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">sizeof</span>(server));
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* 客户端接收服务器端的一些欢迎信息 */</span>
read(control_sock, read_buf, read_len);</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li></ul>
客户端发送用户名和密码,登入 FTP 服务器
<code class="language-c hljs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* 命令 ”USER username\r\n” */</span>
<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">sprintf</span>(send_buf,<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"USER %s\r\n"</span>,username);
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/*客户端发送用户名到服务器端 */</span>
write(control_sock, send_buf, <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">strlen</span>(send_buf));
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* 客户端接收服务器的响应码和信息,正常为 ”331 User name okay, need password.” */</span>
read(control_sock, read_buf, read_len);<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* 命令 ”PASS password\r\n” */</span>
<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">sprintf</span>(send_buf,<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"PASS %s\r\n"</span>,password);
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* 客户端发送密码到服务器端 */</span>
write(control_sock, send_buf, <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">strlen</span>(send_buf));
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* 客户端接收服务器的响应码和信息,正常为 ”230 User logged in, proceed.” */</span>
read(control_sock, read_buf, read_len);</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li></ul>
让服务器进入被动模式,在数据端口监听
<code class="language-c hljs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* 命令 ”PASV\r\n” */</span>
<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">sprintf</span>(send_buf,<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"PASV\r\n"</span>);
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* 客户端告诉服务器用被动模式 */</span>
write(control_sock, send_buf, <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">strlen</span>(send_buf));
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/*客户端接收服务器的响应码和新开的端口号,
* 正常为 ”227 Entering passive mode (<h1,h2,h3,h4,p1,p2>)” */</span>
read(control_sock, read_buf, read_len);</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li></ul>
客户端通过被动模式下载文件
<code class="language-c hljs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* 连接服务器新开的数据端口 */</span>
connect(data_sock,(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">struct</span> sockaddr *)&server, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">sizeof</span>(server));
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* 命令 ”CWD dirname\r\n” */</span>
<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">sprintf</span>(send_buf,<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"CWD %s\r\n"</span>, dirname);
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* 客户端发送命令改变工作目录 */</span>
write(control_sock, send_buf, <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">strlen</span>(send_buf));
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* 客户端接收服务器的响应码和信息,正常为 ”250 Command okay.” */</span>
read(control_sock, read_buf, read_len);<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* 命令 ”SIZE filename\r\n” */</span>
<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">sprintf</span>(send_buf,<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"SIZE %s\r\n"</span>,filename);
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* 客户端发送命令从服务器端得到下载文件的大小 */</span>
write(control_sock, send_buf, <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">strlen</span>(send_buf));
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* 客户端接收服务器的响应码和信息,正常为 ”213 <size>” */</span>
read(control_sock, read_buf, read_len);<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* 命令 ”RETR filename\r\n” */</span>
<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">sprintf</span>(send_buf,<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"RETR %s\r\n"</span>,filename);
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* 客户端发送命令从服务器端下载文件 */</span>
write(control_sock, send_buf, <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">strlen</span>(send_buf));
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* 客户端接收服务器的响应码和信息,正常为 ”150 Opening data connection.” */</span>
read(control_sock, read_buf, read_len);<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* 客户端创建文件 */</span>
file_handle = open(disk_name, CRFLAGS, RWXALL);
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span>( ; ; ) {
... ...
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* 客户端通过数据连接 从服务器接收文件内容 */</span>
read(data_sock, read_buf, read_len);
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* 客户端写文件 */</span>
write(file_handle, read_buf, read_len);
... ...
}
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* 客户端关闭文件 */</span>
rc = close(file_handle);</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li></ul>
遇到的问题
-
第一个问题是很迷茫,应为是第一个做的实验,不知道如何进行文件上传和下载。后来在网上查询,明白了可以分成两部分,第一部分把文件从硬盘里读取到内存中,再把数据从内存中发送到另一端,由另一端读取到内存,再从内存写入到文件。其中使用了一个字节数组当做一个缓冲,就像一辆运输车一样,一趟一趟的运送货物,直到最后read()==-1就读完了。
-
第二个问题是下载文件夹。逻辑较为复杂,首先要使用isDirectory方法判断是否是文件夹,然后在本地判断是否存在同名文件夹,通过file.list方法获得文件列表,进而得到文件个数,然后对每个文件进行下载即可,此处隐含了list,cwd,retr等指令,较为复杂,并且dos dis使用后要及时flush和关闭。
-
第三个问题是阻塞方法的调用。多线程间的协作和socket通信容易混淆,了解到相关原则是尽量少开线程,并时刻注意线程同步异步问题,如上传文件时,服务端使用线程同步(synchronized)的方法,即一个线程访问该方法时,他就获得了该object的对象锁,另一个线程对该object的访问将被暂时阻塞, 保证只存在一个同名文件且后上传的不会覆盖之前的。由于实验末尾才稍微了解了java并发的锁机制,所以所有实验都可以用线程锁等java并发编程知识进行优化,这也是下一步的改进方向
Chatting功能:
登录
发送文件、表情、信息
多人同时聊天
离线发送消息(上线后就能看到)
服务器和客户端通信协议
客户端登录,客户端取得用户列表,以及客户端和客户端的通信都必须跟服务器交互,我们约定客户端和服务器端的通信协议如下:
请求数据格式
客户端发送请求数据格式:
请求码 请求数据
请求码:
101 用户登录
102 取得用户列表
103 发送消息
如登录请求,客户端向服务器发送的请求数据为:
101|用户名,密码
如取得用户列表,客户端向服务器发送的请求数据为:
102|
如发送消息,客户端向服务器发送的请求数据为:
103|消息发送方,消息接收方,消息内容
响应数据格式
服务器响应数据格式:
响应码 响应数据
响应码:
1 响应成功
0 响应失败
如登录请求,服务器端的响应数据为:
1| 或者 0|
如取得用户列表,服务器端的响应数据为:
1|消息类型(102)@消息发送方(Server)@用户1,用户2,用户3@消息发送时间
如服务器给客户端发送消息
1|消息类型(103)@消息发送方@消息内容@消息发送时间
服务器消息队列格式
服务器端接收到客户的请求后(取得用户列表和用户信息)将请求结果放在消息队列中,发送消息的线程用队列中取消息,发送给客户端,
我们约定消息队列中消息的格式如下:
消息发送方 消息接收方 内容 发送时间 消息类别
Server tom Tom,jack,luly,lily 2016-1-29 11:11:11 102
Jack tom How are you doing? 2016-1-29 12:12:12 103
socket采用TCP/IP时,应有可自由分配的IP(内外网)。对于蓝牙协议,上层接口无法得到IP,所以与wifi组成网络时比较困难,需修改底层框架结构。
遇到的问题
-
第一个问题是对该设计模式的理解。这个模型类似于生产-消费者模型,将该模型的订阅模式和p2p模式结合,就实现了即能群聊,又能单独聊天的功能。一开始看通信流程图,认为是每个用户一个消息信道,直到想明白生产-消费者模型,才开始正确的实验过程。网上相关资料实现了jms接口进行开发,这也是可以进一步改进的方向
-
第二个问题是初始化用户列表的时机,后来发现了问题,添加了构造函数,初始化了用户列表。但是主线程里并没有初始化任何一个用户包对象,仅仅是调用了一个静态方法,所以我在服务器建立伊始就初始化了一个用户包对象,虽然这个对象没有被使用,但是成功初始化了用户列表。同样的,消息列表类(MessageDAO)我也使用了此种方法,只不过不在建立服务器时,是在用户登录成功过后。